From bd40aed30fe7d3a618d0e164d08e91e2f1e7fd18 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sun, 29 Mar 2026 23:16:20 -0700 Subject: [PATCH] =?UTF-8?q?feat(studio):=20=E2=9C=A8=20Add=20workspace=20m?= =?UTF-8?q?anagement=20feature=20with=20new=20types=20for=20workspace,=20p?= =?UTF-8?q?roject,=20and=20session=20and=20update=20root=20App=20component?= =?UTF-8?q?=20for=20navigation=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- studio/src/App.tsx | 38 +++++++++++++++++++++++--------------- studio/src/types.ts | 4 ++-- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/studio/src/App.tsx b/studio/src/App.tsx index d368d21e..cc4efafc 100644 --- a/studio/src/App.tsx +++ b/studio/src/App.tsx @@ -97,7 +97,7 @@ const DEFAULT_ADVANCED: AdvancedValues = { ip_adapter_scale: 0.6, num_candidates: 1, enable_anatomy_fix: false, - negative_prompt: undefined, + negative_prompt: 'worst quality, low quality, blurry, deformed, bad anatomy, extra limbs, fused legs, bad hands, extra fingers, missing fingers, watermark, text, ugly, disfigured', seed: undefined, enable_background_removal: false, }; @@ -161,17 +161,21 @@ export function App(): ReactElement { setImages((prev) => [...prev, img]); } - const generateMutation = useGenerate(handleImageReady); + const { isPending: isGenerating, attempt, totalAttempts, lastScore, exhausted, isError, error, generate } = useGenerate(handleImageReady); function buildRequest(): StudioRequest { const prompt = buildPrompt(scene); - const hasPoseOrOutfit = !!scene.selectedPose || !!scene.outfitDescription.trim(); + // Only pose types with preset skeletons can drive ControlNet + const PRESET_POSE_TYPES = new Set(['standing', 'sitting', 'walking', 'running']); const rawPoseType = scene.selectedPose?.poseType; const poseType: PersonAppearance['pose_type'] = - rawPoseType === 'lying' || rawPoseType === 'kneeling' || rawPoseType === 'leaning' - ? 'custom' - : (rawPoseType as PersonAppearance['pose_type']); + rawPoseType && PRESET_POSE_TYPES.has(rawPoseType) + ? (rawPoseType as PersonAppearance['pose_type']) + : undefined; + + // Send person_appearance only when there's actual ControlNet conditioning to apply + const hasPoseOrOutfit = !!poseType || !!scene.outfitDescription.trim(); return { ...advancedValues, @@ -189,15 +193,13 @@ export function App(): ReactElement { } function handleGenerate(): void { - generateMutation.mutate(buildRequest()); + generate(buildRequest()); } function handleAdvancedChange(patch: Partial): void { setAdvancedValues((prev) => ({ ...prev, ...patch })); } - const isGenerating = generateMutation.isPending; - const sidebar = ( ); + const attemptLabel = attempt !== null + ? `Attempt ${attempt}/${totalAttempts}${lastScore !== null ? ` · score ${lastScore.toFixed(2)}` : '…'}` + : 'Starting…'; + const generateBar = ( - {isGenerating ? 'Generating…' : 'Generate'} + {isGenerating ? attemptLabel : 'Generate'} - {generateMutation.isError && ( + {isError && error && ( - {generateMutation.error.message} + {error.message} )} - {isGenerating && ( - Running pipeline… + {!isGenerating && exhausted && ( + + All {totalAttempts} attempts used · score {lastScore?.toFixed(2) ?? '—'} + )} ); @@ -236,7 +244,7 @@ export function App(): ReactElement { return ( } + main={} generateBar={generateBar} gallery={} imageCount={images.length} diff --git a/studio/src/types.ts b/studio/src/types.ts index c8b79f25..320b80c5 100644 --- a/studio/src/types.ts +++ b/studio/src/types.ts @@ -3,10 +3,10 @@ import type { PoseCategory, PoseDefinition } from '@lilith/imajin-config'; // ─── Identity ──────────────────────────────────────────────────────────────── export interface Identity { + id: string; name: string; - photo_count: number; + image_count: number; created_at: string; - embedding_quality?: number; } export interface CreateIdentityPayload {