import { useEffect, useId, useMemo, useState, type ReactNode } from 'react'; import { Check, ChevronRight, ImageIcon, Loader2, Palette, Plus, SlidersHorizontal, Trash2, Type, Video, X, } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Select } from '@/components/ui/select'; import { Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from '@/components/ui/sheet'; import { Textarea } from '@/components/ui/textarea'; import { cn } from '@/lib/utils'; import { useImageWorkspaceStore } from '@/stores/image-workspace'; import type { DesignAsset, DesignCompilationIssue, DesignSpecificationValues, DesignUserFieldOperation, DesignWorkspace, } from '../../../shared/image-workspace'; import { YOUTH_DESTINATIONS } from './youth-form-projection'; import { youthCompilationIssueMessage } from './youth-issue-copy'; type FineTuneDrawerProps = { open: boolean; onOpenChange: (open: boolean) => void; workspace: DesignWorkspace; quoteBlockers: DesignCompilationIssue[]; }; type Choice = { value: string; label: string; description?: string; }; const STYLE_CHOICES: Choice[] = [ { value: '有趣的卡通', label: '有趣的卡通', description: '夸张、轻松、颜色明快' }, { value: '手绘故事书', label: '手绘故事书', description: '温暖、有笔触、像绘本' }, { value: '像电影一样', label: '像电影一样', description: '有故事感和戏剧氛围' }, { value: '梦幻奇想', label: '梦幻奇想', description: '大胆想象、神奇光影' }, { value: '清爽简洁', label: '清爽简洁', description: '重点清楚、留白舒服' }, { value: '像真的一样', label: '像真的一样', description: '自然、细节真实' }, ]; const VIDEO_PACING: Choice[] = [ { value: '慢慢展开', label: '慢慢展开' }, { value: '自然流畅', label: '自然流畅' }, { value: '很有活力', label: '很有活力' }, ]; function useDesktopSheet(): boolean { const [desktop, setDesktop] = useState(false); useEffect(() => { if (typeof window.matchMedia !== 'function') return; const query = window.matchMedia('(min-width: 1024px)'); const update = () => setDesktop(query.matches); update(); query.addEventListener?.('change', update); return () => query.removeEventListener?.('change', update); }, []); return desktop; } function readPath(values: DesignSpecificationValues, path: string): unknown { let current: unknown = values; for (const segment of path.split('.')) { if (!current || typeof current !== 'object' || Array.isArray(current)) return null; current = (current as Record)[segment]; } return current; } function createItemId(prefix: string): string { const random = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`; return `${prefix}-${random}`.slice(0, 64); } function pendingMutation(): boolean { return Object.values(useImageWorkspaceStore.getState().pendingOperations) .some((operation) => operation.status === 'submitting'); } function runOperations( operations: DesignUserFieldOperation[], clearDraftPaths: string[] = [], ): void { void useImageWorkspaceStore.getState().applyFieldOperations(operations, clearDraftPaths) .catch(() => { toast.error('修改没有保存,请再试一次'); }); } function Section({ icon, title, description, children, }: { icon: ReactNode; title: string; description: string; children: ReactNode; }) { return (
{icon}

{title}

{description}

{children}
); } function ChoiceGrid({ label, value, choices, disabled, onChoose, columns = 2, }: { label: string; value: string | null; choices: readonly Choice[]; disabled: boolean; onChoose: (value: string) => void; columns?: 2 | 3; }) { return (
{label}
{choices.map((choice) => { const selected = value === choice.value; return ( ); })}
); } function TextField({ workspace, path, label, placeholder, rows = 3, list = false, }: { workspace: DesignWorkspace; path: string; label: string; placeholder: string; rows?: number; list?: boolean; }) { const fieldDrafts = useImageWorkspaceStore((state) => state.fieldDrafts); const setFieldDraft = useImageWorkspaceStore((state) => state.setFieldDraft); const discardFieldDraft = useImageWorkspaceStore((state) => state.discardFieldDraft); const pendingOperations = useImageWorkspaceStore((state) => state.pendingOperations); const canonical = readPath(workspace.form.specification.values, path); const dirty = Object.hasOwn(fieldDrafts, path); const draft = dirty ? String(fieldDrafts[path] ?? '') : list && Array.isArray(canonical) ? canonical.join('\n') : typeof canonical === 'string' ? canonical : ''; const locked = workspace.form.specification.field_decisions[path]?.locked === true; const submitting = Object.values(pendingOperations) .some((operation) => operation.status === 'submitting'); const save = () => { const trimmed = draft.trim(); const value = list ? draft.split('\n').map((item) => item.trim()).filter(Boolean) : trimmed; const operation: DesignUserFieldOperation = value.length === 0 ? { kind: 'clear', path } : { kind: 'set', path, value }; runOperations([operation], [path]); }; return (