@cut-workbench/workbench 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Separator","SeparatorPrimitive","TooltipPrimitive","Tooltip","Popover","PopoverPrimitive","Popover","Row","Separator","Tooltip","Toaster","Sonner","Label","LabelPrimitive","ScrollArea","ScrollAreaPrimitive","Dialog","DialogPrimitive","DropdownMenu","DropdownMenuPrimitive","ImageIcon","DropdownMenu","KIND_LABEL","ScrollArea","Dialog","Separator","_Fragment","DropdownMenu","TypeIcon","Label","Tooltip","_Fragment","Tooltip","Slider","SliderPrimitive","Tooltip","Slider","FRAME_US","Tooltip","Separator","Tooltip","_Fragment","ContextMenu","ContextMenuPrimitive","ContextMenu","Tabs","TabsPrimitive","Select","SelectPrimitive","Label","_Fragment","Tooltip","Select","_Fragment","_Fragment","Select","_Fragment","Select","_Fragment","ScrollArea","Tabs","_Fragment","Separator","Switch","SwitchPrimitive","Progress","ProgressPrimitive","import.meta.ROLLDOWN_FILE_URL_axujYtGT_5s$jBogY6bRtw","workerUrl","Dialog","Label","Select","Switch","Progress","_Fragment","Separator","Progress","Dialog","_Fragment","Label","_Fragment","_Fragment","_Fragment","Toaster","immer"],"sources":["../src/lib/cn.ts","../src/components/layout/AppShell.tsx","../src/components/ui/button.tsx","../src/components/ui/separator.tsx","../src/portal-container.tsx","../src/components/ui/tooltip.tsx","../src/components/ui/badge.tsx","../src/components/ui/popover.tsx","../src/components/layout/CapabilityBadge.tsx","../src/context.tsx","../src/components/layout/WorkbenchToolbar.tsx","../src/components/layout/use-panel-resize.ts","../src/components/layout/PanelResizer.tsx","../src/components/ui/card.tsx","../src/components/common/LoadingOverlay.tsx","../src/components/common/UnsupportedBrowser.tsx","../src/components/ui/sonner.tsx","../src/components/ui/textarea.tsx","../src/components/ui/label.tsx","../src/components/ui/scroll-area.tsx","../src/components/ui/dialog.tsx","../src/components/ui/dropdown-menu.tsx","../src/components/common/EmptyState.tsx","../src/lib/format.ts","../src/features/media-library/components/DropZone.tsx","../src/components/ui/skeleton.tsx","../src/features/media-library/components/MediaCard.tsx","../src/features/media-library/components/MediaGrid.tsx","../src/features/media-library/hooks/useMediaImport.ts","../src/features/media-library/components/MediaLibraryPanel.tsx","../src/hooks/use-toast-safe.ts","../src/features/preview/hooks/useAvCanvas.ts","../src/features/preview/hooks/usePreviewSync.ts","../src/features/preview/hooks/usePlaybackClock.ts","../src/features/preview/hooks/useCanvasInteraction.ts","../src/features/preview/components/CaptureButton.tsx","../src/features/preview/components/SelectionOverlay.tsx","../src/features/preview/components/PreviewStage.tsx","../src/components/ui/slider.tsx","../src/components/common/Timecode.tsx","../src/features/preview/components/PlaybackControls.tsx","../src/features/preview/lib/download.ts","../src/features/preview/components/PreviewPanel.tsx","../src/lib/constants.ts","../src/features/timeline/components/TimelineToolbar.tsx","../src/features/timeline/components/Ruler.tsx","../src/features/timeline/components/TrackHeader.tsx","../src/features/timeline/hooks/useLazyVisibility.ts","../src/features/timeline/components/ClipBlock.tsx","../src/components/ui/context-menu.tsx","../src/features/timeline/components/ClipContextMenu.tsx","../src/features/timeline/components/Playhead.tsx","../src/features/timeline/components/Marquee.tsx","../src/features/timeline/hooks/useTimelineZoom.ts","../src/features/timeline/hooks/useSnap.ts","../src/features/timeline/hooks/useTimelineDrag.ts","../src/features/timeline/components/Timeline.tsx","../src/components/ui/tabs.tsx","../src/components/ui/input.tsx","../src/components/ui/select.tsx","../src/features/inspector/components/Field.tsx","../src/features/inspector/components/VideoProps.tsx","../src/features/inspector/components/AudioProps.tsx","../src/features/inspector/components/TextProps.tsx","../src/features/inspector/components/SubtitleProps.tsx","../src/features/inspector/components/ImageProps.tsx","../src/features/inspector/hooks/useClipInspector.ts","../src/features/inspector/components/InspectorPanel.tsx","../src/components/ui/switch.tsx","../src/components/ui/progress.tsx","../src/features/export/worker-url.ts","../src/features/export/worker-factory.ts","../src/features/export/export-runner.ts","../src/features/export/hooks/useExportState.ts","../src/features/export/components/ExportResult.tsx","../src/features/export/components/ExportDialog.tsx","../src/features/export/components/ExportProgress.tsx","../src/features/canvas-settings/components/CanvasSettingsDialog.tsx","../src/hooks/use-editor-actions.ts","../src/hooks/use-keyboard-shortcuts.ts","../src/hooks/use-capability.ts","../src/components/layout/use-capability-info.ts","../src/features/lab/components/LabCard.tsx","../src/features/lab/hooks/use-lab-action.ts","../src/features/lab/components/shared.tsx","../src/features/lab/components/ConcatenateLab.tsx","../src/features/lab/components/RemuxLab.tsx","../src/features/lab/components/ChromakeyLab.tsx","../src/features/lab/components/SubtitleLab.tsx","../src/features/lab/components/TrackSplitLab.tsx","../src/features/lab/components/ThumbnailLab.tsx","../src/features/lab/components/RecorderLab.tsx","../src/features/lab/components/CustomClipLab.tsx","../src/features/lab/components/LabPage.tsx","../src/app/debug-bridge.ts","../src/CutWorkbench.tsx","../src/default-bridge.ts","../../../node_modules/immer/dist/immer.mjs","../../../node_modules/zustand/esm/middleware/immer.mjs","../src/store/ui-store.ts","../src/theme.ts","../src/theme-tokens.ts","../src/provider.tsx","../src/init.tsx","../src/atoms-root.tsx"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\n/**\n * 合并 className:tailwind-merge 负责消解冲突,clsx 负责条件拼接。\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import type { ReactNode } from 'react'\r\nimport { cn } from '@/lib/cn'\r\n\r\ninterface AppShellProps {\r\n topBar: ReactNode\r\n left: ReactNode\r\n center: ReactNode\r\n right: ReactNode\r\n timeline: ReactNode\r\n /** 左侧宽度(像素) */\r\n leftWidth: number\r\n /** 右侧宽度(像素) */\r\n rightWidth: number\r\n /** 时间轴高度(像素) */\r\n timelineHeight: number\r\n leftResizer: ReactNode\r\n rightResizer: ReactNode\r\n timelineResizer: ReactNode\r\n className?: string\r\n}\r\n\r\n/**\r\n * 三栏 + 底部时间轴骨架。\r\n *\r\n * 布局用 inline style 的像素宽度做「受控」尺寸,配合 PanelResizer 的拖拽回写;\r\n * 这样不会因为 tailwind 的动态 class 被 purge 掉而失效。\r\n */\r\nexport function AppShell({\r\n topBar,\r\n left,\r\n center,\r\n right,\r\n timeline,\r\n leftWidth,\r\n rightWidth,\r\n timelineHeight,\r\n leftResizer,\r\n rightResizer,\r\n timelineResizer,\r\n className,\r\n}: AppShellProps) {\r\n return (\r\n <div className={cn('flex h-full min-h-0 flex-col bg-background', className)}>\r\n {topBar}\r\n <div className=\"flex min-h-0 flex-1\">\r\n <aside\r\n className=\"flex min-h-0 shrink-0 flex-col border-r bg-card/40\"\r\n style={{ width: leftWidth }}\r\n >\r\n {left}\r\n </aside>\r\n {leftResizer}\r\n <main className=\"flex min-w-0 flex-1 flex-col\">\r\n <div className=\"flex min-h-0 flex-1 flex-col\">{center}</div>\r\n {timelineResizer}\r\n <section\r\n className=\"flex min-h-0 shrink-0 flex-col border-t bg-timeline-bg\"\r\n style={{ height: timelineHeight }}\r\n >\r\n {timeline}\r\n </section>\r\n </main>\r\n {rightResizer}\r\n <aside\r\n className=\"flex min-h-0 shrink-0 flex-col border-l bg-card/40\"\r\n style={{ width: rightWidth }}\r\n >\r\n {right}\r\n </aside>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Slot } from \"radix-ui\"\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot.Root : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Separator as SeparatorPrimitive } from \"radix-ui\"\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n decorative = true,\n ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n return (\n <SeparatorPrimitive.Root\n data-slot=\"separator\"\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Separator }\n","/**\n * Portal 挂载点 Context(极轻量,只放一个 DOM 节点)。\n *\n * 为什么单独一个 Context,而不是复用 `WorkbenchContext.portalContainer`:\n * 1. `useWorkbench()` 在缺少 Provider 时会抛错,而 `components/ui/` 里的\n * Portal 组件必须能「裸用」(宿主只拿一个 Popover 去拼自己的页面);\n * 2. `AtomsRoot` 没有 UI store / 宿主桥,只负责「样式作用域 + 主题」,\n * 不该为了一个挂载点被迫造一整套 WorkbenchContext。\n *\n * 语义:`null` 表示「没有作用域根」,此时 Radix 退回默认的 `document.body`,\n * 与改造前的行为一致。\n */\n\nimport { createContext, useContext, type ReactNode } from 'react'\n\nexport const PortalContainerContext = createContext<HTMLElement | null>(null)\n\n/** 读取当前作用域内的 Portal 挂载点;没有则返回 null(Radix 会退回 body) */\nexport function usePortalContainer(): HTMLElement | null {\n return useContext(PortalContainerContext)\n}\n\nexport interface PortalContainerProviderProps {\n container: HTMLElement | null\n children: ReactNode\n}\n\nexport function PortalContainerProvider({ container, children }: PortalContainerProviderProps) {\n return (\n <PortalContainerContext.Provider value={container}>\n {children}\n </PortalContainerContext.Provider>\n )\n}\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport { cn } from \"@/lib/cn\"\r\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\"\r\nimport { usePortalContainer } from \"@/portal-container\"\r\n\r\nconst TooltipProviderPresence = React.createContext<boolean>(false)\r\n\r\nfunction TooltipProvider({\r\n delayDuration = 0,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\r\n return (\r\n <TooltipProviderPresence.Provider value={true}>\r\n <TooltipPrimitive.Provider\r\n data-slot=\"tooltip-provider\"\r\n delayDuration={delayDuration}\r\n {...props}\r\n />\r\n </TooltipProviderPresence.Provider>\r\n )\r\n}\r\n\r\n/**\r\n * Tooltip 在缺少 Provider 时自动兜底。\r\n *\r\n * Radix 的 Tooltip.Root 要求上层有 Tooltip.Provider,否则直接抛错\r\n * 「`Tooltip` must be used within `TooltipProvider`」。\r\n *\r\n * 对「组件库」来说这是致命的:宿主单独拿一个 `TrackHeader` 或 `ClipBlock`\r\n * 嵌到自己的页面里,不应该被迫知道「还得再包一层 Provider」——\r\n * 那是实现细节泄漏。这里检测不到上层 Provider 时就地补一个,\r\n * 已有 Provider 时行为不变(仍用宿主的 delayDuration 等配置)。\r\n */\r\nfunction useHasTooltipProvider(): boolean {\r\n // Radix 的 Provider 没有公开 context 探测手段,因此用一个内部标记 +\r\n // React context 的组合:Provider 组件自己写入标记,Tooltip 读取。\r\n return React.useContext(TooltipProviderPresence) === true\r\n}\r\n\r\n\r\nfunction Tooltip({\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Root>) {\r\n const hasProvider = useHasTooltipProvider()\r\n const root = <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\r\n if (hasProvider) return root\r\n // 兜底:自带一个 Provider,让原子组件可以裸用\r\n return <TooltipPrimitive.Provider delayDuration={300}>{root}</TooltipPrimitive.Provider>\r\n}\r\n\r\nfunction TooltipTrigger({\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\r\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />\r\n}\r\n\r\nfunction TooltipContent({\r\n className,\r\n sideOffset = 0,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\r\n // ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\r\n // utility 不生效(提示框没有背景/内边距)。原因详见 dialog.tsx 的 DialogPortal。\r\n const container = usePortalContainer()\r\n return (\r\n <TooltipPrimitive.Portal container={container ?? undefined}>\r\n <TooltipPrimitive.Content\r\n data-slot=\"tooltip-content\"\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\r\n </TooltipPrimitive.Content>\r\n </TooltipPrimitive.Portal>\r\n )\r\n}\r\n\r\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\r\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Slot } from \"radix-ui\"\n\nconst badgeVariants = cva(\n \"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n secondary:\n \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90\",\n outline:\n \"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n ghost: \"[a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 [a&]:hover:underline\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction Badge({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot.Root : \"span\"\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport { Badge, badgeVariants }\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction Popover({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\n}\n\nfunction PopoverTrigger({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n // ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n // utility 不生效(浮层没有背景/边框)。原因详见 dialog.tsx 的 DialogPortal。\n const container = usePortalContainer()\n return (\n <PopoverPrimitive.Portal container={container ?? undefined}>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n )\n}\n\nfunction PopoverAnchor({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />\n}\n\nfunction PopoverHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"popover-header\"\n className={cn(\"flex flex-col gap-1 text-sm\", className)}\n {...props}\n />\n )\n}\n\nfunction PopoverTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <div\n data-slot=\"popover-title\"\n className={cn(\"font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction PopoverDescription({\n className,\n ...props\n}: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"popover-description\"\n className={cn(\"text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Popover,\n PopoverTrigger,\n PopoverContent,\n PopoverAnchor,\n PopoverHeader,\n PopoverTitle,\n PopoverDescription,\n}\n","import { CheckCircle2, AlertTriangle, XCircle, Loader2 } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'\nimport type { CapabilityInfo } from './use-capability-info'\n\ninterface CapabilityBadgeProps {\n info: CapabilityInfo\n}\n\n/** 顶栏能力徽标:点击展开逐项结果 */\nexport function CapabilityBadge({ info }: CapabilityBadgeProps) {\n const { status, report, summary } = info\n const icon =\n status === 'checking' ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : status === 'ready' ? (\n <CheckCircle2 className=\"size-3 text-success\" />\n ) : status === 'degraded' ? (\n <AlertTriangle className=\"size-3 text-warning\" />\n ) : (\n <XCircle className=\"size-3 text-destructive\" />\n )\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Badge\n variant=\"outline\"\n className=\"cursor-pointer gap-1 text-[11px] font-normal select-none\"\n >\n {icon}\n {summary}\n </Badge>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 space-y-2 text-xs\">\n <p className=\"font-medium\">浏览器能力</p>\n {report == null ? (\n <p className=\"text-muted-foreground\">{info.loading ? '探测中…' : '探测失败'}</p>\n ) : (\n <ul className=\"space-y-1\">\n <Row label=\"WebCodecs\" ok={report.webCodecs} />\n <Row label=\"H.264 编码\" ok={report.videoEncode} />\n <Row label=\"AAC 编码\" ok={report.audioEncode} />\n <Row label=\"OffscreenCanvas\" ok={report.offscreenCanvas} />\n <Row label=\"OPFS\" ok={report.opfs} />\n <Row label=\"WebGL2(绿幕)\" ok={report.webgl2} optional />\n <Row label=\"Combinator\" ok={report.composerSupported} />\n </ul>\n )}\n <p className=\"border-t pt-2 text-muted-foreground\">\n 输出固定为 MP4(H.264 + AAC 48kHz)。仅支持 Chromium 内核浏览器。\n </p>\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction Row({ label, ok, optional }: { label: string; ok: boolean; optional?: boolean }) {\n return (\n <li className=\"flex items-center justify-between gap-2\">\n <span className={optional ? 'text-muted-foreground' : undefined}>{label}</span>\n <span className={ok ? 'text-success' : optional ? 'text-muted-foreground' : 'text-destructive'}>\n {ok ? '可用' : optional ? '不可用(可选)' : '不可用'}\n </span>\n </li>\n )\n}\n","/**\r\n * 工作台自身的 Context:UI 状态(面板尺寸 / 缩放 / 对话框)+ 宿主桥。\r\n *\r\n * 与 `@cut-workbench/react` 的编辑器 Context 分开:编辑器是「数据与引擎」,\r\n * 这里是「某一次挂载的布局与交互」。两者生命周期可以不同(例如宿主切换项目时\r\n * 复用同一个编辑器、只重建 UI)。\r\n */\r\n\r\nimport { createContext, useContext, useSyncExternalStore, type ReactNode } from 'react'\r\nimport type { HostBridge } from '@cut-workbench/core'\r\nimport type { UiState, UiStore } from './store/ui-store'\r\n\r\nexport interface WorkbenchTheme {\r\n /**\r\n * 宿主请求的主题:\r\n * - `light` / `dark`:固定主题;\r\n * - `inherit`:跟随宿主 `<html class=\"dark\">`(见 theme.ts)。\r\n */\r\n mode?: 'light' | 'dark' | 'inherit'\r\n /**\r\n * 实际生效的主题(`inherit` 已折算成亮/暗)。\r\n *\r\n * 消费方(例如 Sonner 的 Toaster)必须用这个值,而不是 `mode` ——\r\n * `mode` 可能是 `inherit`,那不是 Sonner 认识的主题名。\r\n */\r\n resolved?: 'light' | 'dark'\r\n}\r\n\r\nexport interface WorkbenchConfig {\r\n /** 功能开关 */\r\n features: {\r\n mediaLibrary: boolean\r\n preview: boolean\r\n timeline: boolean\r\n inspector: boolean\r\n export: boolean\r\n canvasSettings: boolean\r\n /** 能力实验室:默认关闭,属于内部验证用途 */\r\n lab: boolean\r\n }\r\n /** 键盘快捷键作用域:global 全局、scoped 仅容器聚焦时、off 关闭 */\r\n keyboard: 'global' | 'scoped' | 'off'\r\n /** 是否允许拖拽调整面板尺寸 */\r\n resizable: boolean\r\n}\r\n\r\nexport interface WorkbenchContextValue {\r\n ui: UiStore\r\n bridge: HostBridge\r\n config: WorkbenchConfig\r\n theme: Required<WorkbenchTheme>\r\n /** Radix portal 的挂载点(作用域化样式与 z-index 依赖它) */\r\n portalContainer: HTMLElement | null\r\n}\r\n\r\nexport const WorkbenchContext = createContext<WorkbenchContextValue | null>(null)\r\n\r\nexport function useWorkbench(): WorkbenchContextValue {\r\n const value = useContext(WorkbenchContext)\r\n if (value == null) {\r\n throw new Error('[cut-workbench] 缺少 <WorkbenchProvider>:组件必须挂在工作台内部')\r\n }\r\n return value\r\n}\r\n\r\n/** 订阅 UI 状态(带 selector) */\r\nexport function useUi<T>(selector: (state: UiState) => T): T {\r\n const { ui } = useWorkbench()\r\n return useSyncExternalStore(\r\n ui.subscribe,\r\n () => selector(ui.getState()),\r\n () => selector(ui.getState()),\r\n )\r\n}\r\n\r\n/** 取 UI store 本体(事件回调里读写用) */\r\nexport function useUiStoreApi(): UiStore {\r\n return useWorkbench().ui\r\n}\r\n\r\nexport interface WorkbenchProviderProps {\r\n value: WorkbenchContextValue\r\n children: ReactNode\r\n}\r\n\r\nexport function WorkbenchProvider({ value, children }: WorkbenchProviderProps) {\r\n return <WorkbenchContext.Provider value={value}>{children}</WorkbenchContext.Provider>\r\n}\r\n\r\n/** 默认功能开关 */\r\nexport const DEFAULT_FEATURES: WorkbenchConfig['features'] = {\r\n mediaLibrary: true,\r\n preview: true,\r\n timeline: true,\r\n inspector: true,\r\n export: true,\r\n canvasSettings: true,\r\n lab: false,\r\n}\r\n\r\nexport const DEFAULT_CONFIG: WorkbenchConfig = {\r\n features: DEFAULT_FEATURES,\r\n keyboard: 'global',\r\n resizable: true,\r\n}\r\n","/**\r\n * 内置顶栏(WorkbenchToolbar)。\r\n *\r\n * 由旧 `TopBar` 演进而来,做了三件事适配「组件库」形态:\r\n * - 品牌区可替换(`brand`),不强制显示项目名;\r\n * - 保存/打开是可选回调,宿主不提供就不渲染按钮;\r\n * - 「视图切换(编辑器/实验室)」只在开启 lab 特性时显示。\r\n */\r\n\r\nimport {\r\n Clapperboard,\r\n Download,\r\n FlaskConical,\r\n FolderOpen,\r\n Monitor,\r\n Redo2,\r\n Save,\r\n Undo2,\r\n Video,\r\n} from 'lucide-react'\r\nimport type { ReactNode } from 'react'\r\nimport { useEditor, useHistory } from '@cut-workbench/react'\r\nimport { ratioLabel } from '@cut-workbench/core'\r\nimport { Button } from '@/components/ui/button'\r\nimport { Separator } from '@/components/ui/separator'\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\r\nimport { CapabilityBadge } from './CapabilityBadge'\r\nimport { useUi } from '../../context'\r\nimport type { CapabilityInfo } from './use-capability-info'\r\n\r\nexport interface WorkbenchToolbarProps {\r\n capability: CapabilityInfo\r\n onExport: () => void\r\n /** 不提供则不渲染「保存」按钮 */\r\n onSaveProject?: () => void\r\n /** 不提供则不渲染「打开」按钮 */\r\n onOpenProject?: () => void\r\n /** 替换左侧品牌区;传 null 表示不要 */\r\n brand?: ReactNode | null\r\n /** 是否显示「实验室」入口 */\r\n showLab?: boolean\r\n}\r\n\r\n/** 顶栏:品牌 / 工程名 / 撤销重做 / 保存 / 导出 / 能力徽标 / 视图切换 */\r\nexport function WorkbenchToolbar({\r\n capability,\r\n onExport,\r\n onSaveProject,\r\n onOpenProject,\r\n brand,\r\n showLab = false,\r\n}: WorkbenchToolbarProps) {\r\n const projectName = useEditor((s) => s.project.name)\r\n const dirty = useEditor((s) => s.dirty)\r\n const undo = useEditor((s) => s.undo)\r\n const redo = useEditor((s) => s.redo)\r\n const canUndo = useHistory((s) => s.past.length > 0)\r\n const canRedo = useHistory((s) => s.future.length > 0)\r\n const route = useUi((s) => s.route)\r\n const setRoute = useUi((s) => s.setRoute)\r\n const openDialog = useUi((s) => s.openDialog)\r\n const clipCount = useEditor((s) => s.project.clips.length)\r\n const projectWidth = useEditor((s) => s.project.width)\r\n const projectHeight = useEditor((s) => s.project.height)\r\n\r\n const branding =\r\n brand === null ? null : (\r\n brand ?? (\r\n <div className=\"flex items-center gap-2 pr-2\">\r\n <div className=\"flex size-7 items-center justify-center rounded-md bg-primary text-primary-foreground\">\r\n <Clapperboard className=\"size-4\" />\r\n </div>\r\n <span className=\"text-sm font-semibold tracking-tight\">Cut Workbench</span>\r\n </div>\r\n )\r\n )\r\n\r\n return (\r\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b bg-card px-3\">\r\n {branding}\r\n\r\n {branding != null && <Separator orientation=\"vertical\" className=\"h-5\" />}\r\n\r\n <div className=\"flex min-w-0 items-center gap-1.5\">\r\n <span className=\"truncate text-sm font-medium\">{projectName}</span>\r\n {dirty && <span className=\"size-1.5 rounded-full bg-primary\" title=\"未保存\" />}\r\n </div>\r\n\r\n <span className=\"hidden text-xs text-muted-foreground sm:inline\">\r\n {clipCount > 0 ? `${clipCount} 个片段` : '空工程'} · {ratioLabel(projectWidth, projectHeight)}\r\n </span>\r\n\r\n <div className=\"ml-auto flex items-center gap-1\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={undo} disabled={!canUndo} aria-label=\"撤销\">\r\n <Undo2 className=\"size-4\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>撤销 (Ctrl+Z)</TooltipContent>\r\n </Tooltip>\r\n\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={redo} disabled={!canRedo} aria-label=\"重做\">\r\n <Redo2 className=\"size-4\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>重做 (Ctrl+Shift+Z)</TooltipContent>\r\n </Tooltip>\r\n\r\n <Separator orientation=\"vertical\" className=\"mx-1 h-5\" />\r\n\r\n {onOpenProject != null && (\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onOpenProject} aria-label=\"加载工程\">\r\n <FolderOpen className=\"size-4\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>加载已保存工程</TooltipContent>\r\n </Tooltip>\r\n )}\r\n\r\n {onSaveProject != null && (\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onSaveProject} aria-label=\"保存工程\">\r\n <Save className=\"size-4\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>保存工程到本地</TooltipContent>\r\n </Tooltip>\r\n )}\r\n\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"gap-1.5\"\r\n aria-label=\"画布设置\"\r\n onClick={() => openDialog('projectSettings')}\r\n >\r\n <Monitor className=\"size-4\" />\r\n <span className=\"hidden md:inline\">画布设置</span>\r\n </Button>\r\n\r\n {showLab && (\r\n <Button\r\n variant={route === 'lab' ? 'secondary' : 'ghost'}\r\n size=\"sm\"\r\n className=\"gap-1.5\"\r\n onClick={() => setRoute(route === 'lab' ? 'editor' : 'lab')}\r\n >\r\n {route === 'lab' ? <Video className=\"size-4\" /> : <FlaskConical className=\"size-4\" />}\r\n <span className=\"hidden md:inline\">{route === 'lab' ? '返回编辑器' : '能力实验室'}</span>\r\n </Button>\r\n )}\r\n\r\n <CapabilityBadge info={capability} />\r\n\r\n <Button size=\"sm\" className=\"ml-1 gap-1.5\" onClick={onExport}>\r\n <Download className=\"size-4\" />\r\n 导出\r\n </Button>\r\n </div>\r\n </header>\r\n )\r\n}\r\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\ninterface ResizerOptions {\n /** 拖拽开始时读取当前尺寸 */\n initial: number\n min: number\n max: number\n /** 方向:横向拖拽调整宽度,纵向调整高度 */\n axis: 'x' | 'y'\n /** 反向:拖拽向右但尺寸变小(右侧面板) */\n invert?: boolean\n onChange: (value: number) => void\n}\n\n/**\n * 面板拖拽调整。内部用 rAF 节流,松手才把最终值写回 store(避免每帧重渲染整棵树)。\n */\nexport function usePanelResize({ initial, min, max, axis, invert, onChange }: ResizerOptions) {\n const [dragging, setDragging] = useState(false)\n const startRef = useRef(0)\n const originRef = useRef(initial)\n const frameRef = useRef<number | null>(null)\n const latestRef = useRef(initial)\n\n const onPointerDown = useCallback(\n (event: React.PointerEvent) => {\n event.preventDefault()\n startRef.current = axis === 'x' ? event.clientX : event.clientY\n originRef.current = initial\n latestRef.current = initial\n setDragging(true)\n },\n [axis, initial],\n )\n\n useEffect(() => {\n if (!dragging) return\n const move = (event: PointerEvent) => {\n const current = axis === 'x' ? event.clientX : event.clientY\n const delta = (current - startRef.current) * (invert ? -1 : 1)\n const next = Math.max(min, Math.min(max, originRef.current + delta))\n latestRef.current = next\n if (frameRef.current != null) return\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null\n onChange(latestRef.current)\n })\n }\n const up = () => {\n if (frameRef.current != null) {\n cancelAnimationFrame(frameRef.current)\n frameRef.current = null\n }\n onChange(latestRef.current)\n setDragging(false)\n }\n window.addEventListener('pointermove', move)\n window.addEventListener('pointerup', up)\n return () => {\n window.removeEventListener('pointermove', move)\n window.removeEventListener('pointerup', up)\n }\n }, [axis, dragging, invert, max, min, onChange])\n\n return { dragging, onPointerDown }\n}\n","import type { ComponentPropsWithoutRef } from 'react'\nimport { cn } from '@/lib/cn'\nimport { usePanelResize } from './use-panel-resize'\n\ninterface PanelResizerProps {\n orientation: 'vertical' | 'horizontal'\n /** 当前尺寸 */\n value: number\n min: number\n max: number\n /** 反向拖拽(右侧 / 底部面板) */\n invert?: boolean\n onChange: (value: number) => void\n className?: string\n label?: string\n}\n\n/**\n * 三栏骨架的分隔条。\n *\n * `orientation=\"vertical\"` 表示「竖线」,拖动改变左右宽度;\n * `orientation=\"horizontal\"` 表示「横线」,拖动改变上下高度。\n */\nexport function PanelResizer({\n orientation,\n value,\n min,\n max,\n invert,\n onChange,\n className,\n label = '拖动调整面板大小',\n}: PanelResizerProps) {\n const isVertical = orientation === 'vertical'\n const { dragging, onPointerDown } = usePanelResize({\n initial: value,\n min,\n max,\n axis: isVertical ? 'x' : 'y',\n invert,\n onChange,\n })\n\n return (\n <div\n role=\"separator\"\n aria-orientation={isVertical ? 'vertical' : 'horizontal'}\n aria-label={label}\n onPointerDown={onPointerDown}\n className={cn(\n 'group relative z-20 shrink-0 touch-none transition-colors',\n isVertical ? 'w-1 cursor-col-resize' : 'h-1 cursor-row-resize',\n dragging ? 'bg-primary/60' : 'bg-border hover:bg-primary/40',\n className,\n )}\n >\n {/* 加大命中区域,避免 1px 宽难以点中 */}\n <div\n className={cn(\n 'absolute',\n isVertical ? '-left-1 -right-1 inset-y-0' : '-top-1 -bottom-1 inset-x-0',\n )}\n />\n </div>\n )\n}\n\nexport type PanelResizerElementProps = ComponentPropsWithoutRef<'div'>\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Card({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card\"\n className={cn(\n \"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\n \"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-title\"\n className={cn(\"leading-none font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-action\"\n className={cn(\n \"col-start-2 row-span-2 row-start-1 self-start justify-self-end\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-content\"\n className={cn(\"px-6\", className)}\n {...props}\n />\n )\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-footer\"\n className={cn(\"flex items-center px-6 [.border-t]:pt-6\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n}\n","import { Loader2 } from 'lucide-react'\nimport { cn } from '@/lib/cn'\n\ninterface LoadingOverlayProps {\n visible: boolean\n title?: string\n description?: string\n /** 0~1;不传则显示不确定进度 */\n progress?: number\n className?: string\n /** 是否覆盖整个视口 */\n fullscreen?: boolean\n children?: React.ReactNode\n}\n\n/** 全屏/局部遮罩加载态,用于导出、批量导入等长任务 */\nexport function LoadingOverlay({\n visible,\n title = '处理中…',\n description,\n progress,\n className,\n fullscreen,\n children,\n}: LoadingOverlayProps) {\n if (!visible) return null\n return (\n <div\n className={cn(\n 'z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm',\n fullscreen ? 'fixed inset-0' : 'absolute inset-0',\n className,\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"w-full max-w-sm space-y-3 rounded-lg border bg-card p-5 shadow-lg\">\n <div className=\"flex items-center gap-2\">\n <Loader2 className=\"size-4 animate-spin text-primary\" />\n <p className=\"text-sm font-medium\">{title}</p>\n </div>\n {description != null && (\n <p className=\"text-xs text-muted-foreground\">{description}</p>\n )}\n {progress != null && (\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-primary transition-all duration-200\"\n style={{ width: `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%` }}\n />\n </div>\n )}\n {children}\n </div>\n </div>\n )\n}\n","import { AlertOctagon, CheckCircle2, XCircle } from 'lucide-react'\nimport type { CapabilityReport } from '@cut-workbench/core/engine'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { LoadingOverlay } from '@/components/common/LoadingOverlay'\n\ninterface UnsupportedBrowserProps {\n report: CapabilityReport | null\n loading: boolean\n error?: string | null\n}\n\nconst CHECKS: Array<{ key: keyof CapabilityReport; label: string; hint: string }> = [\n { key: 'webCodecs', label: 'WebCodecs', hint: '需 Chrome 94+ / Edge 94+' },\n { key: 'videoEncode', label: 'H.264 编码', hint: 'avc1.42E032' },\n { key: 'audioEncode', label: 'AAC 编码', hint: 'mp4a.40.2 / 48kHz' },\n { key: 'offscreenCanvas', label: 'OffscreenCanvas', hint: '离屏渲染' },\n { key: 'opfs', label: 'OPFS', hint: 'WebAV 临时文件存储' },\n { key: 'webgl2', label: 'WebGL2', hint: '绿幕抠像(可选)' },\n]\n\n/** 不满足运行条件时的阻断页:逐项说明缺失能力,不做静默降级 */\nexport function UnsupportedBrowser({ report, loading, error }: UnsupportedBrowserProps) {\n if (loading) {\n return (\n <div className=\"relative flex min-h-screen items-center justify-center bg-background\">\n <LoadingOverlay visible title=\"正在检测浏览器能力…\" fullscreen={false} />\n </div>\n )\n }\n\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-background p-6\">\n <Card className=\"w-full max-w-2xl\">\n <CardHeader>\n <div className=\"flex items-center gap-2 text-destructive\">\n <AlertOctagon className=\"size-5\" />\n <CardTitle className=\"text-base\">当前浏览器无法运行本应用</CardTitle>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n WebAV 依赖 WebCodecs 与 OPFS,仅支持 Chromium 内核浏览器。请使用最新版 Chrome\n 或 Edge(非无痕模式)重新打开。\n </p>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {report != null && (\n <div className=\"grid gap-2 sm:grid-cols-2\">\n {CHECKS.map((check) => {\n const value = report[check.key]\n const ok = typeof value === 'boolean' ? value : false\n return (\n <div\n key={check.key}\n className=\"flex items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm\"\n >\n <div className=\"flex items-center gap-2\">\n {ok ? (\n <CheckCircle2 className=\"size-4 text-success\" />\n ) : (\n <XCircle className=\"size-4 text-destructive\" />\n )}\n <span>{check.label}</span>\n </div>\n <span className=\"text-xs text-muted-foreground\">{check.hint}</span>\n </div>\n )\n })}\n </div>\n )}\n\n {report != null && report.issues.length > 0 && (\n <ul className=\"list-disc space-y-1 rounded-md bg-muted p-3 pl-7 text-xs text-muted-foreground\">\n {report.issues.map((issue) => (\n <li key={issue}>{issue}</li>\n ))}\n </ul>\n )}\n\n {error != null && (\n <p className=\"rounded-md bg-destructive/10 p-3 text-xs text-destructive\">{error}</p>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" onClick={() => window.location.reload()}>\n 重新检测\n </Button>\n <span className=\"text-xs text-muted-foreground\">{report?.advice}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n )\n}\n","import {\r\n CircleCheckIcon,\r\n InfoIcon,\r\n Loader2Icon,\r\n OctagonXIcon,\r\n TriangleAlertIcon,\r\n} from \"lucide-react\"\r\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\r\n\r\n/**\r\n * 工作台内的 toast 容器。\r\n *\r\n * 主题跟随 `CutWorkbenchProvider` 的 data-cw-theme,而不是写死 dark ——\r\n * 库可能被放在亮色宿主里。样式 token 全部走 `--cw-*`,因此不受宿主全局变量影响。\r\n */\r\nconst Toaster = (props: ToasterProps) => {\r\n return (\r\n <Sonner\r\n className=\"toaster group\"\r\n icons={{\r\n success: <CircleCheckIcon className=\"size-4\" />,\r\n info: <InfoIcon className=\"size-4\" />,\r\n warning: <TriangleAlertIcon className=\"size-4\" />,\r\n error: <OctagonXIcon className=\"size-4\" />,\r\n loading: <Loader2Icon className=\"size-4 animate-spin\" />,\r\n }}\r\n style={\r\n {\r\n \"--normal-bg\": \"var(--cw-popover)\",\r\n \"--normal-text\": \"var(--cw-popover-foreground)\",\r\n \"--normal-border\": \"var(--cw-border)\",\r\n \"--border-radius\": \"var(--cw-radius)\",\r\n } as React.CSSProperties\r\n }\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport { Toaster }\r\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Textarea }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Label as LabelPrimitive } from \"radix-ui\"\n\nfunction Label({\n className,\n ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Label }\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { ScrollArea as ScrollAreaPrimitive } from \"radix-ui\"\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\n return (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n data-slot=\"scroll-area-scrollbar\"\n orientation={orientation}\n className={cn(\n \"flex touch-none p-px transition-colors select-none\",\n orientation === \"vertical\" &&\n \"h-full w-2.5 border-l border-l-transparent\",\n orientation === \"horizontal\" &&\n \"h-2.5 flex-col border-t border-t-transparent\",\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb\n data-slot=\"scroll-area-thumb\"\n className=\"relative flex-1 rounded-full bg-border\"\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n )\n}\n\nexport { ScrollArea, ScrollBar }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { XIcon } from \"lucide-react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction Dialog({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\n/**\n * ⚠️ 本文件是 shadcn 生成物,按约定「只改样式 token,不改结构」。\n * 这里是一个**必要的结构修正**,请勿在重新生成时覆盖掉:\n *\n * Radix 的 Portal 默认挂到 `document.body`,而库的全部 utility 都被作用域化成\n * `.cw-root .xxx`。挂到 body 的对话框因此在 `.cw-root` 之外,拿不到任何样式\n * (背景透明、圆角为 0、translate 变体失效导致定位到视口外),实测「点导出后\n * 屏幕上看不到任何对话框」。\n *\n * 因此在每个 Portal 上消费 `PortalContainerContext`(由 `<CutWorkbenchProvider>`\n * 或 `<AtomsRoot>` 提供,即 `.cw-root` 根节点)。没有 Provider 时退回 body,\n * 保持组件可裸用。\n */\nfunction DialogPortal({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n const container = usePortalContainer()\n return (\n <DialogPrimitive.Portal\n data-slot=\"dialog-portal\"\n container={container ?? undefined}\n {...props}\n />\n )\n}\n\nfunction DialogClose({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n )\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n}\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\"\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction DropdownMenu({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />\n}\n\n// ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n// utility 不生效(菜单没有背景/边框)。原因详见 dialog.tsx 的 DialogPortal。\nfunction DropdownMenuPortal({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n const container = usePortalContainer()\n return (\n <DropdownMenuPrimitive.Portal\n data-slot=\"dropdown-menu-portal\"\n container={container ?? undefined}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuTrigger({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n return (\n <DropdownMenuPrimitive.Trigger\n data-slot=\"dropdown-menu-trigger\"\n {...props}\n />\n )\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n // Portal 挂载点由 DropdownMenuPortal 统一处理(含 `.cw-root` 作用域)\n return (\n <DropdownMenuPortal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </DropdownMenuPortal>\n )\n}\n\nfunction DropdownMenuGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n return (\n <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />\n )\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean\n variant?: \"default\" | \"destructive\"\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n )\n}\n\nfunction DropdownMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n return (\n <DropdownMenuPrimitive.RadioGroup\n data-slot=\"dropdown-menu-radio-group\"\n {...props}\n />\n )\n}\n\nfunction DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n )\n}\n\nfunction DropdownMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean\n}) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn(\n \"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuShortcut({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuSub({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean\n}) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n )\n}\n\nfunction DropdownMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n DropdownMenu,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuLabel,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n}\n","import type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/cn'\n\ninterface EmptyStateProps {\n icon: LucideIcon\n title: string\n description?: string\n action?: ReactNode\n className?: string\n compact?: boolean\n}\n\n/** 统一空态:图标 + 标题 + 说明 + 可选操作 */\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n action,\n className,\n compact,\n}: EmptyStateProps) {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-2 text-center',\n compact ? 'px-4 py-6' : 'px-6 py-12',\n className,\n )}\n >\n <div className=\"flex size-10 items-center justify-center rounded-full bg-muted text-muted-foreground\">\n <Icon className=\"size-5\" />\n </div>\n <p className=\"text-sm font-medium\">{title}</p>\n {description != null && (\n <p className=\"max-w-xs text-xs leading-relaxed text-muted-foreground\">{description}</p>\n )}\n {action}\n </div>\n )\n}\n","import { formatDuration, formatTimecode, usToSec } from '@cut-workbench/core'\n\nexport { formatDuration, formatTimecode }\n\n/** 字节数 → 人类可读 */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n const units = ['KB', 'MB', 'GB']\n let value = bytes / 1024\n let i = 0\n while (value >= 1024 && i < units.length - 1) {\n value /= 1024\n i += 1\n }\n return `${value.toFixed(value >= 100 ? 0 : 1)} ${units[i]}`\n}\n\n/** 像素尺寸 → `1920×1080` */\nexport function formatResolution(width: number, height: number): string {\n if (!width || !height) return '—'\n return `${width}×${height}`\n}\n\n/** 秒数 → `12.3s` */\nexport function formatElapsed(seconds: number): string {\n if (seconds < 60) return `${seconds.toFixed(1)}s`\n const m = Math.floor(seconds / 60)\n const s = Math.round(seconds % 60)\n return `${m}m ${s}s`\n}\n\n/** 微秒 → 秒(保留一位小数,用于表单展示) */\nexport function formatSeconds(us: number): string {\n return usToSec(us).toFixed(2)\n}\n\n/** 百分比 0~1 → `42%` */\nexport function formatPercent(ratio: number): string {\n return `${Math.round(ratio * 100)}%`\n}\n","import { useDropzone } from 'react-dropzone'\r\nimport { Upload } from 'lucide-react'\r\nimport { cn } from '@/lib/cn'\r\n\r\ninterface DropZoneProps {\r\n onFiles: (files: File[]) => void\r\n disabled?: boolean\r\n className?: string\r\n /** 紧凑模式用于媒体库顶部条 */\r\n compact?: boolean\r\n}\r\n\r\nconst ACCEPT = {\r\n 'video/mp4': ['.mp4', '.m4v'],\r\n 'audio/*': ['.mp3', '.wav', '.m4a', '.aac', '.ogg', '.flac'],\r\n 'image/*': ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.avif'],\r\n 'text/plain': ['.srt'],\r\n}\r\n\r\n/**\r\n * 拖拽 / 点击导入区域。\r\n *\r\n * 注意:react-dropzone 的 accept 只做初筛,真正按 MIME + 扩展名校验在\r\n * core/media/file-reader,从而保证「拖入 .webm 得到明确错误」而不是被静默拒绝。\r\n */\r\nexport function DropZone({ onFiles, disabled, className, compact }: DropZoneProps) {\r\n const { getRootProps, getInputProps, isDragActive, open } = useDropzone({\r\n onDrop: (accepted) => {\r\n if (accepted.length > 0) onFiles(accepted)\r\n },\r\n noClick: compact,\r\n noKeyboard: compact,\r\n multiple: true,\r\n disabled,\r\n accept: ACCEPT,\r\n })\r\n\r\n return (\r\n <div\r\n {...getRootProps()}\r\n className={cn(\r\n 'group relative flex cursor-pointer flex-col items-center justify-center gap-1.5 rounded-lg border border-dashed text-center transition-colors',\r\n compact ? 'px-3 py-2.5' : 'px-4 py-8',\r\n isDragActive\r\n ? 'border-primary bg-primary/10'\r\n : 'border-border hover:border-primary/60 hover:bg-muted/40',\r\n disabled && 'pointer-events-none opacity-50',\r\n className,\r\n )}\r\n >\r\n <input {...getInputProps()} />\r\n <div className=\"flex items-center gap-2 text-sm font-medium\">\r\n <Upload className={cn('text-muted-foreground', compact ? 'size-3.5' : 'size-4')} />\r\n <span>{compact ? '导入素材' : isDragActive ? '松开以导入' : '拖入文件,或点击选择'}</span>\r\n </div>\r\n {!compact && (\r\n <p className=\"text-xs text-muted-foreground\">\r\n 视频仅支持 MP4 · 音频 / 图片 / SRT 字幕 · 支持多选\r\n </p>\r\n )}\r\n {compact && (\r\n <button\r\n type=\"button\"\r\n className=\"sr-only\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n open()\r\n }}\r\n >\r\n 选择文件\r\n </button>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { cn } from \"@/lib/cn\"\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"skeleton\"\n className={cn(\"animate-pulse rounded-md bg-accent\", className)}\n {...props}\n />\n )\n}\n\nexport { Skeleton }\n","import { useMemo } from 'react'\r\nimport { Film, Image as ImageIcon, Music, Plus, Subtitles, Type, Trash2 } from 'lucide-react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { formatBytes, formatDuration, formatResolution } from '@/lib/format'\r\nimport { buildWaveformPath } from '@cut-workbench/core'\r\nimport { cn } from '@/lib/cn'\r\nimport { Button } from '@/components/ui/button'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuSeparator,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu'\r\n\r\ninterface MediaCardProps {\r\n asset: MediaAsset\r\n selected: boolean\r\n /** 源文件缺失(刷新后恢复的工程):卡片显示警示,双击提示重新导入 */\r\n missing?: boolean\r\n onSelect: () => void\r\n onAddToTimeline: () => void\r\n onShowMeta: () => void\r\n onRemove: () => void\r\n}\r\n\r\nconst KIND_META = {\r\n video: { icon: Film, label: '视频', tone: 'bg-clip-video/20 text-clip-video' },\r\n audio: { icon: Music, label: '音频', tone: 'bg-clip-audio/20 text-clip-audio' },\r\n image: { icon: ImageIcon, label: '图片', tone: 'bg-clip-image/20 text-clip-image' },\r\n text: { icon: Type, label: '文字', tone: 'bg-clip-text/20 text-clip-text' },\r\n subtitle: { icon: Subtitles, label: '字幕', tone: 'bg-clip-subtitle/20 text-clip-subtitle' },\r\n} as const\r\n\r\n/** 素材卡片:缩略图 / 波形 / 元数据 / 快捷操作 */\r\nexport function MediaCard({\r\n asset,\r\n selected,\r\n missing,\r\n onSelect,\r\n onAddToTimeline,\r\n onShowMeta,\r\n onRemove,\r\n}: MediaCardProps) {\r\n const kind = KIND_META[asset.kind]\r\n const Icon = kind.icon\r\n const cover = asset.thumbnailUrls?.[0]\r\n\r\n const waveformPath = useMemo(() => {\r\n if (!asset.waveform || asset.waveform.length === 0) return ''\r\n return buildWaveformPath(asset.waveform, 100, 28)\r\n }, [asset.waveform])\r\n\r\n return (\r\n <div\r\n role=\"button\"\r\n tabIndex={0}\r\n onClick={onSelect}\r\n onDoubleClick={onAddToTimeline}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') onAddToTimeline()\r\n }}\r\n className={cn(\r\n 'group relative flex cursor-pointer flex-col gap-1.5 rounded-lg border p-1.5 text-left transition-colors outline-none',\r\n // 缺源文件用 warning token,不再写死 amber:宿主换肤后警示色也要跟着走\r\n missing\r\n ? 'border-warning/50 bg-warning/5'\r\n : selected\r\n ? 'border-primary bg-primary/10 ring-1 ring-primary/40'\r\n : 'border-border bg-card hover:border-primary/40 hover:bg-muted/40 focus-visible:border-primary',\r\n )}\r\n >\r\n <div className=\"relative aspect-video w-full overflow-hidden rounded-md bg-muted\">\r\n {cover != null ? (\r\n <img src={cover} alt=\"\" className=\"size-full object-cover\" draggable={false} />\r\n ) : asset.kind === 'audio' && waveformPath !== '' ? (\r\n <svg viewBox=\"0 0 100 28\" preserveAspectRatio=\"none\" className=\"size-full p-1.5 text-clip-audio\">\r\n <path d={waveformPath} stroke=\"currentColor\" strokeWidth={0.6} opacity={0.9} />\r\n </svg>\r\n ) : (\r\n <div className={cn('flex size-full items-center justify-center', kind.tone)}>\r\n <Icon className=\"size-7\" />\r\n </div>\r\n )}\r\n\r\n {/*\r\n * 角标压在缩略图上,语义固定为「深底浅字」:底下是任意画面内容,\r\n * 浅色主题下改浅底会直接看不清。因此走 --cw-badge-scrim 系列 token\r\n * (亮/暗两套值相同),而不是 bg-black/text-white 写死。\r\n */}\r\n <span className=\"absolute top-1 left-1 flex items-center gap-1 rounded bg-badge-scrim px-1.5 py-0.5 text-[10px] text-badge-scrim-foreground backdrop-blur-sm\">\r\n <Icon className=\"size-2.5\" />\r\n {kind.label}\r\n </span>\r\n {missing && (\r\n <span className=\"absolute inset-x-1 top-7 rounded bg-warning px-1.5 py-0.5 text-center text-[10px] text-warning-foreground\">\r\n 源文件缺失 · 请重新导入\r\n </span>\r\n )}\r\n <span className=\"absolute right-1 bottom-1 rounded bg-badge-scrim px-1 py-0.5 font-mono text-[10px] text-badge-scrim-foreground tabular-nums\">\r\n {formatDuration(asset.durationUs)}\r\n </span>\r\n\r\n <div className=\"absolute inset-0 flex items-center justify-center gap-1 bg-badge-scrim opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100\">\r\n <Button\r\n size=\"icon-sm\"\r\n variant=\"secondary\"\r\n aria-label=\"添加到时间轴\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n onAddToTimeline()\r\n }}\r\n >\r\n <Plus className=\"size-3.5\" />\r\n </Button>\r\n <Button\r\n size=\"icon-sm\"\r\n variant=\"secondary\"\r\n aria-label=\"删除素材\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n onRemove()\r\n }}\r\n >\r\n <Trash2 className=\"size-3.5\" />\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"min-w-0 px-0.5\">\r\n <p className=\"truncate text-xs font-medium\" title={asset.name}>\r\n {asset.name}\r\n </p>\r\n <p className=\"truncate text-[10px] text-muted-foreground\">\r\n {asset.kind === 'audio'\r\n ? `${formatBytes(asset.size)} · 48kHz 双声道`\r\n : asset.kind === 'text' || asset.kind === 'subtitle'\r\n ? `${formatResolution(asset.width, asset.height)}`\r\n : `${formatResolution(asset.width, asset.height)} · ${formatBytes(asset.size)}`}\r\n {asset.hasAudio ? ' · 含音轨' : ''}\r\n </p>\r\n </div>\r\n\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <button\r\n type=\"button\"\r\n className=\"absolute top-1 right-1 rounded bg-badge-scrim px-1 text-[11px] text-badge-scrim-foreground opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100 data-[state=open]:opacity-100\"\r\n onClick={(e) => e.stopPropagation()}\r\n aria-label=\"更多操作\"\r\n >\r\n ⋯\r\n </button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\" onClick={(e) => e.stopPropagation()}>\r\n <DropdownMenuItem onSelect={onAddToTimeline}>添加到时间轴</DropdownMenuItem>\r\n <DropdownMenuItem onSelect={onShowMeta}>查看详细信息</DropdownMenuItem>\r\n <DropdownMenuSeparator />\r\n <DropdownMenuItem variant=\"destructive\" onSelect={onRemove}>\r\n 删除素材\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n )\r\n}\r\n","import { Upload } from 'lucide-react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { cn } from '@/lib/cn'\r\nimport { Skeleton } from '@/components/ui/skeleton'\r\nimport { MediaCard } from './MediaCard'\r\nimport type { ImportProgressItem } from '../hooks/useMediaImport'\r\n\r\ninterface MediaGridProps {\r\n assets: MediaAsset[]\r\n selectedAssetId: string | null\r\n missingAssetIds?: string[]\r\n progress: ImportProgressItem[]\r\n onSelect: (assetId: string) => void\r\n onAddToTimeline: (assetId: string) => void\r\n onShowMeta: (assetId: string) => void\r\n onRemove: (assetId: string) => void\r\n onDropFiles: (files: File[]) => void\r\n}\r\n\r\n/** 素材网格:已导入卡片 + 导入中骨架 */\r\nexport function MediaGrid({\r\n assets,\r\n selectedAssetId,\r\n missingAssetIds = [],\r\n progress,\r\n onSelect,\r\n onAddToTimeline,\r\n onShowMeta,\r\n onRemove,\r\n onDropFiles,\r\n}: MediaGridProps) {\r\n return (\r\n <div className=\"grid grid-cols-2 gap-2.5 p-2.5\">\r\n {progress.map((item, index) => (\r\n <div\r\n key={`${item.name}-${index}`}\r\n className={cn(\r\n 'flex flex-col gap-1.5 rounded-lg border border-dashed p-1.5',\r\n item.error != null && 'border-destructive/50 bg-destructive/5',\r\n )}\r\n >\r\n <Skeleton className=\"aspect-video w-full rounded-md\" />\r\n <p className=\"truncate text-[10px] text-muted-foreground\" title={item.name}>\r\n {item.name}\r\n </p>\r\n {item.error != null ? (\r\n <p className=\"line-clamp-2 text-[10px] text-destructive\">{item.error}</p>\r\n ) : (\r\n <div className=\"h-0.5 w-full overflow-hidden rounded-full bg-muted\">\r\n <div\r\n className=\"h-full rounded-full bg-primary transition-all\"\r\n style={{ width: `${Math.round(item.progress * 100)}%` }}\r\n />\r\n </div>\r\n )}\r\n </div>\r\n ))}\r\n\r\n {assets.map((asset) => (\r\n <MediaCard\r\n key={asset.id}\r\n asset={asset}\r\n selected={selectedAssetId === asset.id}\r\n missing={missingAssetIds.includes(asset.id)}\r\n onSelect={() => onSelect(asset.id)}\r\n onAddToTimeline={() => onAddToTimeline(asset.id)}\r\n onShowMeta={() => onShowMeta(asset.id)}\r\n onRemove={() => onRemove(asset.id)}\r\n />\r\n ))}\r\n\r\n {/* 末尾追加一个快捷导入格,方便连续导入 */}\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.multiple = true\r\n input.accept = '.mp4,.m4v,video/mp4,audio/*,image/*,.srt'\r\n input.onchange = () => {\r\n if (input.files) onDropFiles(Array.from(input.files))\r\n }\r\n input.click()\r\n }}\r\n className=\"flex aspect-video flex-col items-center justify-center gap-1 rounded-lg border border-dashed text-muted-foreground transition-colors hover:border-primary/50 hover:text-foreground\"\r\n aria-label=\"继续导入\"\r\n >\r\n <Upload className=\"size-4\" />\r\n <span className=\"text-[10px]\">导入更多</span>\r\n </button>\r\n </div>\r\n )\r\n}\r\n","import { useCallback, useState } from 'react'\r\nimport { toast } from 'sonner'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { useUi } from '../../../context'\r\nimport type { EditorState } from '@cut-workbench/core'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { importFile, UnsupportedFileError } from '@cut-workbench/core/engine'\r\n\r\nexport interface ImportProgressItem {\r\n name: string\r\n progress: number\r\n error?: string\r\n}\r\n\r\nexport interface UseMediaImportResult {\r\n importing: boolean\r\n progress: ImportProgressItem[]\r\n importFiles: (files: File[]) => Promise<MediaAsset[]>\r\n}\r\n\r\nconst ACCEPT = ['video/mp4', 'audio/*', 'image/*', '.srt', 'text/plain']\r\n\r\nexport { ACCEPT }\r\n\r\n/**\r\n * 找出可以「重连」的素材。\r\n *\r\n * 场景:刷新后工程从 IndexedDB 恢复,但素材二进制消失(File 无法持久化)。\r\n * 用户重新导入同一个文件时,复用原 assetId,时间轴上已有片段立即恢复。\r\n * 匹配条件:同名 + 同类型 + 当前确实缺少二进制。\r\n */\r\nfunction findReconnectableAssetId(\r\n state: EditorState,\r\n file: File,\r\n registry: { getFile(id: string): File | null },\r\n): string | null {\r\n if (state.missingAssetIds.length === 0) return null\r\n const candidate = state.assets.find(\r\n (asset) =>\r\n state.missingAssetIds.includes(asset.id) &&\r\n asset.name === file.name &&\r\n registry.getFile(asset.id) == null,\r\n )\r\n return candidate?.id ?? null\r\n}\r\n\r\n/**\r\n * 批量导入素材。\r\n *\r\n * - 串行处理,避免同时开辟多个解码器把内存打爆;\r\n * - 单个文件失败只影响自身,最后汇总 toast;\r\n * - 进度写在本地 state(不放进 store,避免高频更新引起全局重渲染);\r\n * - 若与「缺失源文件」的素材同名同类型,则重连而不是新增。\r\n */\r\nexport function useMediaImport(): UseMediaImportResult {\r\n const editor = useCutEditor()\r\n const clips = editor.runtime.clips\r\n const store = editor.store\r\n const [progress, setProgress] = useState<ImportProgressItem[]>([])\r\n const [importing, setImporting] = useState(false)\r\n const addAsset = useEditor((s) => s.addAsset)\r\n const replaceAsset = useEditor((s) => s.replaceAsset)\r\n const clearMissing = useEditor((s) => s.clearMissing)\r\n const selectAsset = useEditor((s) => s.selectAsset)\r\n const beginImport = useUi((s) => s.beginImport)\r\n const endImport = useUi((s) => s.endImport)\r\n\r\n const importFiles = useCallback(\r\n async (files: File[]): Promise<MediaAsset[]> => {\r\n if (files.length === 0) return []\r\n setImporting(true)\r\n beginImport()\r\n setProgress(files.map((f) => ({ name: f.name, progress: 0 })))\r\n\r\n const imported: MediaAsset[] = []\r\n const failures: string[] = []\r\n let reconnected = 0\r\n\r\n for (let i = 0; i < files.length; i += 1) {\r\n const file = files[i]\r\n if (!file) continue\r\n try {\r\n // 刷新恢复的工程会留下「有元数据、无二进制」的素材。\r\n // 这里按文件名 + 类型尝试重连:复用原 assetId,时间轴上的片段立即恢复可用。\r\n const reconnectId = findReconnectableAssetId(store.getState(), file, clips)\r\n const result = await importFile(clips, file, (p) => {\r\n setProgress((prev) =>\r\n prev.map((item, index) => (index === i ? { ...item, progress: p } : item)),\r\n )\r\n })\r\n\r\n if (reconnectId != null) {\r\n // 先把二进制换回原 id(注册表内部会销毁旧母版与派生实例),\r\n // 再更新 store 里的元数据,并解除缺失标记。\r\n clips.replaceAssetSource(reconnectId, file, result.master ?? null)\r\n replaceAsset(reconnectId, result.asset)\r\n clearMissing(reconnectId)\r\n imported.push({ ...result.asset, id: reconnectId })\r\n reconnected += 1\r\n } else {\r\n addAsset(result.asset)\r\n imported.push(result.asset)\r\n }\r\n } catch (err) {\r\n const message =\r\n err instanceof UnsupportedFileError\r\n ? err.message\r\n : err instanceof Error\r\n ? err.message\r\n : String(err)\r\n failures.push(`${file.name}:${message}`)\r\n setProgress((prev) =>\r\n prev.map((item, index) => (index === i ? { ...item, error: message } : item)),\r\n )\r\n }\r\n }\r\n\r\n setImporting(false)\r\n endImport()\r\n\r\n if (imported.length > 0) {\r\n selectAsset(imported[0]?.id ?? null)\r\n toast.success(\r\n reconnected > 0\r\n ? `已导入 ${imported.length} 个素材(其中 ${reconnected} 个重连到已有片段)`\r\n : `已导入 ${imported.length} 个素材`,\r\n )\r\n }\r\n if (failures.length > 0) {\r\n toast.error(`${failures.length} 个文件导入失败`, {\r\n description: failures.slice(0, 3).join('\\n'),\r\n duration: 8000,\r\n })\r\n }\r\n\r\n window.setTimeout(() => setProgress([]), 3000)\r\n return imported\r\n },\r\n [addAsset, beginImport, clearMissing, endImport, replaceAsset, selectAsset],\r\n )\r\n\r\n return { importing, progress, importFiles }\r\n}\r\n","import { useState } from 'react'\r\nimport { Film, Plus, Subtitles, Type as TypeIcon } from 'lucide-react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { toast } from 'sonner'\r\nimport { parseSrtDurationUs } from '@cut-workbench/core'\r\nimport { createSubtitleAsset, createTextAsset } from '@cut-workbench/core/engine'\r\nimport { Button } from '@/components/ui/button'\r\nimport { Textarea } from '@/components/ui/textarea'\r\nimport { Label } from '@/components/ui/label'\r\nimport { Separator } from '@/components/ui/separator'\r\nimport { ScrollArea } from '@/components/ui/scroll-area'\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogDescription,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n} from '@/components/ui/dialog'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuLabel,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { formatBytes, formatDuration, formatResolution } from '@/lib/format'\r\nimport { DropZone } from './DropZone'\r\nimport { MediaGrid } from './MediaGrid'\r\nimport { useMediaImport } from '../hooks/useMediaImport'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\n\r\ninterface MediaLibraryPanelProps {\r\n /** 双击 / 点击「+」时由外层把素材添加到时间轴 */\r\n onAddToTimeline: (assetId: string) => void\r\n onRemoveAsset: (assetId: string) => void\r\n}\r\n\r\nconst KIND_LABEL: Record<MediaAsset['kind'], string> = {\r\n video: '视频',\r\n audio: '音频',\r\n image: '图片',\r\n text: '文字',\r\n subtitle: '字幕',\r\n}\r\n\r\n/**\r\n * 素材库面板:导入区 + 新建(文字/字幕)+ 网格 + 详情。\r\n */\r\nexport function MediaLibraryPanel({ onAddToTimeline, onRemoveAsset }: MediaLibraryPanelProps) {\r\n const assets = useEditor((s) => s.assets)\r\n const selectedAssetId = useEditor((s) => s.selectedAssetId)\r\n const missingAssetIds = useEditor((s) => s.missingAssetIds)\r\n const selectAsset = useEditor((s) => s.selectAsset)\r\n const { importing, progress, importFiles } = useMediaImport()\r\n const [metaAssetId, setMetaAssetId] = useState<string | null>(null)\r\n\r\n const metaAsset = assets.find((a) => a.id === metaAssetId) ?? null\r\n\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b px-3 py-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <h2 className=\"text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\r\n 素材库\r\n </h2>\r\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground tabular-nums\">\r\n {assets.length}\r\n </span>\r\n </div>\r\n <NewAssetMenu onCreated={selectAsset} />\r\n </div>\r\n\r\n <div className=\"shrink-0 px-2.5 pt-2.5\">\r\n <DropZone onFiles={(files) => void importFiles(files)} disabled={importing} />\r\n </div>\r\n\r\n <ScrollArea className=\"thin-scrollbar min-h-0 flex-1\">\r\n {assets.length === 0 && progress.length === 0 ? (\r\n <EmptyState\r\n icon={Film}\r\n title=\"素材库是空的\"\r\n description=\"拖入 MP4 / 音频 / 图片 / SRT,或用右上角「新建」创建文字与字幕\"\r\n className=\"pt-10\"\r\n />\r\n ) : (\r\n <MediaGrid\r\n assets={assets}\r\n selectedAssetId={selectedAssetId}\r\n missingAssetIds={missingAssetIds}\r\n progress={progress}\r\n onSelect={selectAsset}\r\n onAddToTimeline={onAddToTimeline}\r\n onShowMeta={setMetaAssetId}\r\n onRemove={onRemoveAsset}\r\n onDropFiles={(files) => void importFiles(files)}\r\n />\r\n )}\r\n </ScrollArea>\r\n\r\n <Dialog open={metaAsset != null} onOpenChange={(open) => !open && setMetaAssetId(null)}>\r\n {metaAsset != null && (\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle className=\"truncate\">{metaAsset.name}</DialogTitle>\r\n <DialogDescription>素材详细信息</DialogDescription>\r\n </DialogHeader>\r\n <Separator />\r\n <dl className=\"grid grid-cols-2 gap-y-2 text-sm\">\r\n <Row label=\"类型\" value={KIND_LABEL[metaAsset.kind]} />\r\n <Row label=\"MIME\" value={metaAsset.mimeType} />\r\n <Row label=\"时长\" value={formatDuration(metaAsset.durationUs)} />\r\n <Row label=\"分辨率\" value={formatResolution(metaAsset.width, metaAsset.height)} />\r\n <Row\r\n label=\"大小\"\r\n value={metaAsset.kind === 'text' ? '—' : formatBytes(metaAsset.size)}\r\n />\r\n <Row label=\"音轨\" value={metaAsset.hasAudio ? '有(48kHz 双声道)' : '无'} />\r\n <Row label=\"素材 ID\" value={metaAsset.id} mono />\r\n </dl>\r\n <DialogFooter>\r\n <Button variant=\"outline\" size=\"sm\" onClick={() => setMetaAssetId(null)}>\r\n 关闭\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n )}\r\n </Dialog>\r\n </div>\r\n )\r\n}\r\n\r\nfunction Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) {\r\n return (\r\n <>\r\n <dt className=\"text-muted-foreground\">{label}</dt>\r\n <dd className={mono ? 'truncate font-mono text-xs' : 'truncate'}>{value}</dd>\r\n </>\r\n )\r\n}\r\n\r\nfunction NewAssetMenu({ onCreated }: { onCreated: (assetId: string) => void }) {\r\n const [textOpen, setTextOpen] = useState(false)\r\n const [srtOpen, setSrtOpen] = useState(false)\r\n\r\n return (\r\n <>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"outline\" size=\"xs\">\r\n <Plus className=\"size-3\" />\r\n 新建\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\">\r\n <DropdownMenuLabel>非文件素材</DropdownMenuLabel>\r\n <DropdownMenuItem onSelect={() => setTextOpen(true)}>\r\n <TypeIcon className=\"size-3.5\" />\r\n 文字标题\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onSelect={() => setSrtOpen(true)}>\r\n <Subtitles className=\"size-3.5\" />\r\n 粘贴 SRT 字幕\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n\r\n <CreateTextDialog open={textOpen} onOpenChange={setTextOpen} onCreated={onCreated} />\r\n <CreateSubtitleDialog open={srtOpen} onOpenChange={setSrtOpen} onCreated={onCreated} />\r\n </>\r\n )\r\n}\r\n\r\nfunction CreateTextDialog({\r\n open,\r\n onOpenChange,\r\n onCreated,\r\n}: {\r\n open: boolean\r\n onOpenChange: (open: boolean) => void\r\n onCreated: (assetId: string) => void\r\n}) {\r\n const editor = useCutEditor()\r\n const [text, setText] = useState('双击编辑文字')\r\n const projectWidth = useEditor((s) => s.project.width)\r\n const projectHeight = useEditor((s) => s.project.height)\r\n const addAsset = useEditor((s) => s.addAsset)\r\n\r\n const handleCreate = () => {\r\n const { asset } = createTextAsset({\r\n registry: editor.runtime.clips,\r\n text,\r\n width: projectWidth,\r\n height: projectHeight,\r\n })\r\n addAsset(asset)\r\n onCreated(asset.id)\r\n onOpenChange(false)\r\n toast.success('已创建文字素材')\r\n }\r\n\r\n return (\r\n <Dialog open={open} onOpenChange={onOpenChange}>\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle>新建文字素材</DialogTitle>\r\n <DialogDescription>\r\n 通过 renderTxt2ImgBitmap 渲染为位图,可叠加在任意视频上。\r\n </DialogDescription>\r\n </DialogHeader>\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"text-content\">文字内容</Label>\r\n <Textarea\r\n id=\"text-content\"\r\n value={text}\r\n onChange={(e) => setText(e.target.value)}\r\n rows={3}\r\n />\r\n </div>\r\n <DialogFooter>\r\n <Button onClick={handleCreate} disabled={text.trim().length === 0} size=\"sm\">\r\n 创建\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n )\r\n}\r\n\r\nfunction CreateSubtitleDialog({\r\n open,\r\n onOpenChange,\r\n onCreated,\r\n}: {\r\n open: boolean\r\n onOpenChange: (open: boolean) => void\r\n onCreated: (assetId: string) => void\r\n}) {\r\n const editor = useCutEditor()\r\n const [srt, setSrt] = useState(\r\n '1\\n00:00:00,000 --> 00:00:02,000\\n示例字幕第一句\\n\\n2\\n00:00:02,000 --> 00:00:04,000\\n示例字幕第二句\\n',\r\n )\r\n const projectWidth = useEditor((s) => s.project.width)\r\n const projectHeight = useEditor((s) => s.project.height)\r\n const addAsset = useEditor((s) => s.addAsset)\r\n\r\n const handleCreate = () => {\r\n const durationUs = parseSrtDurationUs(srt)\r\n if (durationUs <= 0) {\r\n toast.error('SRT 解析失败', { description: '请检查时间轴格式:00:00:00,000 --> 00:00:02,000' })\r\n return\r\n }\r\n const { asset } = createSubtitleAsset({\r\n registry: editor.runtime.clips,\r\n srt,\r\n width: projectWidth,\r\n height: projectHeight,\r\n durationUs,\r\n })\r\n addAsset(asset)\r\n onCreated(asset.id)\r\n onOpenChange(false)\r\n toast.success('已创建字幕素材')\r\n }\r\n\r\n return (\r\n <Dialog open={open} onOpenChange={onOpenChange}>\r\n <DialogContent className=\"max-w-xl\">\r\n <DialogHeader>\r\n <DialogTitle>粘贴 SRT 字幕</DialogTitle>\r\n <DialogDescription>\r\n 字幕会作为独立片段烧录进画面(EmbedSubtitlesClip,仅支持 SRT)。\r\n </DialogDescription>\r\n </DialogHeader>\r\n <Textarea\r\n value={srt}\r\n onChange={(e) => setSrt(e.target.value)}\r\n rows={9}\r\n className=\"font-mono text-xs\"\r\n />\r\n <DialogFooter>\r\n <Button onClick={handleCreate} disabled={srt.trim().length === 0} size=\"sm\">\r\n 创建字幕素材\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n )\r\n}\r\n","import { useCallback } from 'react'\nimport { toast } from 'sonner'\n\n/**\n * 快捷操作:把编辑相关动作 + 错误提示集中在一处,供工具栏与快捷键共用。\n */\nexport interface EditorActions {\n undo: () => void\n redo: () => void\n split: () => void\n removeSelected: () => void\n duplicateSelected: () => void\n selectAll: () => void\n stepFrame: (direction: -1 | 1) => void\n jumpToStart: () => void\n jumpToEnd: () => void\n}\n\n/** 统一把异常转成可见提示,禁止静默吞掉 */\nexport function reportError(error: unknown, fallback = '操作失败'): void {\n const message = error instanceof Error ? error.message : String(error)\n console.error(error)\n toast.error(fallback, { description: message })\n}\n\nexport function useToastSafe(): {\n success: (message: string, description?: string) => void\n error: (message: string, description?: string) => void\n info: (message: string, description?: string) => void\n} {\n const success = useCallback((message: string, description?: string) => {\n toast.success(message, description ? { description } : undefined)\n }, [])\n const error = useCallback((message: string, description?: string) => {\n toast.error(message, description ? { description } : undefined)\n }, [])\n const info = useCallback((message: string, description?: string) => {\n toast.info(message, description ? { description } : undefined)\n }, [])\n return { success, error, info }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport type { ClipTransform, MediaAsset, Project } from '@cut-workbench/core'\r\nimport { PreviewCanvas, type SyncReport } from '@cut-workbench/core/engine'\r\nimport { reportError } from '@/hooks/use-toast-safe'\r\n\r\nexport interface UseAvCanvasOptions {\r\n width: number\r\n height: number\r\n bgColor?: string\r\n}\r\n\r\nexport interface UseAvCanvasResult {\r\n /** 用 callback ref 挂载画布容器 */\r\n attachRef: (node: HTMLDivElement | null) => void\r\n containerRef: React.RefObject<HTMLDivElement | null>\r\n ready: boolean\r\n sync: (project: Project, assets: MediaAsset[]) => Promise<SyncReport>\r\n /** 只写 sprite 属性(位置/尺寸/旋转/透明度/翻转),不重建 IClip */\r\n applyProps: (project: Project) => void\r\n /** 拖动中的实时几何:只写 sprite + 重绘,不写 store */\r\n previewTransform: (clipId: string, transform: ClipTransform) => Promise<void>\r\n previewFrame: (us: number) => Promise<void>\r\n refresh: (us: number) => Promise<void>\r\n captureImage: () => string | null\r\n /** 命中测试(工程坐标 + 当前时间,只命中此刻可见的片段) */\r\n hitTest: (point: { x: number; y: number }, project: Project, atUs: number) => string | null\r\n /** sprite 上的实时几何(拖动起点) */\r\n getSpriteTransform: (clipId: string) => ClipTransform | null\r\n captureStream: () => MediaStream | null\r\n getCanvas: () => PreviewCanvas | null\r\n /** 画布 CSS 尺寸 / 工程分辨率 */\r\n getCssScale: () => { x: number; y: number }\r\n}\r\n\r\n/**\r\n * 预览画布的 React 适配层。\r\n *\r\n * 用 callback ref 创建/销毁 `PreviewCanvas`:AVCanvas 的构造函数会直接操作 DOM,\r\n * 放在 effect 里再 setState 会触发「effect 内同步 setState」的反模式,\r\n * callback ref 则天然在提交阶段执行,语义更准确。\r\n *\r\n * ⚠️ 播放不通过 AVCanvas.play(),由 usePlaybackClock + previewFrame 驱动。\r\n * ⚠️ 工程分辨率变化时需 remount(调用方应把 width/height 放进 key)。\r\n * ⚠️ 本层只管画布;选中框与画布内交互在 feature 层(SelectionOverlay /\r\n * useCanvasInteraction),因为那是纯 UI 逻辑,不该下沉到 core。\r\n */\r\nexport function useAvCanvas({\r\n width,\r\n height,\r\n bgColor,\r\n}: UseAvCanvasOptions): UseAvCanvasResult {\r\n const editor = useCutEditor()\r\n const containerRef = useRef<HTMLDivElement | null>(null)\r\n const canvasRef = useRef<PreviewCanvas | null>(null)\r\n const [ready, setReady] = useState(false)\r\n\r\n const attachRef = useCallback(\r\n (node: HTMLDivElement | null) => {\r\n // 先销毁旧实例(node 变化或卸载)\r\n canvasRef.current?.destroy()\r\n canvasRef.current = null\r\n containerRef.current = node\r\n\r\n if (node == null) {\r\n setReady(false)\r\n return\r\n }\r\n\r\n try {\r\n canvasRef.current = new PreviewCanvas(node, { width, height, bgColor, registry: editor.runtime.clips })\r\n setReady(true)\r\n } catch (err) {\r\n setReady(false)\r\n reportError(err, '画布初始化失败')\r\n }\r\n },\r\n [bgColor, height, width],\r\n )\r\n\r\n // 卸载兜底(callback ref 的 cleanup 在 React 19 里不会自动执行,需显式处理)\r\n useEffect(() => {\r\n return () => {\r\n canvasRef.current?.destroy()\r\n canvasRef.current = null\r\n }\r\n }, [])\r\n\r\n const sync = useCallback(async (project: Project, assets: MediaAsset[]) => {\r\n const canvas = canvasRef.current\r\n if (canvas == null) return { created: [], removed: [], errors: [] }\r\n return canvas.sync(project, assets)\r\n }, [])\r\n\r\n const previewFrame = useCallback(async (us: number) => {\r\n await canvasRef.current?.previewFrame(us)\r\n }, [])\r\n\r\n const applyProps = useCallback((project: Project) => {\r\n canvasRef.current?.applyProps(project)\r\n }, [])\r\n\r\n const previewTransform = useCallback((clipId: string, transform: ClipTransform) => {\r\n return canvasRef.current?.previewTransform(clipId, transform) ?? Promise.resolve()\r\n }, [])\r\n\r\n const refresh = useCallback(async (us: number) => {\r\n await canvasRef.current?.refresh(us)\r\n }, [])\r\n\r\n const captureImage = useCallback(() => canvasRef.current?.captureImage() ?? null, [])\r\n\r\n const hitTest = useCallback(\r\n (point: { x: number; y: number }, project: Project, atUs: number) => {\r\n return canvasRef.current?.hitTest(point, project, atUs) ?? null\r\n },\r\n [],\r\n )\r\n\r\n const getSpriteTransform = useCallback((clipId: string) => {\r\n return canvasRef.current?.getSpriteTransform(clipId) ?? null\r\n }, [])\r\n\r\n const captureStream = useCallback(() => canvasRef.current?.captureStream() ?? null, [])\r\n\r\n const getCanvas = useCallback(() => canvasRef.current, [])\r\n\r\n const getCssScale = useCallback(\r\n () => canvasRef.current?.getCssScale() ?? { x: 1, y: 1 },\r\n [],\r\n )\r\n\r\n return {\r\n attachRef,\r\n containerRef,\r\n ready,\r\n sync,\r\n applyProps,\r\n previewTransform,\r\n previewFrame,\r\n refresh,\r\n captureImage,\r\n hitTest,\r\n getSpriteTransform,\r\n captureStream,\r\n getCanvas,\r\n getCssScale,\r\n }\r\n}\r\n","import { useEffect, useRef } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport type { MediaAsset, Project } from '@cut-workbench/core'\r\nimport type { UseAvCanvasResult } from './useAvCanvas'\r\n\r\ninterface UsePreviewSyncOptions {\r\n project: Project\r\n assets: MediaAsset[]\r\n canvas: UseAvCanvasResult\r\n /** 强制重绘当前帧(属性变化后调用) */\r\n refresh: (us: number) => Promise<void>\r\n onSyncErrors: (errors: Array<{ clipId: string; message: string }>) => void\r\n}\r\n\r\n/**\r\n * 结构指纹:只包含「会导致 IClip 重建」的字段。\r\n *\r\n * 位置 / 透明度 / 文字样式等纯属性不在内 —— 它们只需要把值写回 sprite,\r\n * 不需要重建 IClip(重建会重新解码,代价高且会闪)。\r\n */\r\nfunction buildStructureKey(project: Project): string {\r\n const tracks = project.tracks.map((t) => `${t.id}:${t.hidden ? 1 : 0}`).join(',')\r\n const clips = project.clips\r\n .map(\r\n (c) =>\r\n `${c.id}:${c.trackId}:${c.assetId}:${c.inPointUs}:${c.durationUs}:${c.startUs}:${c.playbackRate}`,\r\n )\r\n .join(';')\r\n return `${project.width}x${project.height}#${tracks}#${clips}`\r\n}\r\n\r\n/**\r\n * 属性指纹:不触发重建、但必须同步到 sprite 的字段。\r\n *\r\n * ⚠️ 为什么必须单独有这一层:AVCanvas 的内部 33ms 循环只会「重绘」sprite,\r\n * 不会把 store 的数值写进 sprite.rect / opacity / flip。如果只靠 refresh(),\r\n * 属性面板改 X / 宽 / 旋转后 store 变了但画面纹丝不动 —— 实测确认的 bug。\r\n * 因此属性变化时必须走一遍 `canvas.applyProps()`(只写属性,不重建)。\r\n */\r\nfunction buildPropsKey(project: Project): string {\r\n return project.clips\r\n .map(\r\n (c) =>\r\n `${c.id}:${c.transform.x},${c.transform.y},${c.transform.w},${c.transform.h},${c.transform.angle}:${c.opacity}:${c.flip ?? '-'}`,\r\n )\r\n .join(';')\r\n}\r\n\r\n/**\r\n * 把 store 的时间轴数据同步到 AVCanvas。\r\n *\r\n * 三档处理(按代价从低到高):\r\n * 1. 结构变化 → 全量 `sync`(新建/销毁 IClip 与 sprite);\r\n * 2. 属性变化 → `applyProps`(只写 sprite 属性,然后重绘当前帧);\r\n * 3. 都没变 → 仅重绘当前帧。\r\n *\r\n * ⚠️ sync 必须串行且可取消:快速连续的结构变化(拖拽中持续 split)并发执行会重复 addSprite。\r\n * 播放头从 store 直接读取(getState),避免把它放进依赖导致每帧重跑同步。\r\n */\r\nexport function usePreviewSync({\r\n project,\r\n assets,\r\n canvas,\r\n refresh,\r\n onSyncErrors,\r\n}: UsePreviewSyncOptions): void {\r\n const editor = useCutEditor()\r\n const lastKeyRef = useRef<string>('')\r\n const lastPropsKeyRef = useRef<string>('')\r\n const runIdRef = useRef(0)\r\n const onSyncErrorsRef = useRef(onSyncErrors)\r\n\r\n useEffect(() => {\r\n onSyncErrorsRef.current = onSyncErrors\r\n })\r\n\r\n useEffect(() => {\r\n if (!canvas.ready) {\r\n lastKeyRef.current = ''\r\n lastPropsKeyRef.current = ''\r\n return\r\n }\r\n const playhead = editor.store.getState().playheadUs\r\n const key = buildStructureKey(project)\r\n const propsKey = buildPropsKey(project)\r\n\r\n if (key === lastKeyRef.current) {\r\n if (propsKey !== lastPropsKeyRef.current) {\r\n // 只改了位置/尺寸/旋转/透明度/翻转:写回 sprite 再重绘\r\n canvas.applyProps(project)\r\n lastPropsKeyRef.current = propsKey\r\n }\r\n void refresh(playhead)\r\n return\r\n }\r\n\r\n const runId = ++runIdRef.current\r\n void (async () => {\r\n try {\r\n const report = await canvas.sync(project, assets)\r\n if (runId !== runIdRef.current) return\r\n if (report.errors.length > 0) onSyncErrorsRef.current(report.errors)\r\n lastKeyRef.current = key\r\n lastPropsKeyRef.current = propsKey\r\n await refresh(editor.store.getState().playheadUs)\r\n } catch (err) {\r\n if (runId !== runIdRef.current) return\r\n onSyncErrorsRef.current([\r\n { clipId: '', message: err instanceof Error ? err.message : String(err) },\r\n ])\r\n }\r\n })()\r\n }, [assets, canvas, project, refresh])\r\n}\r\n","import { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { MediaAsset, Project } from '@cut-workbench/core'\r\nimport { loadAudioClipForAsset } from '@cut-workbench/core/engine'\r\nimport { getProjectDuration } from '@cut-workbench/core'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\n\r\nexport interface UsePlaybackClockOptions {\r\n project: Project\r\n assets: MediaAsset[]\r\n /** 每帧渲染回调(微秒) */\r\n onFrame: (us: number) => void\r\n /** 播放到达末尾时回调 */\r\n onEnded?: () => void\r\n masterVolume: number\r\n}\r\n\r\nexport interface UsePlaybackClockResult {\r\n playing: boolean\r\n currentUs: number\r\n play: () => void\r\n pause: () => void\r\n toggle: () => void\r\n seek: (us: number) => void\r\n setPlaybackRate: (rate: number) => void\r\n playbackRate: number\r\n durationUs: number\r\n /** 拖动播放头时暂停音频(不改变播放状态) */\r\n beginScrub: () => void\r\n endScrub: () => void\r\n}\r\n\r\n/**\r\n * 主时钟驱动的播放循环。\r\n *\r\n * 时间来源:`AudioContext.currentTime`(core/clock/playback-clock)。\r\n * rAF 只负责「读取当前时间 → 渲染一帧」,不做任何时间累加,因此长时间播放不会漂移。\r\n *\r\n * 音频:由 `previewAudioEngine` 直接把 PCM 排进同一个 AudioContext,与应用时钟同源。\r\n *\r\n * ⚠️ 拖动播放头时若仍在播放,音频会断续(WebAV tick 的 3s 重置语义),\r\n * 因此 `beginScrub` 会先暂停音频排程,松手后再重新排程。\r\n */\r\nexport function usePlaybackClock({\r\n project,\r\n assets,\r\n onFrame,\r\n onEnded,\r\n masterVolume,\r\n}: UsePlaybackClockOptions): UsePlaybackClockResult {\r\n const editor = useCutEditor()\r\n const [playing, setPlaying] = useState(false)\r\n const [currentUs, setCurrentUs] = useState(0)\r\n const [playbackRate, setPlaybackRateState] = useState(1)\r\n\r\n const rafRef = useRef<number | null>(null)\r\n const playingRef = useRef(false)\r\n const onFrameRef = useRef(onFrame)\r\n const onEndedRef = useRef(onEnded)\r\n const projectRef = useRef(project)\r\n const assetsRef = useRef(assets)\r\n const rateRef = useRef(1)\r\n\r\n // 所有「最新值」的同步统一放在 effect 里,不在渲染期写 ref\r\n useEffect(() => {\r\n onFrameRef.current = onFrame\r\n onEndedRef.current = onEnded\r\n projectRef.current = project\r\n assetsRef.current = assets\r\n })\r\n\r\n const durationUs = getProjectDuration(project)\r\n\r\n useEffect(() => {\r\n editor.runtime.audio.setMasterVolume(masterVolume)\r\n }, [masterVolume])\r\n\r\n // 为含音轨的素材注册 PCM 解码(懒加载,失败静默 —— 只影响预览音质)\r\n useEffect(() => {\r\n const { clock, audio, clips } = editor.runtime\r\n const ctx = clock.context\r\n if (ctx != null) audio.attach(ctx)\r\n for (const asset of assets) {\r\n if (asset.kind !== 'audio' && asset.kind !== 'video') continue\r\n if (!asset.hasAudio) continue\r\n audio.registerAsset(asset.id, async () => {\r\n const clip = await loadAudioClipForAsset(clips, asset.id)\r\n if (clip == null) throw new Error(`无法加载音频:${asset.name}`)\r\n return clip\r\n })\r\n }\r\n }, [assets])\r\n\r\n // 用 ref 持有循环体,避免 useCallback 自引用(React Compiler 不允许变量在声明前被引用)\r\n // 用 ref 持有循环体,避免 useCallback 自引用(React Compiler 不允许变量在声明前被引用)。\r\n // 赋值放在 effect 中,保持渲染纯函数。\r\n const loopRef = useRef<() => void>(() => undefined)\r\n useEffect(() => {\r\n loopRef.current = () => {\r\n if (!playingRef.current) return\r\n const { clock, audio } = editor.runtime\r\n const us = clock.currentUs\r\n const total = getProjectDuration(projectRef.current)\r\n setCurrentUs(us)\r\n onFrameRef.current(us)\r\n if (total > 0 && us >= total) {\r\n playingRef.current = false\r\n clock.pause()\r\n audio.stopAll()\r\n setPlaying(false)\r\n setCurrentUs(total)\r\n onEndedRef.current?.()\r\n return\r\n }\r\n rafRef.current = requestAnimationFrame(() => loopRef.current())\r\n }\r\n })\r\n\r\n const startAudio = useCallback((fromUs: number) => {\r\n const state = projectRef.current\r\n const assetMap = new Map(assetsRef.current.map((a) => [a.id, a]))\r\n const trackMap = new Map(state.tracks.map((t) => [t.id, t]))\r\n const items = state.clips.flatMap((clip) => {\r\n const track = trackMap.get(clip.trackId)\r\n const asset = assetMap.get(clip.assetId)\r\n if (!track || !asset) return []\r\n return [{ clip, track, assetKind: asset.kind }]\r\n })\r\n void editor.runtime.audio.scheduleFrom(fromUs, items, getProjectDuration(state))\r\n }, [])\r\n\r\n const play = useCallback(() => {\r\n if (playingRef.current) return\r\n const { clock, audio } = editor.runtime\r\n const ctx = clock.ensureContext()\r\n audio.attach(ctx)\r\n let from = clock.positionUs\r\n const total = getProjectDuration(projectRef.current)\r\n if (total > 0 && from >= total) from = 0\r\n clock.play(from, rateRef.current)\r\n startAudio(from)\r\n playingRef.current = true\r\n setPlaying(true)\r\n setCurrentUs(from)\r\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\r\n rafRef.current = requestAnimationFrame(() => loopRef.current())\r\n }, [startAudio])\r\n\r\n const pause = useCallback(() => {\r\n if (!playingRef.current) return\r\n playingRef.current = false\r\n if (rafRef.current != null) {\r\n cancelAnimationFrame(rafRef.current)\r\n rafRef.current = null\r\n }\r\n editor.runtime.clock.pause()\r\n editor.runtime.audio.stopAll()\r\n setPlaying(false)\r\n setCurrentUs(editor.runtime.clock.positionUs)\r\n }, [])\r\n\r\n const toggle = useCallback(() => {\r\n if (playingRef.current) pause()\r\n else play()\r\n }, [pause, play])\r\n\r\n const seek = useCallback(\r\n (us: number) => {\r\n const total = getProjectDuration(projectRef.current)\r\n const clamped = Math.max(0, total > 0 ? Math.min(us, total) : us)\r\n editor.runtime.clock.seek(clamped)\r\n setCurrentUs(clamped)\r\n onFrameRef.current(clamped)\r\n if (playingRef.current) {\r\n // 播放中跳转:重排音频,避免 tick 状态错乱\r\n startAudio(clamped)\r\n }\r\n },\r\n [startAudio],\r\n )\r\n\r\n const setPlaybackRate = useCallback((rate: number) => {\r\n const safe = rate > 0 ? rate : 1\r\n rateRef.current = safe\r\n editor.runtime.clock.setRate(safe)\r\n setPlaybackRateState(safe)\r\n }, [])\r\n\r\n const beginScrub = useCallback(() => {\r\n editor.runtime.audio.stopAll()\r\n }, [])\r\n\r\n const endScrub = useCallback(() => {\r\n if (playingRef.current) {\r\n startAudio(editor.runtime.clock.positionUs)\r\n }\r\n }, [startAudio])\r\n\r\n // 卸载时停止播放并释放音频\r\n useEffect(() => {\r\n return () => {\r\n playingRef.current = false\r\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\r\n editor.runtime.audio.stopAll()\r\n editor.runtime.clock.pause()\r\n }\r\n }, [])\r\n\r\n // 首帧渲染在画布就绪后由 PreviewPanel 负责;这里不主动触发\r\n\r\n return {\r\n playing,\r\n currentUs,\r\n play,\r\n pause,\r\n toggle,\r\n seek,\r\n setPlaybackRate,\r\n playbackRate,\r\n durationUs,\r\n beginScrub,\r\n endScrub,\r\n }\r\n}\r\n\r\n","/**\n * 画布内几何交互(选中 / 拖动 / 缩放 / 旋转)。\n *\n * 为什么自绘而不是用 AVCanvas 内建的选中框:\n * - 内建选框每次 `propsChange` 都读 `getBoundingClientRect()` 并写 style,\n * 同时我们的 store 回写又会触发 React 重渲染,拖动时主线程被两套更新挤爆\n * (实测 40 次 pointermove 会产生 6 个 ~100ms 的 long task);\n * - 内建选框的样式只能 `!important` 覆盖私有 DOM,改不动结构(比如想加辅助线、\n * 想按素材类型换颜色都做不到)。\n *\n * 现在的链路:\n * pointermove → 更新本地 ref + 直接写 sprite.rect(不写 store)\n * → DOM 选框只改 transform(浏览器合成层完成,不开重排)\n * pointerup → 一次性写回 store(一步撤销)\n *\n * 因此拖动期间 React 完全不重渲染,选框与画面按同一帧推进。\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { ClipTransform } from '@cut-workbench/core'\nimport type { PreviewCanvas } from '@cut-workbench/core/engine'\nimport {\n normalizeAngle,\n resizeTransform,\n sameTransform,\n type HandleKey,\n} from '@cut-workbench/core'\n\nexport type CanvasDragMode = 'move' | 'resize' | 'rotate'\n\nexport interface CanvasInteractionState {\n clipId: string\n mode: CanvasDragMode\n handle: HandleKey | null\n}\n\ninterface UseCanvasInteractionOptions {\n /** 当前画布实例;未就绪时返回 null */\n getCanvas: () => PreviewCanvas | null\n /** 工程分辨率 */\n projectWidth: number\n projectHeight: number\n /** 画布 DOM 容器(用于换算指针坐标与测量 CSS 尺寸) */\n hostRef: React.RefObject<HTMLDivElement | null>\n /** 当前选中的 clip(单选时才允许画布内变换) */\n selectedClipId: string | null\n /** 该 clip 在 store 里的几何 */\n transform: ClipTransform | null\n /**\n * 是否允许在画布内变换。\n *\n * 为 false 时手柄不响应(例如播放头已经离开该片段区间 —— 此时画面上根本看不到它,\n * 却在原位能被拖走,会让用户误以为「拖了块空白」)。\n */\n interactive: boolean\n /** 该片段是否锁定了宽高比(属性面板的锁) */\n aspectLocked?: boolean\n /** 命中测试所需:给定工程坐标返回 clip id */\n hitTest: (point: { x: number; y: number }) => string | null\n /** 点选画布内素材(不会触发 store 的历史) */\n onSelect: (clipId: string | null) => void\n /** 一次变换结束时提交(drag 期间不写 store) */\n onCommit: (clipId: string, transform: ClipTransform) => void\n}\n\nexport interface UseCanvasInteractionResult {\n /** 拖动中的实时几何(覆盖 store 值用于绘制选框) */\n liveTransform: ClipTransform | null\n interaction: CanvasInteractionState | null\n /** 传给选框容器的 pointerdown */\n onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void\n}\n\nexport function useCanvasInteraction({\n getCanvas,\n projectWidth,\n projectHeight,\n hostRef,\n selectedClipId,\n transform,\n interactive,\n aspectLocked = false,\n hitTest,\n onSelect,\n onCommit,\n}: UseCanvasInteractionOptions): UseCanvasInteractionResult {\n const [liveTransform, setLiveTransform] = useState<ClipTransform | null>(null)\n const [interaction, setInteraction] = useState<CanvasInteractionState | null>(null)\n\n const liveRef = useRef<ClipTransform | null>(null)\n const rafRef = useRef<number | null>(null)\n const envRef = useRef({\n getCanvas,\n projectWidth,\n projectHeight,\n hostRef,\n selectedClipId,\n transform,\n interactive,\n aspectLocked,\n hitTest,\n onSelect,\n onCommit,\n })\n\n useEffect(() => {\n envRef.current = {\n getCanvas,\n projectWidth,\n projectHeight,\n hostRef,\n selectedClipId,\n transform,\n interactive,\n aspectLocked,\n hitTest,\n onSelect,\n onCommit,\n }\n })\n\n /** 指针事件 → 工程坐标 */\n const toProjectPoint = useCallback((clientX: number, clientY: number) => {\n const host = envRef.current.hostRef.current\n if (host == null) return null\n const rect = host.getBoundingClientRect()\n const scaleX = rect.width > 0 ? rect.width / envRef.current.projectWidth : 1\n const scaleY = rect.height > 0 ? rect.height / envRef.current.projectHeight : 1\n return {\n x: (clientX - rect.left) / (scaleX || 1),\n y: (clientY - rect.top) / (scaleY || 1),\n }\n }, [])\n\n const flush = useCallback(() => {\n rafRef.current = null\n const next = liveRef.current\n const canvas = envRef.current.getCanvas()\n const id = envRef.current.selectedClipId\n if (next == null || canvas == null || id == null) return\n // 只写 sprite,不写 store:拖动期间 React 不参与\n void canvas.previewTransform(id, next)\n }, [])\n\n const scheduleFlush = useCallback(() => {\n if (rafRef.current != null) return\n rafRef.current = requestAnimationFrame(flush)\n }, [flush])\n\n const startDrag = useCallback(\n (\n mode: CanvasDragMode,\n handle: HandleKey | null,\n origin: ClipTransform,\n startPoint: { x: number; y: number },\n startAngleDeg: number,\n startPointerAngle: number,\n clipId: string,\n ) => {\n setInteraction({ clipId, mode, handle })\n const onMove = (event: PointerEvent) => {\n const point = toProjectPoint(event.clientX, event.clientY)\n if (point == null) return\n let next: ClipTransform\n if (mode === 'move') {\n next = {\n ...origin,\n x: origin.x + (point.x - startPoint.x),\n y: origin.y + (point.y - startPoint.y),\n }\n } else if (mode === 'rotate') {\n const center = { x: origin.x + origin.w / 2, y: origin.y + origin.h / 2 }\n const angle = (Math.atan2(point.y - center.y, point.x - center.x) * 180) / Math.PI\n next = {\n ...origin,\n angle: normalizeAngle(startAngleDeg + (angle - startPointerAngle)),\n }\n } else if (handle != null) {\n /*\n * Shift 临时等比(所有手柄);片段自身锁定宽高比时只有角手柄等比。\n * 边手柄若也等比,会变成「拖右边、上下也跟着长」,与用户的单维意图相悖。\n */\n const keepAspect = event.shiftKey\n ? true\n : envRef.current.aspectLocked\n ? ('corners' as const)\n : false\n next = resizeTransform(origin, handle, point, keepAspect)\n } else {\n return\n }\n liveRef.current = next\n setLiveTransform(next)\n scheduleFlush()\n }\n\n const onUp = () => {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', onUp)\n window.removeEventListener('pointercancel', onUp)\n if (rafRef.current != null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n const final = liveRef.current\n liveRef.current = null\n setLiveTransform(null)\n setInteraction(null)\n if (final != null && !sameTransform(final, origin)) {\n onCommit(clipId, final)\n }\n }\n\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', onUp)\n window.addEventListener('pointercancel', onUp)\n },\n [onCommit, scheduleFlush, toProjectPoint],\n )\n\n const onPointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n const target = event.target as HTMLElement\n const dataset = target.dataset\n const point = toProjectPoint(event.clientX, event.clientY)\n if (point == null) return\n\n // 手柄优先:先看是否按在已选中素材的手柄上\n const handleKey = dataset.handle as HandleKey | undefined\n const rotate = dataset.rotate != null\n const currentId = envRef.current.selectedClipId\n const currentTransform = envRef.current.transform\n\n if (\n currentId != null &&\n currentTransform != null &&\n envRef.current.interactive &&\n (handleKey != null || rotate)\n ) {\n event.preventDefault()\n event.stopPropagation()\n const center = {\n x: currentTransform.x + currentTransform.w / 2,\n y: currentTransform.y + currentTransform.h / 2,\n }\n const startPointerAngle =\n (Math.atan2(point.y - center.y, point.x - center.x) * 180) / Math.PI\n startDrag(\n rotate ? 'rotate' : 'resize',\n rotate ? null : handleKey!,\n currentTransform,\n point,\n currentTransform.angle,\n startPointerAngle,\n currentId,\n )\n return\n }\n\n // 命中素材:选中并开始拖动\n const hitId = envRef.current.hitTest(point)\n if (hitId == null) {\n envRef.current.onSelect(null)\n return\n }\n envRef.current.onSelect(hitId)\n\n /*\n * 命中后需要先拿到「被选中 clip 的几何」才能开始拖动。\n * 但 onSelect 会写 store,当前这一帧读不到新值 —— 因此用 hit 目标的\n * 实时几何作为起点(由调用方通过 getTransform 提供),失败则本轮不拖动。\n */\n const canvas = envRef.current.getCanvas()\n const targetTransform = canvas?.getSpriteTransform(hitId) ?? null\n if (targetTransform == null) return\n event.preventDefault()\n startDrag('move', null, targetTransform, point, targetTransform.angle, 0, hitId)\n },\n [startDrag, toProjectPoint],\n )\n\n // 卸载时清理未完成的动画帧\n useEffect(() => {\n return () => {\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\n }\n }, [])\n\n return { liveTransform, interaction, onPointerDown }\n}\n","import { Camera } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\n\ninterface CaptureButtonProps {\n onCapture: () => void\n disabled?: boolean\n}\n\n/**\n * 截图当前帧。\n *\n * 内部走 `AVCanvas.captureImage()`(返回 base64 data URL),由调用方负责下载。\n * 单独抽成组件是为了让预览工具条可以复用它(当前工具条直接内联在 PreviewStage,\n * 保留此导出供扩展)。\n */\nexport function CaptureButton({ onCapture, disabled }: CaptureButtonProps) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"secondary\"\n size=\"icon-sm\"\n onClick={onCapture}\n disabled={disabled}\n aria-label=\"截图当前帧\"\n >\n <Camera className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>截图当前帧</TooltipContent>\n </Tooltip>\n )\n}\n","import type { ClipTransform } from '@cut-workbench/core'\nimport { handleCursor, handleLocalOffset, RESIZE_HANDLES } from '@cut-workbench/core'\n\ninterface SelectionOverlayProps {\n /** 工程坐标系下的几何(拖动中为实时值) */\n transform: ClipTransform\n /** 工程分辨率 → CSS 像素的缩放 */\n scaleX: number\n scaleY: number\n /** 只读(音频等不可变换的 clip)时不画手柄 */\n interactive?: boolean\n}\n\n/**\n * 画布自绘选框。\n *\n * 布局:外层 div 用 `left/top/width/height + rotate` 定位,与素材完全重合;\n * 手柄与旋转柄用 `translate(-50%,-50%)` 贴在外层的边上。\n *\n * 性能:拖动时只更新这些元素的 style(transform/left/top),不做布局以外的\n * React 计算 —— 但因为我们在拖动期间不写 store,React 实际上也不重渲染。\n */\nexport function SelectionOverlay({\n transform,\n scaleX,\n scaleY,\n interactive = true,\n}: SelectionOverlayProps) {\n const { x, y, w, h, angle } = transform\n const left = x * scaleX\n const top = y * scaleY\n const width = Math.max(1, w * scaleX)\n const height = Math.max(1, h * scaleY)\n\n return (\n <div\n className=\"pointer-events-none absolute origin-center\"\n style={{\n left,\n top,\n width,\n height,\n transform: `rotate(${angle}deg)`,\n zIndex: 20,\n }}\n data-selection-box\n >\n <div className=\"absolute inset-0 border-2 border-primary shadow-[0_0_0_1px_rgba(0,0,0,0.55)]\" />\n\n {interactive && (\n <>\n {RESIZE_HANDLES.map((key) => {\n const offset = handleLocalOffset(key, width, height)\n return (\n <div\n key={key}\n data-handle={key}\n className=\"pointer-events-auto absolute size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background\"\n style={{\n left: width / 2 + offset.x,\n top: height / 2 + offset.y,\n cursor: handleCursor(key, angle),\n }}\n />\n )\n })}\n <div\n data-rotate=\"true\"\n className=\"pointer-events-auto absolute size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background\"\n style={{\n // 旋转柄放在上边中点的外侧(按缩放后固定 20px,避免素材很小时压住角柄)\n left: width / 2,\n top: -20,\n cursor: handleCursor('rotate', angle),\n }}\n />\n <div\n className=\"pointer-events-none absolute bg-primary\"\n style={{ left: width / 2 - 0.5, top: -20, width: 1, height: 20 }}\n />\n </>\n )}\n </div>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { Maximize2 } from 'lucide-react'\nimport type { ClipTransform } from '@cut-workbench/core'\nimport { Button } from '@/components/ui/button'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport { cn } from '@/lib/cn'\nimport { CaptureButton } from './CaptureButton'\nimport { SelectionOverlay } from './SelectionOverlay'\nimport type { CanvasInteractionState } from '../hooks/useCanvasInteraction'\n\ninterface PreviewStageProps {\n /** callback ref:由 useAvCanvas 负责挂载/销毁 AVCanvas 实例 */\n attachRef: (node: HTMLDivElement | null) => void\n width: number\n height: number\n emptyHint?: React.ReactNode\n onCapture: () => void\n onFullscreen: () => void\n /** 选中素材的几何(拖动中为实时值);null 表示不画选框 */\n selection: ClipTransform | null\n /** 是否显示可拖拽手柄(音频片段等不显示) */\n selectionInteractive: boolean\n interaction: CanvasInteractionState | null\n onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void\n className?: string\n}\n\n/**\n * 预览舞台。\n *\n * AVCanvas 依赖容器的 clientWidth/clientHeight 换算指针坐标,并给内部 canvas 设\n * `width:100%;height:100%`。因此这里用 ResizeObserver 测量可用空间后,\n * 计算一个精确的 CSS 像素尺寸(等比 fit),而不是依赖百分比。\n *\n * 选框叠加层与画布共用同一个缩放系数(`box.w / width`),保证「画出来的框」\n * 与「画面里的素材」严格重合。\n */\nexport function PreviewStage({\n attachRef,\n width,\n height,\n emptyHint,\n onCapture,\n onFullscreen,\n selection,\n selectionInteractive,\n interaction,\n onPointerDown,\n className,\n}: PreviewStageProps) {\n const outerRef = useRef<HTMLDivElement | null>(null)\n const [box, setBox] = useState({ w: 0, h: 0 })\n\n useEffect(() => {\n const el = outerRef.current\n if (el == null) return\n const padding = 32\n const observer = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect\n if (!rect) return\n const availableW = Math.max(0, rect.width - padding)\n const availableH = Math.max(0, rect.height - padding)\n if (availableW <= 0 || availableH <= 0) return\n const scale = Math.min(availableW / width, availableH / height)\n setBox({\n w: Math.max(1, Math.floor(width * scale)),\n h: Math.max(1, Math.floor(height * scale)),\n })\n })\n observer.observe(el)\n return () => observer.disconnect()\n }, [width, height])\n\n const scaleX = width > 0 ? box.w / width : 1\n const scaleY = height > 0 ? box.h / height : 1\n\n return (\n <div\n ref={outerRef}\n className={cn(\n 'relative flex min-h-0 flex-1 items-center justify-center overflow-hidden bg-stage-bg',\n className,\n )}\n >\n {/*\n * 内层是「画面本身」的容器:视频在导出时就是黑底合成,\n * 这里的 bg-black 属于内容底色而不是 UI 主题色,故意保留固定黑。\n * 外层的舞台底则走 --cw-stage-bg,跟随主题。\n */}\n <div\n className=\"relative shrink-0 overflow-hidden rounded-md bg-black shadow-2xl ring-1 ring-border\"\n style={{ width: box.w, height: box.h }}\n >\n <div\n ref={attachRef}\n className=\"av-canvas-host size-full\"\n style={{ width: '100%', height: '100%' }}\n />\n {/* 交互层:整块覆盖在画布上,负责点选与拖动;选框本身在它内部 */}\n <div\n className=\"absolute inset-0\"\n onPointerDown={onPointerDown}\n style={{ cursor: interaction?.mode === 'move' ? 'grabbing' : 'default' }}\n >\n {selection != null && (\n <SelectionOverlay\n transform={selection}\n scaleX={scaleX}\n scaleY={scaleY}\n interactive={selectionInteractive}\n />\n )}\n </div>\n {emptyHint != null && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center px-6\">\n {emptyHint}\n </div>\n )}\n </div>\n\n <div className=\"absolute right-3 bottom-3 flex items-center gap-1\">\n <CaptureButton onCapture={onCapture} />\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"secondary\" size=\"icon-sm\" onClick={onFullscreen} aria-label=\"全屏预览\">\n <Maximize2 className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>全屏预览</TooltipContent>\n </Tooltip>\n </div>\n </div>\n )\n}\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Slider as SliderPrimitive } from \"radix-ui\"\n\nfunction Slider({\n className,\n defaultValue,\n value,\n min = 0,\n max = 100,\n ...props\n}: React.ComponentProps<typeof SliderPrimitive.Root>) {\n const _values = React.useMemo(\n () =>\n Array.isArray(value)\n ? value\n : Array.isArray(defaultValue)\n ? defaultValue\n : [min, max],\n [value, defaultValue, min, max]\n )\n\n return (\n <SliderPrimitive.Root\n data-slot=\"slider\"\n defaultValue={defaultValue}\n value={value}\n min={min}\n max={max}\n className={cn(\n \"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col\",\n className\n )}\n {...props}\n >\n <SliderPrimitive.Track\n data-slot=\"slider-track\"\n className={cn(\n \"relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5\"\n )}\n >\n <SliderPrimitive.Range\n data-slot=\"slider-range\"\n className={cn(\n \"absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\"\n )}\n />\n </SliderPrimitive.Track>\n {Array.from({ length: _values.length }, (_, index) => (\n <SliderPrimitive.Thumb\n data-slot=\"slider-thumb\"\n key={index}\n // thumb 跟随主题:亮色下白芯深边、暗色下深芯浅边(shadcn 原版写死 bg-white,\n // 暗色主题里会出现一个刺眼的白点)\n className=\"block size-4 shrink-0 rounded-full border border-primary bg-background shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50\"\n />\n ))}\n </SliderPrimitive.Root>\n )\n}\n\nexport { Slider }\n","import { formatTimecode } from '@cut-workbench/core'\nimport { cn } from '@/lib/cn'\n\ninterface TimecodeProps {\n /** 微秒 */\n us: number\n /** 是否显示毫秒段 */\n showMs?: boolean\n className?: string\n /** 显示为弱化色(例如总时长) */\n muted?: boolean\n title?: string\n}\n\n/**\n * µs ↔ `HH:MM:SS.mmm` 的唯一展示入口。\n * 等宽字体 + `tabular-nums`,避免播放时数字宽度跳动。\n */\nexport function Timecode({ us, showMs = true, className, muted, title }: TimecodeProps) {\n return (\n <span\n className={cn(\n 'font-mono text-xs tabular-nums',\n muted && 'text-muted-foreground',\n className,\n )}\n title={title ?? `${us} µs`}\n >\n {formatTimecode(us, showMs)}\n </span>\n )\n}\n","import {\n Pause,\n Play,\n SkipBack,\n SkipForward,\n StepBack,\n StepForward,\n Volume2,\n VolumeX,\n} from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport { Slider } from '@/components/ui/slider'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport { Timecode } from '@/components/common/Timecode'\nimport { cn } from '@/lib/cn'\n\ninterface PlaybackControlsProps {\n playing: boolean\n currentUs: number\n durationUs: number\n masterVolume: number\n playbackRate: number\n disabled?: boolean\n onToggle: () => void\n onJumpStart: () => void\n onJumpEnd: () => void\n onStepFrame: (direction: -1 | 1) => void\n onSeek: (us: number) => void\n onVolumeChange: (volume: number) => void\n onRateChange: (rate: number) => void\n className?: string\n}\n\nconst RATES = [0.5, 1, 1.5, 2]\n\n/** 播放控制条:逐帧 / 跳首尾 / 播放 / 时间码 / 速率 / 音量 */\nexport function PlaybackControls({\n playing,\n currentUs,\n durationUs,\n masterVolume,\n playbackRate,\n disabled,\n onToggle,\n onJumpStart,\n onJumpEnd,\n onStepFrame,\n onSeek,\n onVolumeChange,\n onRateChange,\n className,\n}: PlaybackControlsProps) {\n return (\n <div\n className={cn(\n 'flex h-11 shrink-0 items-center gap-1.5 border-t bg-card/60 px-3',\n className,\n )}\n >\n <div className=\"flex items-center gap-0.5\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onJumpStart} disabled={disabled} aria-label=\"跳到开头\">\n <SkipBack className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>跳到开头 (Home)</TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => onStepFrame(-1)}\n disabled={disabled}\n aria-label=\"上一帧\"\n >\n <StepBack className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>上一帧 (←)</TooltipContent>\n </Tooltip>\n\n <Button\n variant=\"secondary\"\n size=\"icon\"\n onClick={onToggle}\n disabled={disabled}\n aria-label={playing ? '暂停' : '播放'}\n className=\"mx-0.5\"\n >\n {playing ? <Pause className=\"size-4\" /> : <Play className=\"size-4\" />}\n </Button>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => onStepFrame(1)}\n disabled={disabled}\n aria-label=\"下一帧\"\n >\n <StepForward className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>下一帧 (→)</TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onJumpEnd} disabled={disabled} aria-label=\"跳到结尾\">\n <SkipForward className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>跳到结尾 (End)</TooltipContent>\n </Tooltip>\n </div>\n\n <div className=\"mx-2 flex items-baseline gap-1\">\n <Timecode us={currentUs} className=\"text-[13px] font-medium\" />\n <span className=\"text-xs text-muted-foreground\">/</span>\n <Timecode us={durationUs} muted />\n </div>\n\n <input\n type=\"range\"\n className=\"mx-1 h-1 min-w-16 flex-1 cursor-pointer appearance-none rounded-full bg-muted accent-primary\"\n min={0}\n max={Math.max(1, durationUs)}\n step={1000}\n value={Math.min(currentUs, durationUs)}\n onChange={(e) => onSeek(Number(e.target.value))}\n disabled={disabled}\n aria-label=\"播放进度\"\n />\n\n <div className=\"flex items-center gap-1\">\n <div className=\"flex items-center rounded-md border p-0.5\">\n {RATES.map((rate) => (\n <button\n key={rate}\n type=\"button\"\n onClick={() => onRateChange(rate)}\n className={cn(\n 'rounded px-1.5 py-0.5 text-[11px] tabular-nums transition-colors',\n playbackRate === rate\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:text-foreground',\n )}\n title={rate !== 1 ? '变速预览(不会改变导出结果)' : '正常速度'}\n >\n {rate}×\n </button>\n ))}\n </div>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => onVolumeChange(masterVolume > 0 ? 0 : 1)}\n aria-label=\"静音\"\n >\n {masterVolume > 0 ? <Volume2 className=\"size-3.5\" /> : <VolumeX className=\"size-3.5\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>{masterVolume > 0 ? '静音预览' : '取消静音'}</TooltipContent>\n </Tooltip>\n <Slider\n className=\"w-20\"\n min={0}\n max={1}\n step={0.01}\n value={[masterVolume]}\n onValueChange={([v]) => onVolumeChange(v ?? 0)}\n aria-label=\"预览音量\"\n />\n </div>\n </div>\n )\n}\n","/** 下载 data URL(截图) */\nexport function downloadDataUrl(dataUrl: string, filename: string): void {\n const link = document.createElement('a')\n link.href = dataUrl\n link.download = filename\n document.body.appendChild(link)\n link.click()\n link.remove()\n}\n","import { useCallback, useEffect, useRef, useMemo } from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { Film, Loader2 } from 'lucide-react'\r\nimport { toast } from 'sonner'\r\nimport type { ClipTransform } from '@cut-workbench/core'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { reportError } from '@/hooks/use-toast-safe'\r\nimport { useAvCanvas } from '../hooks/useAvCanvas'\r\nimport { usePreviewSync } from '../hooks/usePreviewSync'\r\nimport { usePlaybackClock } from '../hooks/usePlaybackClock'\r\nimport { useCanvasInteraction } from '../hooks/useCanvasInteraction'\r\nimport { PreviewStage } from './PreviewStage'\r\nimport { PlaybackControls } from './PlaybackControls'\r\nimport { downloadDataUrl } from '../lib/download'\r\n\r\nconst FRAME_US = 1_000_000 / 30\r\n/** 播放头写 store 的最小间隔(微秒):避免 60fps 全量重渲染 */\r\nconst PLAYHEAD_WRITE_STEP_US = 10_000\r\n\r\n/**\r\n * 预览面板:画布 + 播放时钟 + sprite 同步。\r\n *\r\n * 播放路径:PlaybackClock(AudioContext) → rAF → previewFrame;音频独立排程。\r\n * 拖拽播放头:暂停音频 → 松手后重排 + previewFrame。\r\n *\r\n * 播放控制通过全局自定义事件与快捷键 / 时间轴解耦:\r\n * - `cut-test:toggle-play`\r\n * - `cut-test:step-frame` {direction}\r\n * - `cut-test:seek` {us}\r\n * - `cut-test:scrub-start` / `cut-test:scrub-end`\r\n * - `cut-test:add-asset-to-timeline` {assetId} → 转发 `cut-test:add-asset-request`\r\n */\r\nexport function PreviewPanel() {\r\n const editor = useCutEditor()\r\n const project = useEditor((s) => s.project)\r\n const assets = useEditor((s) => s.assets)\r\n const masterVolume = useEditor((s) => s.masterVolume)\r\n const setMasterVolume = useEditor((s) => s.setMasterVolume)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n const selectedClipIds = useEditor((s) => s.selectedClipIds)\r\n\r\n const canvas = useAvCanvas({ width: project.width, height: project.height })\r\n const previewFrame = canvas.previewFrame\r\n const lastWriteRef = useRef(0)\r\n\r\n const handleSyncErrors = useCallback((errors: Array<{ clipId: string; message: string }>) => {\r\n if (errors.length === 0) return\r\n // 同一批错误合并成一条提示:刷新恢复的工程可能有几十个 clip 都缺文件\r\n const missing = errors.filter((e) => e.message.startsWith('素材文件未载入'))\r\n const other = errors.filter((e) => !e.message.startsWith('素材文件未载入'))\r\n if (missing.length > 0) {\r\n toast.warning(`${missing.length} 个片段缺少源文件,已跳过预览`, {\r\n description: '刷新后素材二进制不会保留,请重新导入相同文件',\r\n duration: 8000,\r\n })\r\n }\r\n if (other.length > 0) {\r\n toast.error('部分片段无法载入预览', {\r\n description: Array.from(new Set(other.map((e) => e.message))).slice(0, 3).join('\\n'),\r\n })\r\n }\r\n }, [])\r\n\r\n const onFrame = useCallback(\r\n (us: number) => {\r\n void previewFrame(us)\r\n if (Math.abs(us - lastWriteRef.current) >= PLAYHEAD_WRITE_STEP_US) {\r\n lastWriteRef.current = us\r\n editor.store.getState().setPlayhead(us)\r\n }\r\n },\r\n [previewFrame],\r\n )\r\n\r\n const handleEnded = useCallback(() => {\r\n const total = editor.store.getState().project.clips.reduce(\r\n (max, c) => Math.max(max, c.startUs + c.durationUs),\r\n 0,\r\n )\r\n editor.store.getState().setPlayhead(total)\r\n }, [])\r\n\r\n const clock = usePlaybackClock({\r\n project,\r\n assets,\r\n masterVolume,\r\n onFrame,\r\n onEnded: handleEnded,\r\n })\r\n\r\n const clockRef = useRef(clock)\r\n useEffect(() => {\r\n clockRef.current = clock\r\n })\r\n\r\n const { refresh } = canvas\r\n usePreviewSync({ project, assets, canvas, refresh, onSyncErrors: handleSyncErrors })\r\n\r\n // 时间轴 / 属性面板的选中 → 画布选框\r\n const onlySelected = selectedClipIds.length === 1 ? (selectedClipIds[0] ?? null) : null\r\n const selectedClip = useMemo(\r\n () => (onlySelected == null ? null : (project.clips.find((c) => c.id === onlySelected) ?? null)),\r\n [onlySelected, project.clips],\r\n )\r\n const selectedAsset = useMemo(\r\n () => (selectedClip == null ? undefined : assets.find((a) => a.id === selectedClip.assetId)),\r\n [selectedClip, assets],\r\n )\r\n const selectionTransform: ClipTransform | null = selectedClip?.transform ?? null\r\n /**\r\n * 该片段此刻是否真的在画面上。\r\n *\r\n * 播放头离开片段区间后,AVCanvas 不再绘制它(时间可见性),但 sprite 本身仍\r\n * 存在且 `visible = true`(那个字段只跟随「轨道隐藏」)。因此这里必须自己判时间,\r\n * 否则用户能在空白处拖到「已经不在画面上」的素材。\r\n */\r\n const selectionInRange =\r\n selectedClip != null &&\r\n playheadUs >= selectedClip.startUs &&\r\n playheadUs <= selectedClip.startUs + selectedClip.durationUs\r\n /**\r\n * 纯音频片段没有画面(sprite 尺寸为 0、transform 只是「铺满画布」的占位值),\r\n * 若照常画选框会出现一个覆盖整个画布、却拖不动的框,反而误导用户。\r\n */\r\n const selectionVisible = selectedAsset != null && selectedAsset.kind !== 'audio'\r\n /** 是否允许在画布内拖动 / 缩放:必须同时「有画面」且「此刻在画面上」 */\r\n const selectionInteractive = selectionVisible && selectionInRange\r\n\r\n const hitTest = useCallback(\r\n (point: { x: number; y: number }) => {\r\n // 时间取播放头:不在当前画面上的片段不能被点中(否则会拖到看不见的素材)\r\n const state = editor.store.getState()\r\n return canvas.hitTest(point, state.project, state.playheadUs)\r\n },\r\n [canvas],\r\n )\r\n\r\n const handleCanvasSelect = useCallback((clipId: string | null) => {\r\n const state = editor.store.getState()\r\n if (clipId == null) {\r\n if (state.selectedClipIds.length > 0) state.selectClips([])\r\n return\r\n }\r\n if (!state.selectedClipIds.includes(clipId)) state.selectClips([clipId])\r\n }, [])\r\n\r\n /** 画布内一次变换结束:只在这里写 store(拖动期间不写) */\r\n const handleCanvasCommit = useCallback((clipId: string, transform: ClipTransform) => {\r\n editor.store\r\n .getState()\r\n .updateClip(clipId, { transform }, true, `canvas:${clipId}`)\r\n }, [])\r\n\r\n const interaction = useCanvasInteraction({\r\n getCanvas: canvas.getCanvas,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n hostRef: canvas.containerRef,\r\n selectedClipId: selectedClip?.id ?? null,\r\n transform: selectionTransform,\r\n /** 播放头不在片段区间内时,不允许在画布内拖动 / 缩放 / 旋转 */\r\n interactive: selectionInteractive,\r\n aspectLocked: selectedClip?.aspectLocked === true,\r\n hitTest,\r\n onSelect: handleCanvasSelect,\r\n onCommit: handleCanvasCommit,\r\n })\r\n\r\n // 拖动中优先用实时几何绘制选框,松手后回落到 store 值\r\n // 播放头离开片段后仍显示选框(提示「它被选中了」),只是不给手柄、不可拖\r\n const overlayTransform =\r\n selectionVisible ? (interaction.liveTransform ?? selectionTransform) : null\r\n\r\n // 首帧:画布就绪后渲染当前播放头\r\n useEffect(() => {\r\n if (!canvas.ready) return\r\n const timer = window.setTimeout(() => {\r\n void refresh(editor.store.getState().playheadUs)\r\n }, 80)\r\n return () => window.clearTimeout(timer)\r\n }, [canvas.ready, refresh])\r\n\r\n const handleSeek = useCallback((us: number) => {\r\n clockRef.current.seek(us)\r\n editor.store.getState().setPlayhead(us)\r\n }, [])\r\n\r\n const handleStepFrame = useCallback((direction: -1 | 1) => {\r\n const current = clockRef.current\r\n current.pause()\r\n const from = editor.store.getState().playheadUs\r\n const next = Math.max(0, Math.min(current.durationUs, from + direction * FRAME_US))\r\n current.seek(next)\r\n editor.store.getState().setPlayhead(next)\r\n }, [])\r\n\r\n // 全局事件桥接\r\n useEffect(() => {\r\n const onToggle = () => clockRef.current.toggle()\r\n const onStep = (event: Event) => {\r\n const detail = (event as CustomEvent<{ direction: -1 | 1 }>).detail\r\n handleStepFrame(detail?.direction ?? 1)\r\n }\r\n const onSeek = (event: Event) => {\r\n const detail = (event as CustomEvent<{ us: number }>).detail\r\n if (detail?.us != null) handleSeek(detail.us)\r\n }\r\n const onScrubStart = () => clockRef.current.beginScrub()\r\n const onScrubEnd = () => {\r\n const us = editor.store.getState().playheadUs\r\n clockRef.current.endScrub()\r\n clockRef.current.seek(us)\r\n }\r\n const onAddAsset = (event: Event) => {\r\n const detail = (event as CustomEvent<{ assetId: string }>).detail\r\n if (detail?.assetId) {\r\n window.dispatchEvent(\r\n new CustomEvent('cut-test:add-asset-request', { detail: { assetId: detail.assetId } }),\r\n )\r\n }\r\n }\r\n\r\n window.addEventListener('cut-test:toggle-play', onToggle)\r\n window.addEventListener('cut-test:step-frame', onStep)\r\n window.addEventListener('cut-test:seek', onSeek)\r\n window.addEventListener('cut-test:scrub-start', onScrubStart)\r\n window.addEventListener('cut-test:scrub-end', onScrubEnd)\r\n window.addEventListener('cut-test:add-asset-to-timeline', onAddAsset)\r\n return () => {\r\n window.removeEventListener('cut-test:toggle-play', onToggle)\r\n window.removeEventListener('cut-test:step-frame', onStep)\r\n window.removeEventListener('cut-test:seek', onSeek)\r\n window.removeEventListener('cut-test:scrub-start', onScrubStart)\r\n window.removeEventListener('cut-test:scrub-end', onScrubEnd)\r\n window.removeEventListener('cut-test:add-asset-to-timeline', onAddAsset)\r\n }\r\n }, [handleSeek, handleStepFrame])\r\n\r\n const handleCapture = useCallback(() => {\r\n const dataUrl = canvas.captureImage()\r\n if (dataUrl == null) {\r\n toast.error('截图失败:画布尚未就绪')\r\n return\r\n }\r\n downloadDataUrl(dataUrl, `frame-${Date.now()}.png`)\r\n toast.success('已导出当前帧')\r\n }, [canvas])\r\n\r\n // 全屏直接读 DOM,无需 memo(React Compiler 无法保存含 ref 读取的 useCallback)\r\n const handleFullscreen = () => {\r\n const el = canvas.containerRef.current?.parentElement\r\n if (el == null) return\r\n if (document.fullscreenElement != null) {\r\n void document.exitFullscreen()\r\n } else {\r\n void el.requestFullscreen().catch((err: unknown) => reportError(err, '全屏失败'))\r\n }\r\n }\r\n\r\n const emptyHint = useMemo(() => {\r\n if (assets.length === 0) {\r\n return (\r\n <EmptyState\r\n icon={Film}\r\n title=\"还没有素材\"\r\n description=\"把 MP4、音频、图片或 SRT 拖到左侧素材库\"\r\n // 提示文字叠在深色舞台底上,用的是舞台内的前景色而非主题前景色\r\n className=\"text-muted-foreground\"\r\n />\r\n )\r\n }\r\n if (project.clips.length === 0) {\r\n return (\r\n <EmptyState\r\n icon={Film}\r\n title=\"时间轴为空\"\r\n description=\"双击左侧素材卡片,把它加入时间轴\"\r\n className=\"text-muted-foreground\"\r\n />\r\n )\r\n }\r\n return null\r\n }, [assets.length, project.clips.length])\r\n\r\n return (\r\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\r\n <PreviewStage\r\n attachRef={canvas.attachRef}\r\n width={project.width}\r\n height={project.height}\r\n emptyHint={emptyHint}\r\n onCapture={handleCapture}\r\n onFullscreen={handleFullscreen}\r\n selection={overlayTransform}\r\n selectionInteractive={selectionInteractive}\r\n interaction={interaction.interaction}\r\n onPointerDown={interaction.onPointerDown}\r\n />\r\n <PlaybackControls\r\n playing={clock.playing}\r\n currentUs={playheadUs}\r\n durationUs={clock.durationUs}\r\n masterVolume={masterVolume}\r\n playbackRate={clock.playbackRate}\r\n onToggle={clock.toggle}\r\n onJumpStart={() => handleSeek(0)}\r\n onJumpEnd={() => handleSeek(clock.durationUs)}\r\n onStepFrame={handleStepFrame}\r\n onSeek={handleSeek}\r\n onVolumeChange={setMasterVolume}\r\n onRateChange={clock.setPlaybackRate}\r\n />\r\n {!canvas.ready && (\r\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\r\n <span className=\"flex items-center gap-2 rounded-md bg-background/80 px-3 py-1.5 text-xs text-muted-foreground\">\r\n <Loader2 className=\"size-3 animate-spin\" />\r\n 画布初始化…\r\n </span>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\n * 应用侧量化常量。集中管理,避免散落魔法数字。\n *\n * 与核心逻辑无关的常量(缩放档位、面板尺寸、吸附像素阈值)留在这里;\n * 逻辑层必须知道的值(默认时长、默认音量、schema 版本、撤销栈深度)\n * 由 `@cut-workbench/core` 提供,这里只做转发,避免两处定义漂移。\n */\n\nexport {\n DEFAULT_STILL_DURATION_US,\n DEFAULT_CLIP_VOLUME,\n PROJECT_SCHEMA_VERSION,\n MAX_HISTORY_STEPS,\n HISTORY_MERGE_WINDOW_MS,\n} from '@cut-workbench/core'\n\n/** 时间轴横向缩放档位(每秒像素) */\nexport const ZOOM_LEVELS = [10, 20, 40, 80, 120, 200, 320, 500] as const\n\nexport const DEFAULT_PX_PER_SECOND = 80\nexport const MIN_PX_PER_SECOND = 10\nexport const MAX_PX_PER_SECOND = 500\n\n/** 吸附阈值(像素) */\nexport const SNAP_THRESHOLD_PX = 15\n/**\n * 已吸附后脱离的滞后阈值(像素)。\n *\n * 必须 **大于** SNAP_THRESHOLD_PX,否则「先吸附、再多走一点才脱离」在数学上不成立:\n * 旧值 8px 小于吸附阈值 15px,导致滞后分支永远走不到,吸附在窗口边缘反复跳动。\n */\nexport const SNAP_RELEASE_PX = 26\n\n/** 轨道行高(像素) */\nexport const TRACK_HEIGHT_PX = 56\nexport const RULER_HEIGHT_PX = 28\nexport const TRACK_HEADER_WIDTH_PX = 150\n\n/** 播放器每帧步进(微秒),仅用于 UI 刷新节流 */\nexport const FRAME_INTERVAL_US = 16_666\n\n/** 导出默认码率 */\nexport const DEFAULT_BITRATE = 8_000_000\n\n/** IndexedDB 数据库(应用默认,库默认值为 `cut-workbench`) */\nexport const DB_NAME = 'cut-workbench'\nexport const DB_VERSION = 1\n","import {\r\n Copy,\r\n Info,\r\n Magnet,\r\n Plus,\r\n Scissors,\r\n Trash2,\r\n ZoomIn,\r\n ZoomOut,\r\n} from 'lucide-react'\r\nimport { Button } from '@/components/ui/button'\r\nimport { Separator } from '@/components/ui/separator'\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\r\nimport { Timecode } from '@/components/common/Timecode'\r\n\r\ninterface TimelineToolbarProps {\r\n playheadUs: number\r\n durationUs: number\r\n pxPerSecond: number\r\n selectionCount: number\r\n magnetEnabled: boolean\r\n onToggleMagnet: () => void\r\n onSplit: () => void\r\n onDuplicate: () => void\r\n onRemove: () => void\r\n onZoomIn: () => void\r\n onZoomOut: () => void\r\n onZoomToFit: () => void\r\n onAddTrack: (kind: 'visual' | 'audio') => void\r\n canZoomIn: boolean\r\n canZoomOut: boolean\r\n}\r\n\r\n/** 时间轴顶部工具条:分割 / 复制 / 删除 / 吸附开关 / 缩放 / 加轨道 */\r\nexport function TimelineToolbar({\r\n playheadUs,\r\n durationUs,\r\n pxPerSecond,\r\n selectionCount,\r\n magnetEnabled,\r\n onToggleMagnet,\r\n onSplit,\r\n onDuplicate,\r\n onRemove,\r\n onZoomIn,\r\n onZoomOut,\r\n onZoomToFit,\r\n onAddTrack,\r\n canZoomIn,\r\n canZoomOut,\r\n}: TimelineToolbarProps) {\r\n return (\r\n <div className=\"flex h-9 shrink-0 items-center gap-1 border-b bg-card/60 px-2\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onSplit} aria-label=\"分割\">\r\n <Scissors className=\"size-3.5\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>在播放头分割 (S)</TooltipContent>\r\n </Tooltip>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n onClick={onDuplicate}\r\n disabled={selectionCount === 0}\r\n aria-label=\"复制片段\"\r\n >\r\n <Copy className=\"size-3.5\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>复制片段 (Ctrl+D)</TooltipContent>\r\n </Tooltip>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n onClick={onRemove}\r\n disabled={selectionCount === 0}\r\n aria-label=\"删除片段\"\r\n >\r\n <Trash2 className=\"size-3.5\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>删除片段 (Delete)</TooltipContent>\r\n </Tooltip>\r\n\r\n <Separator orientation=\"vertical\" className=\"mx-1 h-4\" />\r\n\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={magnetEnabled ? 'secondary' : 'ghost'}\r\n size=\"icon-sm\"\r\n onClick={onToggleMagnet}\r\n aria-label=\"吸附\"\r\n aria-pressed={magnetEnabled}\r\n >\r\n <Magnet className=\"size-3.5\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>吸附对齐(阈值 15px)</TooltipContent>\r\n </Tooltip>\r\n\r\n <Separator orientation=\"vertical\" className=\"mx-1 h-4\" />\r\n\r\n <div className=\"flex items-center gap-0.5\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n onClick={onZoomOut}\r\n disabled={!canZoomOut}\r\n aria-label=\"缩小\"\r\n >\r\n <ZoomOut className=\"size-3.5\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>缩小 (-)</TooltipContent>\r\n </Tooltip>\r\n <span className=\"w-11 text-center font-mono text-[10px] text-muted-foreground tabular-nums\">\r\n {Math.round(pxPerSecond)}\r\n </span>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n onClick={onZoomIn}\r\n disabled={!canZoomIn}\r\n aria-label=\"放大\"\r\n >\r\n <ZoomIn className=\"size-3.5\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>放大 (+)</TooltipContent>\r\n </Tooltip>\r\n <Button variant=\"ghost\" size=\"xs\" onClick={onZoomToFit} className=\"text-[10px]\">\r\n 适应\r\n </Button>\r\n </div>\r\n\r\n <Separator orientation=\"vertical\" className=\"mx-1 h-4\" />\r\n\r\n <div className=\"flex items-center gap-0.5\">\r\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onAddTrack('visual')}>\r\n <Plus className=\"size-3\" />\r\n 视频轨\r\n </Button>\r\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onAddTrack('audio')}>\r\n <Plus className=\"size-3\" />\r\n 音频轨\r\n </Button>\r\n </div>\r\n\r\n <div className=\"ml-auto flex items-center gap-2 text-[11px] text-muted-foreground\">\r\n {selectionCount > 0 && (\r\n <span className=\"rounded bg-primary/15 px-1.5 py-0.5 text-primary\">\r\n 已选 {selectionCount}\r\n </span>\r\n )}\r\n <Info className=\"size-3\" />\r\n <span className=\"tabular-nums\">\r\n <Timecode us={playheadUs} className=\"text-foreground\" />\r\n <span className=\"mx-1\">/</span>\r\n <Timecode us={durationUs} muted />\r\n </span>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","import { useMemo } from 'react'\nimport { buildTicks, formatTimecode } from '@cut-workbench/core'\nimport { RULER_HEIGHT_PX } from '@/lib/constants'\nimport { cn } from '@/lib/cn'\n\ninterface RulerProps {\n pxPerSecond: number\n /** 内容总宽(像素) */\n widthPx: number\n /** 播放头像素位置(仅用于视觉标记,不做拖拽) */\n playheadPx: number\n /** 当前横向滚动(用于只渲染可见刻度) */\n scrollLeftPx: number\n className?: string\n}\n\n/**\n * 刻度尺。自适应刻度密度(1s / 5s / 10s / 1min…),由 buildTicks 决定。\n *\n * 指针交互由父容器(Timeline 的 sticky 头部)统一处理,因此这里只负责绘制,\n * 避免嵌套的 pointer 事件处理。\n */\nexport function Ruler({\n pxPerSecond,\n widthPx,\n playheadPx,\n scrollLeftPx,\n className,\n}: RulerProps) {\n const ticks = useMemo(\n () => buildTicks(pxPerSecond, Math.max(widthPx, 1), scrollLeftPx),\n [pxPerSecond, scrollLeftPx, widthPx],\n )\n\n return (\n <div\n className={cn('relative shrink-0 cursor-ew-resize border-b bg-timeline-bg select-none', className)}\n style={{ height: RULER_HEIGHT_PX, width: widthPx }}\n >\n {ticks.map((tick) => (\n <div\n key={`${tick.us}-${tick.major ? 'M' : 'm'}`}\n className={cn(\n 'absolute bottom-0 w-px',\n tick.major ? 'h-2 bg-muted-foreground/50' : 'h-1 bg-muted-foreground/25',\n )}\n style={{ left: tick.px }}\n />\n ))}\n {ticks\n .filter((t) => t.major)\n .map((tick) => (\n <span\n key={`label-${tick.us}`}\n className=\"absolute top-1 font-mono text-[10px] text-muted-foreground tabular-nums\"\n style={{ left: tick.px + 3 }}\n >\n {formatTimecode(tick.us, false)}\n </span>\n ))}\n\n <div\n className=\"pointer-events-none absolute top-0 z-10 -translate-x-1/2\"\n style={{ left: playheadPx }}\n >\n <div className=\"h-2 w-3 rounded-b-sm bg-playhead\" />\n </div>\n </div>\n )\n}\n","import { memo } from 'react'\r\nimport { Eye, EyeOff, Lock, LockOpen, Volume2, VolumeX, X } from 'lucide-react'\r\nimport type { Track } from '@cut-workbench/core'\r\nimport { cn } from '@/lib/cn'\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\r\nimport { TRACK_HEIGHT_PX } from '@/lib/constants'\r\n\r\nexport interface TrackHeaderProps {\r\n track: Track\r\n onToggleMute: () => void\r\n onToggleLock: () => void\r\n onToggleHidden: () => void\r\n onRemove: () => void\r\n}\r\n\r\n/**\r\n * 轨道头:名称 + 静音 / 锁定 / 隐藏 / 删除。\r\n *\r\n * 使用绝对定位叠在轨道内容之上(时间轴左列),因此宽度固定、不随横向滚动。\r\n *\r\n * 宽度预算(TRACK_HEADER_WIDTH_PX = 132):左右内边距 8+8、gap 4、图标区 4×18。\r\n * 之前图标按钮是 size-5(20px),文字区只剩 24px,「视频 1」被截成「视…」。\r\n * 缩到 18px 后文字区约 40px,中文轨名可以完整显示。\r\n */\r\nexport const TrackHeader = memo(function TrackHeader({\r\n track,\r\n onToggleMute,\r\n onToggleLock,\r\n onToggleHidden,\r\n onRemove,\r\n}: TrackHeaderProps) {\r\n return (\r\n <div\r\n className={cn(\r\n 'flex items-center gap-1 border-r border-b border-timeline-grid px-2',\r\n track.kind === 'audio' ? 'bg-clip-audio/10' : 'bg-clip-video/10',\r\n track.hidden && 'opacity-60',\r\n )}\r\n style={{ height: TRACK_HEIGHT_PX }}\r\n data-track-header\r\n >\r\n <div className=\"min-w-0 flex-1\">\r\n <p className=\"truncate text-[11px] font-medium\">{track.name}</p>\r\n <p className=\"truncate text-[10px] text-muted-foreground\">\r\n {track.kind === 'audio' ? '音频轨' : '视觉轨'}\r\n </p>\r\n </div>\r\n <div className=\"flex shrink-0 items-center gap-px\">\r\n <IconToggle\r\n active={track.muted}\r\n onClick={onToggleMute}\r\n label={track.muted ? '取消静音' : '静音轨道'}\r\n icon={track.muted ? VolumeX : Volume2}\r\n />\r\n <IconToggle\r\n active={track.locked}\r\n onClick={onToggleLock}\r\n label={track.locked ? '解锁轨道' : '锁定轨道'}\r\n icon={track.locked ? Lock : LockOpen}\r\n />\r\n <IconToggle\r\n active={track.hidden}\r\n onClick={onToggleHidden}\r\n label={track.hidden ? '显示轨道' : '隐藏轨道'}\r\n icon={track.hidden ? EyeOff : Eye}\r\n />\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <button\r\n type=\"button\"\r\n onClick={onRemove}\r\n aria-label=\"删除轨道\"\r\n className=\"flex size-[18px] items-center justify-center rounded text-muted-foreground transition-colors hover:text-destructive\"\r\n >\r\n <X className=\"size-3\" />\r\n </button>\r\n </TooltipTrigger>\r\n <TooltipContent>删除轨道</TooltipContent>\r\n </Tooltip>\r\n </div>\r\n </div>\r\n )\r\n})\r\n\r\nfunction IconToggle({\r\n active,\r\n onClick,\r\n label,\r\n icon: Icon,\r\n}: {\r\n active: boolean\r\n onClick: () => void\r\n label: string\r\n icon: typeof Eye\r\n}) {\r\n return (\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <button\r\n type=\"button\"\r\n onClick={onClick}\r\n aria-label={label}\r\n aria-pressed={active}\r\n className={cn(\r\n 'flex size-[18px] items-center justify-center rounded transition-colors',\r\n active ? 'bg-primary/20 text-primary' : 'text-muted-foreground hover:text-foreground',\r\n )}\r\n >\r\n <Icon className=\"size-3\" />\r\n </button>\r\n </TooltipTrigger>\r\n <TooltipContent>{label}</TooltipContent>\r\n </Tooltip>\r\n )\r\n}\r\n","import { useEffect, useRef, useState } from 'react'\n\n/**\n * 懒加载可见性:元素进入视口才置 true(只切一次,不来回抖动)。\n *\n * 用于时间轴上大量 clip 的缩略图/波形渲染:200 个 clip 同时解码缩略图会很卡。\n *\n * IntersectionObserver 不可用时用「首帧后置可见」的异步方式降级(不在 effect 里同步 setState)。\n */\nexport function useLazyVisibility<T extends HTMLElement>(): {\n ref: React.RefObject<T | null>\n visible: boolean\n} {\n const ref = useRef<T | null>(null)\n const [visible, setVisible] = useState(false)\n\n useEffect(() => {\n if (visible) return\n const el = ref.current\n if (el == null) return\n\n if (typeof IntersectionObserver === 'undefined') {\n const timer = window.setTimeout(() => setVisible(true), 0)\n return () => window.clearTimeout(timer)\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((e) => e.isIntersecting)) {\n setVisible(true)\n observer.disconnect()\n }\n },\n { rootMargin: '200px' },\n )\n observer.observe(el)\n return () => observer.disconnect()\n }, [visible])\n\n return { ref, visible }\n}\n","import { memo, useMemo } from \"react\";\nimport type { MediaAsset, TimelineClip, TrackKind } from \"@cut-workbench/core\";\nimport { formatDuration } from \"@/lib/format\";\nimport { usToPx } from \"@cut-workbench/core\";\nimport { buildWaveformPath } from \"@cut-workbench/core\";\nimport { cn } from \"@/lib/cn\";\nimport { MIN_CLIP_WIDTH_PX, TRIM_HANDLE_PX } from \"../constants\";\nimport { useLazyVisibility } from \"../hooks/useLazyVisibility\";\n\n/**\n * 一组稳定的 clip 交互回调。\n *\n * 为什么单独抽出来:Timeline 每次拖拽预览都会重新渲染,如果给每个 ClipBlock 传内联箭头函数,\n * `memo` 会全部失效(200 个 clip 一起重渲染,拖拽直接卡死)。\n * 把回调做成「接收 clip 参数」的稳定对象后,其余 clip 的 props 不变,memo 生效。\n */\nexport interface ClipHandlers {\n beginMove: (event: React.PointerEvent, clip: TimelineClip) => void;\n beginTrimStart: (event: React.PointerEvent, clip: TimelineClip) => void;\n beginTrimEnd: (event: React.PointerEvent, clip: TimelineClip) => void;\n select: (clip: TimelineClip, additive: boolean) => void;\n split: (clip: TimelineClip) => void;\n duplicate: (clip: TimelineClip) => void;\n remove: (clip: TimelineClip) => void;\n showInfo: (clip: TimelineClip) => void;\n seekTo: (clip: TimelineClip) => void;\n selectOnly: (clip: TimelineClip) => void;\n}\n\nexport interface ClipBlockProps {\n clip: TimelineClip;\n asset: MediaAsset | undefined;\n pxPerSecond: number;\n selected: boolean;\n locked: boolean;\n trackKind: TrackKind;\n /** 拖拽中的临时位移(像素),不写入 store */\n previewOffsetPx?: number;\n previewWidthPx?: number;\n handlers: ClipHandlers;\n}\n\nconst KIND_BG: Record<MediaAsset[\"kind\"], string> = {\n video: \"bg-clip-video/35 border-clip-video/60\",\n audio: \"bg-clip-audio/35 border-clip-audio/60\",\n image: \"bg-clip-image/35 border-clip-image/60\",\n text: \"bg-clip-text/35 border-clip-text/60\",\n subtitle: \"bg-clip-subtitle/35 border-clip-subtitle/60\",\n};\n\n/**\n * 单个 clip 块。\n *\n * 渲染策略:\n * - 视频背景用多张缩略图按百分比平铺成一条缩略图带(CSS background 多值),不建 DOM 图;\n * - 音频用 SVG 波形,且只在元素进入视口后才生成路径(IntersectionObserver 懒加载);\n * - 拖拽位移用 transform,不写 store;\n * - 全 memo 化 + 稳定回调(见 ClipHandlers),200 clip 拖拽仍流畅。\n */\nexport const ClipBlock = memo(function ClipBlock({\n clip,\n asset,\n pxPerSecond,\n selected,\n locked,\n trackKind,\n previewOffsetPx = 0,\n previewWidthPx,\n handlers,\n}: ClipBlockProps) {\n const { ref, visible } = useLazyVisibility<HTMLDivElement>();\n const leftPx = usToPx(clip.startUs, pxPerSecond);\n const widthPx = Math.max(\n MIN_CLIP_WIDTH_PX,\n previewWidthPx ?? usToPx(clip.durationUs, pxPerSecond),\n );\n const kind = asset?.kind ?? (trackKind === \"audio\" ? \"audio\" : \"video\");\n const tooNarrow = widthPx < 56;\n const urls = asset?.thumbnailUrls;\n const thumbnailKey = urls?.join(\"|\") ?? \"\";\n\n const background = useMemo(() => {\n if (\n asset?.kind !== \"video\" ||\n urls == null ||\n urls.length === 0 ||\n !visible\n )\n return undefined;\n const share = 100 / urls.length;\n return urls\n .map(\n (url, index) =>\n `url(\"${url}\") ${index * share}% 50% / ${share}% 100% no-repeat`,\n )\n .join(\", \");\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [asset?.kind, thumbnailKey, visible]);\n\n const waveformPath = useMemo(() => {\n if (asset?.kind !== \"audio\" || asset.waveform == null || !visible)\n return \"\";\n return buildWaveformPath(asset.waveform, 200, 32);\n }, [asset?.kind, asset?.waveform, visible]);\n\n return (\n <div\n ref={ref}\n role=\"button\"\n tabIndex={0}\n aria-selected={selected}\n data-clip-block={clip.id}\n onPointerDown={\n locked\n ? undefined\n : (event) => {\n handlers.select(clip, event.shiftKey);\n handlers.beginMove(event, clip);\n }\n }\n onDoubleClick={() => handlers.seekTo(clip)}\n className={cn(\n // select-none:素材名与时长只是标签,拖拽时不该被浏览器选中(会留下蓝色选区)\n 'group absolute top-1 bottom-1 overflow-hidden rounded-md border text-left select-none transition-shadow',\n KIND_BG[kind],\n selected\n ? \"ring-2 ring-primary ring-offset-1 ring-offset-timeline-bg\"\n : \"hover:brightness-110\",\n locked\n ? \"cursor-not-allowed opacity-70\"\n : \"cursor-grab active:cursor-grabbing\",\n )}\n style={{\n left: leftPx,\n width: widthPx,\n transform:\n previewOffsetPx !== 0\n ? `translateX(${previewOffsetPx}px)`\n : undefined,\n zIndex: selected ? 5 : 1,\n background,\n }}\n title={`${asset?.name ?? \"缺失素材\"}\\n${formatDuration(clip.durationUs)}`}\n >\n {/*\n * 片段上的文字/波形都压在不固定的画面(缩略图)上,语义固定为「深底浅字」,\n * 因此用 --cw-badge-scrim* 系列 token 而不是 text-white / bg-black。\n * 透明度用 token + `/xx` 表达,宿主改 token 后整套遮罩一起变。\n */}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-b from-badge-scrim/15 via-transparent to-badge-scrim/75\" />\n\n {waveformPath !== \"\" && (\n <svg\n viewBox=\"0 0 200 32\"\n preserveAspectRatio=\"none\"\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-2/3 w-full text-badge-scrim-foreground/60\"\n >\n <path d={waveformPath} stroke=\"currentColor\" strokeWidth={0.8} />\n </svg>\n )}\n\n <div className=\"pointer-events-none relative flex h-full flex-col justify-between p-1\">\n {!tooNarrow && (\n <p className=\"truncate text-[10px] leading-tight font-medium text-badge-scrim-foreground/95 drop-shadow\">\n {asset?.name ?? \"缺失素材\"}\n </p>\n )}\n {!tooNarrow && (\n <p className=\"truncate font-mono text-[9px] text-badge-scrim-foreground/75 tabular-nums\">\n {formatDuration(clip.durationUs)}\n {clip.playbackRate !== 1 && ` · ${clip.playbackRate}×`}\n {clip.muted && \" · 静音\"}\n </p>\n )}\n </div>\n\n {!locked && widthPx > 28 && (\n <>\n <div\n onPointerDown={(event) => handlers.beginTrimStart(event, clip)}\n className=\"absolute inset-y-0 left-0 z-10 flex cursor-col-resize items-center justify-center transition-colors hover:bg-primary/40\"\n style={{ width: TRIM_HANDLE_PX }}\n aria-label=\"裁剪起点\"\n role=\"slider\"\n aria-valuenow={0}\n >\n <span className=\"h-4 w-0.5 rounded bg-badge-scrim-foreground/70 opacity-0 group-hover:opacity-100\" />\n </div>\n <div\n onPointerDown={(event) => handlers.beginTrimEnd(event, clip)}\n className=\"absolute inset-y-0 right-0 z-10 flex cursor-col-resize items-center justify-center transition-colors hover:bg-primary/40\"\n style={{ width: TRIM_HANDLE_PX }}\n aria-label=\"裁剪终点\"\n role=\"slider\"\n aria-valuenow={0}\n >\n <span className=\"h-4 w-0.5 rounded bg-badge-scrim-foreground/70 opacity-0 group-hover:opacity-100\" />\n </div>\n </>\n )}\n </div>\n );\n});\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\"\nimport { ContextMenu as ContextMenuPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction ContextMenu({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {\n return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />\n}\n\nfunction ContextMenuTrigger({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {\n return (\n <ContextMenuPrimitive.Trigger data-slot=\"context-menu-trigger\" {...props} />\n )\n}\n\nfunction ContextMenuGroup({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {\n return (\n <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />\n )\n}\n\n// ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n// utility 不生效(右键菜单没有背景/边框)。原因详见 dialog.tsx 的 DialogPortal。\nfunction ContextMenuPortal({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {\n const container = usePortalContainer()\n return (\n <ContextMenuPrimitive.Portal\n data-slot=\"context-menu-portal\"\n container={container ?? undefined}\n {...props}\n />\n )\n}\n\nfunction ContextMenuSub({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {\n return <ContextMenuPrimitive.Sub data-slot=\"context-menu-sub\" {...props} />\n}\n\nfunction ContextMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {\n return (\n <ContextMenuPrimitive.RadioGroup\n data-slot=\"context-menu-radio-group\"\n {...props}\n />\n )\n}\n\nfunction ContextMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {\n inset?: boolean\n}) {\n return (\n <ContextMenuPrimitive.SubTrigger\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto\" />\n </ContextMenuPrimitive.SubTrigger>\n )\n}\n\nfunction ContextMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {\n return (\n <ContextMenuPrimitive.SubContent\n data-slot=\"context-menu-sub-content\"\n className={cn(\n \"z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ContextMenuContent({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {\n // Portal 挂载点由 ContextMenuPortal 统一处理(含 `.cw-root` 作用域)\n return (\n <ContextMenuPortal>\n <ContextMenuPrimitive.Content\n data-slot=\"context-menu-content\"\n className={cn(\n \"z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </ContextMenuPortal>\n )\n}\n\nfunction ContextMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {\n inset?: boolean\n variant?: \"default\" | \"destructive\"\n}) {\n return (\n <ContextMenuPrimitive.Item\n data-slot=\"context-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ContextMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {\n return (\n <ContextMenuPrimitive.CheckboxItem\n data-slot=\"context-menu-checkbox-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.CheckboxItem>\n )\n}\n\nfunction ContextMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {\n return (\n <ContextMenuPrimitive.RadioItem\n data-slot=\"context-menu-radio-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.RadioItem>\n )\n}\n\nfunction ContextMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {\n inset?: boolean\n}) {\n return (\n <ContextMenuPrimitive.Label\n data-slot=\"context-menu-label\"\n data-inset={inset}\n className={cn(\n \"px-2 py-1.5 text-sm font-medium text-foreground data-[inset]:pl-8\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ContextMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {\n return (\n <ContextMenuPrimitive.Separator\n data-slot=\"context-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction ContextMenuShortcut({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n ContextMenu,\n ContextMenuTrigger,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuCheckboxItem,\n ContextMenuRadioItem,\n ContextMenuLabel,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuGroup,\n ContextMenuPortal,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuRadioGroup,\n}\n","import { Copy, Info, Scissors, Trash2 } from 'lucide-react'\nimport {\n ContextMenu,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuTrigger,\n} from '@/components/ui/context-menu'\n\ninterface ClipContextMenuProps {\n children: React.ReactNode\n onSplit: () => void\n onDuplicate: () => void\n onRemove: () => void\n onShowInfo: () => void\n disabled?: boolean\n}\n\n/**\n * 时间轴右键菜单的「单例」外壳。\n *\n * 只挂一层 ContextMenu 包住整个轨道区 —— 若给每个 ClipBlock 各挂一个 Radix 菜单,\n * 200 个 clip 时右键与选中的重渲染成本会从 ~30ms 升到数百毫秒。\n * 具体作用于哪个 clip 由右键时的选中状态决定(见 Timeline 的 onContextMenu 处理)。\n */\nexport function ClipContextMenu({\n children,\n onSplit,\n onDuplicate,\n onRemove,\n onShowInfo,\n disabled,\n}: ClipContextMenuProps) {\n return (\n <ContextMenu>\n <ContextMenuTrigger asChild disabled={disabled}>\n {children}\n </ContextMenuTrigger>\n <ContextMenuContent className=\"w-44\">\n <ContextMenuItem onSelect={onSplit}>\n <Scissors className=\"size-3.5\" />\n 在播放头分割\n <ContextMenuShortcut>S</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem onSelect={onDuplicate}>\n <Copy className=\"size-3.5\" />\n 复制\n <ContextMenuShortcut>Ctrl+D</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem onSelect={onShowInfo}>\n <Info className=\"size-3.5\" />\n 属性\n </ContextMenuItem>\n <ContextMenuSeparator />\n <ContextMenuItem variant=\"destructive\" onSelect={onRemove}>\n <Trash2 className=\"size-3.5\" />\n 删除\n <ContextMenuShortcut>Del</ContextMenuShortcut>\n </ContextMenuItem>\n </ContextMenuContent>\n </ContextMenu>\n )\n}\n","import { cn } from '@/lib/cn'\nimport { RULER_HEIGHT_PX } from '@/lib/constants'\n\ninterface PlayheadProps {\n px: number\n playing: boolean\n className?: string\n}\n\n/** 播放头:一条竖线 + 顶部把手,跨过所有轨道 */\nexport function Playhead({ px, playing, className }: PlayheadProps) {\n return (\n <div\n className={cn('pointer-events-none absolute top-0 bottom-0 z-20 w-px', className)}\n style={{ left: px }}\n aria-hidden\n >\n <div\n className={cn(\n 'absolute inset-y-0 -left-px w-0.5 bg-playhead',\n playing && 'shadow-[0_0_6px_var(--cw-playhead)]',\n )}\n style={{ top: RULER_HEIGHT_PX - 4 }}\n />\n <div\n className={cn(\n 'absolute -translate-x-1/2 rounded-sm bg-playhead',\n playing ? 'size-3 rotate-45' : 'h-2.5 w-2.5 rotate-45',\n )}\n style={{ top: RULER_HEIGHT_PX - 14 }}\n />\n </div>\n )\n}\n","import { cn } from '@/lib/cn'\r\n\r\nexport interface MarqueeProps {\r\n x1: number\r\n y1: number\r\n x2: number\r\n y2: number\r\n className?: string\r\n}\r\n\r\n/** 框选矩形 */\r\nexport function Marquee({ x1, y1, x2, y2, className }: MarqueeProps) {\r\n return (\r\n <div\r\n className={cn(\r\n 'pointer-events-none absolute z-40 rounded-sm border border-primary/70 bg-primary/15',\r\n className,\r\n )}\r\n style={{\r\n left: Math.min(x1, x2),\r\n top: Math.min(y1, y2),\r\n width: Math.abs(x2 - x1),\r\n height: Math.abs(y2 - y1),\r\n }}\r\n aria-hidden\r\n />\r\n )\r\n}\r\n","import { useCallback, useEffect, useRef } from 'react'\r\nimport { useUi, useUiStoreApi } from '../../../context'\r\nimport { MAX_PX_PER_SECOND, MIN_PX_PER_SECOND, ZOOM_LEVELS } from '@/lib/constants'\r\nimport { clamp } from '@cut-workbench/core'\r\n\r\nexport interface UseTimelineZoomResult {\r\n pxPerSecond: number\r\n zoomIn: () => void\r\n zoomOut: () => void\r\n setZoom: (value: number) => void\r\n /** 按倍数缩放(Ctrl+滚轮用),以指针位置为锚点 */\r\n zoomBy: (factor: number, anchorClientX?: number, container?: HTMLElement | null) => void\r\n canZoomIn: boolean\r\n canZoomOut: boolean\r\n}\r\n\r\n/**\r\n * 时间轴横向缩放。\r\n *\r\n * 缩放以「指针位置对应的时刻」为锚点:先记录指针下的时间,缩放后把 scrollLeft\r\n * 调整回去,视觉上内容不会乱跳。\r\n */\r\nexport function useTimelineZoom(containerRef: React.RefObject<HTMLElement | null>): UseTimelineZoomResult {\r\n const uiApi = useUiStoreApi()\r\n const pxPerSecond = useUi((s) => s.pxPerSecond)\r\n const setPxPerSecond = useUi((s) => s.setPxPerSecond)\r\n const setScrollLeftPx = useUi((s) => s.setScrollLeftPx)\r\n\r\n const applyZoom = useCallback(\r\n (nextPps: number, anchorClientX?: number) => {\r\n const container = containerRef.current\r\n const prevPps = uiApi.getState().pxPerSecond\r\n const next = clamp(nextPps, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND)\r\n if (next === prevPps) return\r\n\r\n if (container != null && anchorClientX != null) {\r\n const rect = container.getBoundingClientRect()\r\n const offsetPx = anchorClientX - rect.left\r\n const anchorTimeSec = (container.scrollLeft + offsetPx) / prevPps\r\n setPxPerSecond(next)\r\n // 下一帧再回调 scroll,等 DOM 用新宽度重排\r\n requestAnimationFrame(() => {\r\n const targetScroll = anchorTimeSec * next - offsetPx\r\n container.scrollLeft = Math.max(0, targetScroll)\r\n setScrollLeftPx(container.scrollLeft)\r\n })\r\n } else {\r\n setPxPerSecond(next)\r\n }\r\n },\r\n [containerRef, setPxPerSecond, setScrollLeftPx],\r\n )\r\n\r\n const zoomIn = useCallback(() => {\r\n const next = ZOOM_LEVELS.find((z) => z > pxPerSecond) ?? MAX_PX_PER_SECOND\r\n applyZoom(next)\r\n }, [applyZoom, pxPerSecond])\r\n\r\n const zoomOut = useCallback(() => {\r\n const next = [...ZOOM_LEVELS].reverse().find((z) => z < pxPerSecond) ?? MIN_PX_PER_SECOND\r\n applyZoom(next)\r\n }, [applyZoom, pxPerSecond])\r\n\r\n const zoomBy = useCallback(\r\n (factor: number, anchorClientX?: number, container?: HTMLElement | null) => {\r\n const target = container === undefined ? containerRef.current : container\r\n void target\r\n applyZoom(pxPerSecond * factor, anchorClientX)\r\n },\r\n [applyZoom, containerRef, pxPerSecond],\r\n )\r\n\r\n const setZoom = useCallback((value: number) => applyZoom(value), [applyZoom])\r\n\r\n // Ctrl+滚轮缩放(非 passive,才能 preventDefault 阻止页面缩放)\r\n useEffect(() => {\r\n const container = containerRef.current\r\n if (container == null) return\r\n const onWheel = (event: WheelEvent) => {\r\n if (!event.ctrlKey && !event.metaKey) return\r\n event.preventDefault()\r\n const factor = event.deltaY < 0 ? 1.15 : 1 / 1.15\r\n applyZoom(uiApi.getState().pxPerSecond * factor, event.clientX)\r\n }\r\n container.addEventListener('wheel', onWheel, { passive: false })\r\n return () => container.removeEventListener('wheel', onWheel)\r\n }, [applyZoom, containerRef])\r\n\r\n return {\r\n pxPerSecond,\r\n zoomIn,\r\n zoomOut,\r\n setZoom,\r\n zoomBy,\r\n canZoomIn: pxPerSecond < MAX_PX_PER_SECOND,\r\n canZoomOut: pxPerSecond > MIN_PX_PER_SECOND,\r\n }\r\n}\r\n\r\n/** 内部使用:ref 仅为兼容 React 19 类型 */\r\nexport type TimelineZoomRef = ReturnType<typeof useRef<number>>\r\n","import { useCallback, useMemo, useRef } from 'react'\nimport type { Project, TimelineClip } from '@cut-workbench/core'\nimport {\n computeSnapWithHysteresis,\n type SnapResult,\n type SnapTarget,\n} from '@cut-workbench/core'\nimport { getClipEnd } from '@cut-workbench/core'\nimport { SNAP_RELEASE_PX, SNAP_THRESHOLD_PX } from '@/lib/constants'\nimport { SNAP_GUIDE_TTL_MS } from '../constants'\n\nexport interface SnapContext {\n project: Project\n /** 当前拖动/裁剪的 clip id(不参与吸附目标) */\n excludeIds: string[]\n playheadUs: number\n}\n\nexport interface UseSnapResult {\n /** 计算吸附;同时返回需要绘制的辅助线 */\n snap: (\n startUs: number,\n durationUs: number,\n pxPerSecond: number,\n context: SnapContext,\n /** 参与吸附的边,默认两端;裁剪终点时应只传 ['end'] */\n edges?: Array<'start' | 'end'>,\n ) => SnapResult\n /** 清空滞后状态 */\n reset: () => void\n}\n\n/**\n * 吸附 hook:把 store 数据转成候选目标,调用 core 的纯函数。\n *\n * 候选目标:\n * - 其它 clip 的头 / 尾\n * - 播放头\n * - 0 点\n * - 每条轨道的末尾(同轨最后一个 clip 的结束)\n *\n * 滞后状态保存在 ref 里(每次拖动会话重置),避免 React 重渲染丢失。\n */\nexport function useSnap(): UseSnapResult {\n const previousRef = useRef<SnapResult | null>(null)\n const lastTouchRef = useRef(0)\n\n const buildTargets = useCallback((context: SnapContext): SnapTarget[] => {\n const exclude = new Set(context.excludeIds)\n const targets: SnapTarget[] = [{ us: 0, kind: 'zero' }]\n\n const perTrackEnd = new Map<string, number>()\n for (const clip of context.project.clips) {\n if (exclude.has(clip.id)) continue\n targets.push({ us: clip.startUs, kind: 'clip-start', sourceId: clip.id })\n targets.push({ us: getClipEnd(clip), kind: 'clip-end', sourceId: clip.id })\n perTrackEnd.set(clip.trackId, Math.max(perTrackEnd.get(clip.trackId) ?? 0, getClipEnd(clip)))\n }\n\n targets.push({ us: context.playheadUs, kind: 'playhead' })\n for (const [trackId, end] of perTrackEnd) {\n targets.push({ us: end, kind: 'track-end', sourceId: trackId })\n }\n return targets\n }, [])\n\n const snap = useCallback(\n (\n startUs: number,\n durationUs: number,\n pxPerSecond: number,\n context: SnapContext,\n edges?: Array<'start' | 'end'>,\n ): SnapResult => {\n const now = performance.now()\n // 超过 TTL 的旧吸附状态不再参与滞后判断(例如两次独立拖拽)\n const previous = now - lastTouchRef.current < SNAP_GUIDE_TTL_MS ? previousRef.current : null\n const result = computeSnapWithHysteresis(\n {\n startUs,\n durationUs,\n targets: buildTargets(context),\n pxPerSecond,\n thresholdPx: SNAP_THRESHOLD_PX,\n previous,\n edges,\n },\n SNAP_RELEASE_PX,\n )\n previousRef.current = result.edge != null ? result : null\n lastTouchRef.current = now\n return result\n },\n [buildTargets],\n )\n\n const reset = useCallback(() => {\n previousRef.current = null\n lastTouchRef.current = 0\n }, [])\n\n return useMemo(() => ({ snap, reset }), [snap, reset])\n}\n\nexport type { SnapResult, SnapTarget, TimelineClip }\n","import { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { TimelineClip } from '@cut-workbench/core'\r\nimport { pxToUs, usToPx } from '@cut-workbench/core'\r\nimport { EMPTY_SNAP } from '@cut-workbench/core'\r\nimport { useSnap } from './useSnap'\r\nimport { useEditor } from '@cut-workbench/react'\r\n\r\nexport type DragMode = 'move' | 'trim-start' | 'trim-end'\r\n\r\nexport interface DragState {\r\n mode: DragMode\r\n clipId: string\r\n trackId: string\r\n originStartUs: number\r\n originDurationUs: number\r\n originInPointUs: number\r\n playbackRate: number\r\n /** 抓取点到 clip 左边缘的距离(像素),保证抓取点不跳 */\r\n grabOffsetPx: number\r\n}\r\n\r\nexport interface DragPreview {\r\n clipId: string\r\n trackId: string\r\n startUs: number\r\n durationUs: number\r\n guidePx: number[]\r\n}\r\n\r\nexport interface DragCommit extends DragPreview {\r\n inPointUs: number\r\n}\r\n\r\ninterface UseTimelineDragOptions {\r\n pxPerSecond: number\r\n /** 时间轴内容区左边界(clientX 坐标) */\r\n contentLeftPx: () => number\r\n /** 根据指针纵坐标定位目标轨道 id */\r\n resolveTrackAt: (clientY: number) => string | null\r\n /** 是否启用吸附(工具栏磁吸开关);关闭时拖拽完全自由 */\r\n snapEnabled: boolean\r\n onPreview: (preview: DragPreview | null) => void\r\n onCommit: (commit: DragCommit) => void\r\n}\r\n\r\nconst MIN_DURATION_US = 100_000\r\n\r\n/**\r\n * 时间轴拖拽交互(移动 / 左侧裁剪 / 右侧裁剪)。\r\n *\r\n * 性能设计:拖拽过程只更新「临时预览」(Timeline 用 transform 位移渲染),\r\n * 松手时才一次性写 store。因此 200 个 clip 拖动也不触发全量重渲染。\r\n *\r\n * 吸附:移动与右侧裁剪会参考吸附结果;左侧裁剪不吸附(避免与入点计算打架,\r\n * 这一点在 README 的已知限制里说明)。\r\n */\r\nexport function useTimelineDrag({\r\n pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled,\r\n onPreview,\r\n onCommit,\r\n}: UseTimelineDragOptions) {\r\n const [drag, setDrag] = useState<DragState | null>(null)\r\n const dragRef = useRef<DragState | null>(null)\r\n const snapApi = useSnap()\r\n const project = useEditor((s) => s.project)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n\r\n const envRef = useRef({\r\n pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled,\r\n project,\r\n playheadUs,\r\n })\r\n\r\n // 最新值在 effect 中同步(不在渲染期写 ref,保持 React Compiler 的纯渲染假设)\r\n useEffect(() => {\r\n envRef.current = {\r\n pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled,\r\n project,\r\n playheadUs,\r\n }\r\n })\r\n\r\n const compute = useCallback(\r\n (clientX: number, clientY: number, current: DragState) => {\r\n const env = envRef.current\r\n const xInContent = clientX - env.contentLeftPx()\r\n const pointerUs = pxToUs(xInContent, env.pxPerSecond)\r\n const rate = current.playbackRate > 0 ? current.playbackRate : 1\r\n\r\n let startUs = current.originStartUs\r\n let durationUs = current.originDurationUs\r\n let inPointUs = current.originInPointUs\r\n\r\n if (current.mode === 'move') {\r\n const grabUs = pxToUs(current.grabOffsetPx, env.pxPerSecond)\r\n startUs = Math.max(0, pointerUs - grabUs)\r\n } else if (current.mode === 'trim-start') {\r\n const endUs = current.originStartUs + current.originDurationUs\r\n // 起点最多回到「源素材入点归零」的位置,且至少保留 MIN_DURATION_US\r\n const minStartUs = current.originStartUs - current.originInPointUs / rate\r\n startUs = Math.max(\r\n Math.max(0, minStartUs),\r\n Math.min(pointerUs, endUs - MIN_DURATION_US),\r\n )\r\n } else {\r\n // 右侧裁剪:起点固定,终点跟随指针,下限由最短时长保证\r\n durationUs = Math.max(MIN_DURATION_US, pointerUs - current.originStartUs)\r\n }\r\n\r\n const targetTrack = env.resolveTrackAt(clientY) ?? current.trackId\r\n\r\n /**\r\n * 吸附。\r\n *\r\n * 关键:把「哪条边被吸住」交给 core 判定,这里只把位移量施加到对应的量上。\r\n * 旧实现无论吸住哪条边都拿 `snappedStartUs - originStartUs` 当作新时长,\r\n * 右侧裁剪时起点固定,于是时长被算成「目标点与当前终点的差值」,\r\n * 片段瞬间缩成 0.1s(用户看到的「闪到左边」)。这就是那次 bug 的根因。\r\n *\r\n * 三条拖动路径各自只让「真正在动的那条边」参与吸附:\r\n * - move:两端都在动,都参与;\r\n * - trim-end:只有终点在动 → ['end'];\r\n * - trim-start:只有起点在动 → ['start'],结束时再按位移回算入点。\r\n */\r\n const edges: Array<'start' | 'end'> =\r\n current.mode === 'trim-end'\r\n ? ['end']\r\n : current.mode === 'trim-start'\r\n ? ['start']\r\n : ['start', 'end']\r\n const result = env.snapEnabled\r\n ? snapApi.snap(\r\n startUs,\r\n durationUs,\r\n env.pxPerSecond,\r\n {\r\n project: env.project,\r\n excludeIds: [current.clipId],\r\n playheadUs: env.playheadUs,\r\n },\r\n edges,\r\n )\r\n : EMPTY_SNAP\r\n\r\n if (result.edge != null && result.deltaUs !== 0) {\r\n if (current.mode === 'trim-end') {\r\n // 终点在动:位移直接落到时长上,起点不动\r\n durationUs = durationUs + result.deltaUs\r\n } else {\r\n // move / trim-start:起点在动,位移落到起点;trim-start 的时长随之收缩\r\n const nextStart = startUs + result.deltaUs\r\n if (current.mode === 'move') {\r\n startUs = nextStart\r\n } else {\r\n startUs = nextStart\r\n durationUs = current.originStartUs + current.originDurationUs - nextStart\r\n }\r\n }\r\n }\r\n\r\n if (current.mode === 'trim-start') {\r\n const endUs = current.originStartUs + current.originDurationUs\r\n const deltaTimeline = startUs - current.originStartUs\r\n durationUs = endUs - startUs\r\n inPointUs = Math.max(0, current.originInPointUs + Math.round(deltaTimeline * rate))\r\n }\r\n\r\n return {\r\n preview: {\r\n clipId: current.clipId,\r\n trackId: targetTrack,\r\n startUs: Math.max(0, Math.round(startUs)),\r\n durationUs: Math.max(MIN_DURATION_US, Math.round(durationUs)),\r\n guidePx: result.guidePx,\r\n },\r\n inPointUs: Math.round(inPointUs),\r\n }\r\n },\r\n [snapApi],\r\n )\r\n\r\n const begin = useCallback(\r\n (event: React.PointerEvent, clip: TimelineClip, mode: DragMode) => {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n const env = envRef.current\r\n const pointerX = event.clientX - env.contentLeftPx()\r\n const clipLeftPx = usToPx(clip.startUs, env.pxPerSecond)\r\n const state: DragState = {\r\n mode,\r\n clipId: clip.id,\r\n trackId: clip.trackId,\r\n originStartUs: clip.startUs,\r\n originDurationUs: clip.durationUs,\r\n originInPointUs: clip.inPointUs,\r\n playbackRate: clip.playbackRate,\r\n grabOffsetPx: mode === 'move' ? Math.max(0, pointerX - clipLeftPx) : 0,\r\n }\r\n dragRef.current = state\r\n setDrag(state)\r\n snapApi.reset()\r\n },\r\n [snapApi],\r\n )\r\n\r\n useEffect(() => {\r\n if (drag == null) return\r\n let frame: number | null = null\r\n let lastPointer: { x: number; y: number } | null = null\r\n\r\n const flush = () => {\r\n frame = null\r\n const current = dragRef.current\r\n if (lastPointer == null || current == null) return\r\n const { preview } = compute(lastPointer.x, lastPointer.y, current)\r\n onPreview(preview)\r\n }\r\n\r\n const move = (event: PointerEvent) => {\r\n lastPointer = { x: event.clientX, y: event.clientY }\r\n if (frame != null) return\r\n frame = requestAnimationFrame(flush)\r\n }\r\n\r\n const finish = () => {\r\n if (frame != null) cancelAnimationFrame(frame)\r\n const current = dragRef.current\r\n if (current != null && lastPointer != null) {\r\n const { preview, inPointUs } = compute(lastPointer.x, lastPointer.y, current)\r\n onCommit({ ...preview, inPointUs })\r\n }\r\n dragRef.current = null\r\n lastPointer = null\r\n setDrag(null)\r\n onPreview(null)\r\n snapApi.reset()\r\n }\r\n\r\n window.addEventListener('pointermove', move)\r\n window.addEventListener('pointerup', finish)\r\n window.addEventListener('pointercancel', finish)\r\n return () => {\r\n if (frame != null) cancelAnimationFrame(frame)\r\n window.removeEventListener('pointermove', move)\r\n window.removeEventListener('pointerup', finish)\r\n window.removeEventListener('pointercancel', finish)\r\n }\r\n }, [compute, drag, onCommit, onPreview, snapApi])\r\n\r\n return {\r\n drag,\r\n beginMove: (event: React.PointerEvent, clip: TimelineClip) => begin(event, clip, 'move'),\r\n beginTrimStart: (event: React.PointerEvent, clip: TimelineClip) =>\r\n begin(event, clip, 'trim-start'),\r\n beginTrimEnd: (event: React.PointerEvent, clip: TimelineClip) => begin(event, clip, 'trim-end'),\r\n }\r\n}\r\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { useUiStoreApi } from '../../../context'\r\nimport { Layers } from 'lucide-react'\r\nimport type { MediaAsset, TimelineClip, Track } from '@cut-workbench/core'\r\nimport { getProjectDuration } from '@cut-workbench/core'\r\nimport { pxToUs, usToPx } from '@cut-workbench/core'\r\nimport { RULER_HEIGHT_PX, TRACK_HEADER_WIDTH_PX, TRACK_HEIGHT_PX } from '@/lib/constants'\r\nimport { cn } from '@/lib/cn'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { TimelineToolbar } from './TimelineToolbar'\r\nimport { Ruler } from './Ruler'\r\nimport { TrackHeader } from './TrackHeader'\r\nimport { ClipBlock, type ClipHandlers } from './ClipBlock'\r\nimport { ClipContextMenu } from './ClipContextMenu'\r\nimport { Playhead } from './Playhead'\r\nimport { Marquee } from './Marquee'\r\nimport { useTimelineZoom } from '../hooks/useTimelineZoom'\r\nimport { useTimelineDrag, type DragPreview } from '../hooks/useTimelineDrag'\r\nimport { MARQUEE_THRESHOLD_PX } from '../constants'\r\n\r\n/**\r\n * 时间轴主视图。\r\n *\r\n * 布局:\r\n * ┌ 工具条 ─────────────────────────────────────┐\r\n * │ [轨道头列 固定宽] │ [刻度尺 + 轨道内容 横向滚动] │\r\n * └─────────────────────────────────────────────┘\r\n *\r\n * 性能:\r\n * - ClipBlock 全 memo,拖拽只更新 preview state(不写 store);\r\n * - 刻度尺 / 播放头分别订阅,互不牵连;\r\n * - 吸附是 core 里的纯函数,候选点一次构建。\r\n */\r\nexport function Timeline() {\r\n const editor = useCutEditor()\r\n const uiApi = useUiStoreApi()\r\n const project = useEditor((s) => s.project)\r\n const assets = useEditor((s) => s.assets)\r\n const selectedClipIds = useEditor((s) => s.selectedClipIds)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n const setPlayhead = useEditor((s) => s.setPlayhead)\r\n const selectClips = useEditor((s) => s.selectClips)\r\n const splitClipsAt = useEditor((s) => s.splitClipsAt)\r\n const duplicateSelection = useEditor((s) => s.duplicateSelection)\r\n const removeClips = useEditor((s) => s.removeClips)\r\n const updateTrack = useEditor((s) => s.updateTrack)\r\n const removeTrack = useEditor((s) => s.removeTrack)\r\n const addTrack = useEditor((s) => s.addTrack)\r\n const compactTrack = useEditor((s) => s.compactTrack)\r\n const playing = useEditor((s) => s.playing)\r\n\r\n const [magnet, setMagnet] = useState(true)\r\n const [preview, setPreview] = useState<DragPreview | null>(null)\r\n const [scrollLeft, setScrollLeft] = useState(0)\r\n const [viewportWidth, setViewportWidth] = useState(800)\r\n\r\n const scrollRef = useRef<HTMLDivElement | null>(null)\r\n const tracksRef = useRef<HTMLDivElement | null>(null)\r\n\r\n const zoom = useTimelineZoom(scrollRef)\r\n const durationUs = getProjectDuration(project)\r\n\r\n const contentWidthPx = useMemo(\r\n () =>\r\n Math.max(\r\n viewportWidth + 240,\r\n Math.ceil((durationUs / 1_000_000) * zoom.pxPerSecond) + 320,\r\n ),\r\n [durationUs, viewportWidth, zoom.pxPerSecond],\r\n )\r\n\r\n const assetMap = useMemo(\r\n () => new Map<string, MediaAsset>(assets.map((a) => [a.id, a])),\r\n [assets],\r\n )\r\n const trackMap = useMemo(\r\n () => new Map<string, Track>(project.tracks.map((t) => [t.id, t])),\r\n [project.tracks],\r\n )\r\n const clipsByTrack = useMemo(() => {\r\n const map = new Map<string, TimelineClip[]>()\r\n for (const clip of project.clips) {\r\n const list = map.get(clip.trackId)\r\n if (list) list.push(clip)\r\n else map.set(clip.trackId, [clip])\r\n }\r\n return map\r\n }, [project.clips])\r\n\r\n useEffect(() => {\r\n const el = scrollRef.current\r\n if (el == null) return\r\n const ro = new ResizeObserver(() => setViewportWidth(el.clientWidth))\r\n ro.observe(el)\r\n setViewportWidth(el.clientWidth)\r\n return () => ro.disconnect()\r\n }, [])\r\n\r\n useEffect(() => {\r\n const el = scrollRef.current\r\n if (el == null) return\r\n const onScroll = () => {\r\n setScrollLeft(el.scrollLeft)\r\n uiApi.getState().setScrollLeftPx(el.scrollLeft)\r\n }\r\n el.addEventListener('scroll', onScroll, { passive: true })\r\n return () => el.removeEventListener('scroll', onScroll)\r\n }, [])\r\n\r\n const contentLeftPx = useCallback(\r\n () => tracksRef.current?.getBoundingClientRect().left ?? 0,\r\n [],\r\n )\r\n\r\n const resolveTrackAt = useCallback(\r\n (clientY: number): string | null => {\r\n const el = tracksRef.current\r\n if (el == null) return null\r\n const y = clientY - el.getBoundingClientRect().top\r\n const index = Math.floor(y / TRACK_HEIGHT_PX)\r\n return project.tracks[index]?.id ?? null\r\n },\r\n [project.tracks],\r\n )\r\n\r\n const handlePreview = useCallback((next: DragPreview | null) => {\r\n setPreview(next)\r\n }, [])\r\n\r\n const handleCommit = useCallback(\r\n (commit: {\r\n clipId: string\r\n trackId: string\r\n startUs: number\r\n durationUs: number\r\n inPointUs: number\r\n }) => {\r\n const clip = project.clips.find((c) => c.id === commit.clipId)\r\n if (!clip) return\r\n const patch: Partial<typeof clip> = {}\r\n if (commit.trackId !== clip.trackId) patch.trackId = commit.trackId\r\n if (commit.startUs !== clip.startUs) patch.startUs = commit.startUs as typeof clip.startUs\r\n if (commit.durationUs !== clip.durationUs) {\r\n patch.durationUs = commit.durationUs as typeof clip.durationUs\r\n }\r\n if (commit.inPointUs !== clip.inPointUs) {\r\n patch.inPointUs = commit.inPointUs as typeof clip.inPointUs\r\n }\r\n if (Object.keys(patch).length === 0) return\r\n editor.store.getState().updateClip(commit.clipId, patch, true)\r\n },\r\n [project.clips],\r\n )\r\n\r\n const drag = useTimelineDrag({\r\n pxPerSecond: zoom.pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled: magnet,\r\n onPreview: handlePreview,\r\n onCommit: handleCommit,\r\n })\r\n\r\n const playheadPx = usToPx(playheadUs, zoom.pxPerSecond)\r\n\r\n const seekToClientX = useCallback(\r\n (clientX: number) => {\r\n const left = tracksRef.current?.getBoundingClientRect().left ?? 0\r\n const scrolled = scrollRef.current?.scrollLeft ?? 0\r\n const us = pxToUs(clientX - left + scrolled, zoom.pxPerSecond)\r\n setPlayhead(Math.max(0, us))\r\n },\r\n [setPlayhead, zoom.pxPerSecond],\r\n )\r\n\r\n // 刻度尺拖拽:音频由 PreviewPanel 侧暂停(scrub 事件)\r\n const scrubCleanupRef = useRef<(() => void) | null>(null)\r\n const beginScrub = useCallback(\r\n (event: React.PointerEvent) => {\r\n window.dispatchEvent(new CustomEvent('cut-test:scrub-start'))\r\n seekToClientX(event.clientX)\r\n const move = (e: PointerEvent) => seekToClientX(e.clientX)\r\n const up = () => {\r\n window.removeEventListener('pointermove', move)\r\n window.removeEventListener('pointerup', up)\r\n window.dispatchEvent(new CustomEvent('cut-test:scrub-end'))\r\n scrubCleanupRef.current = null\r\n }\r\n scrubCleanupRef.current = up\r\n window.addEventListener('pointermove', move)\r\n window.addEventListener('pointerup', up)\r\n },\r\n [seekToClientX],\r\n )\r\n\r\n useEffect(() => {\r\n return () => {\r\n scrubCleanupRef.current?.()\r\n }\r\n }, [])\r\n\r\n // 框选\r\n const [marquee, setMarquee] = useState<{\r\n x1: number\r\n y1: number\r\n x2: number\r\n y2: number\r\n } | null>(null)\r\n\r\n const handleBackgroundPointerDown = useCallback(\r\n (event: React.PointerEvent) => {\r\n if (event.button !== 0) return\r\n const target = event.target as HTMLElement\r\n if (target.closest('[data-clip-block]')) return\r\n if (target.closest('[data-track-header]')) return\r\n const area = tracksRef.current\r\n if (area == null) return\r\n const rect = area.getBoundingClientRect()\r\n const startX = event.clientX\r\n const startY = event.clientY\r\n let moved = false\r\n const baseSelection = event.shiftKey ? selectedClipIds : []\r\n\r\n const move = (e: PointerEvent) => {\r\n if (!moved && Math.hypot(e.clientX - startX, e.clientY - startY) < MARQUEE_THRESHOLD_PX) {\r\n return\r\n }\r\n moved = true\r\n setMarquee({\r\n x1: startX - rect.left,\r\n y1: startY - rect.top,\r\n x2: e.clientX - rect.left,\r\n y2: e.clientY - rect.top,\r\n })\r\n }\r\n const up = (e: PointerEvent) => {\r\n window.removeEventListener('pointermove', move)\r\n window.removeEventListener('pointerup', up)\r\n setMarquee(null)\r\n if (!moved) {\r\n selectClips([])\r\n return\r\n }\r\n const minX = Math.min(startX, e.clientX) - rect.left\r\n const maxX = Math.max(startX, e.clientX) - rect.left\r\n const minY = Math.min(startY, e.clientY) - rect.top\r\n const maxY = Math.max(startY, e.clientY) - rect.top\r\n const hits: string[] = []\r\n project.tracks.forEach((track, index) => {\r\n const rowTop = index * TRACK_HEIGHT_PX\r\n const rowBottom = rowTop + TRACK_HEIGHT_PX\r\n if (rowBottom < minY || rowTop > maxY) return\r\n for (const clip of project.clips) {\r\n if (clip.trackId !== track.id) continue\r\n const leftPx = usToPx(clip.startUs, zoom.pxPerSecond)\r\n const rightPx = usToPx(clip.startUs + clip.durationUs, zoom.pxPerSecond)\r\n if (rightPx < minX || leftPx > maxX) continue\r\n hits.push(clip.id)\r\n }\r\n })\r\n selectClips(Array.from(new Set([...baseSelection, ...hits])))\r\n }\r\n window.addEventListener('pointermove', move)\r\n window.addEventListener('pointerup', up)\r\n },\r\n [project.clips, project.tracks, selectClips, selectedClipIds, zoom.pxPerSecond],\r\n )\r\n\r\n const zoomToFit = useCallback(() => {\r\n if (durationUs <= 0 || viewportWidth <= 0) return\r\n zoom.setZoom(Math.max(10, (viewportWidth - 60) / (durationUs / 1_000_000)))\r\n }, [durationUs, viewportWidth, zoom])\r\n\r\n const splitSelection = useCallback(() => {\r\n splitClipsAt(playheadUs, selectedClipIds.length > 0 ? selectedClipIds : undefined)\r\n }, [playheadUs, selectedClipIds, splitClipsAt])\r\n\r\n const splitClip = useCallback(\r\n (clip: TimelineClip) => {\r\n splitClipsAt(editor.store.getState().playheadUs, [clip.id])\r\n },\r\n [splitClipsAt],\r\n )\r\n\r\n const selectClip = useCallback(\r\n (clip: TimelineClip, additive: boolean) => {\r\n const state = editor.store.getState()\r\n if (additive) state.toggleSelectClip(clip.id)\r\n else if (!state.selectedClipIds.includes(clip.id)) state.selectClips([clip.id])\r\n },\r\n [],\r\n )\r\n\r\n const duplicateClip = useCallback(\r\n (clip: TimelineClip) => {\r\n const state = editor.store.getState()\r\n state.selectClips([clip.id])\r\n state.duplicateSelection(500_000)\r\n },\r\n [],\r\n )\r\n\r\n const removeClip = useCallback((clip: TimelineClip) => {\r\n editor.store.getState().removeClips([clip.id])\r\n }, [])\r\n\r\n const seekToClip = useCallback((clip: TimelineClip) => {\r\n editor.store.getState().setPlayhead(clip.startUs)\r\n }, [])\r\n\r\n // 稳定的 handler 集合:避免每次渲染生成新函数导致 ClipBlock 的 memo 失效\r\n // (200 clip 场景下这是拖拽是否流畅的关键)\r\n /** 右键作用于「当前选中的第一个 clip」(右键时会先选中它) */\r\n const firstSelectedClip = useCallback((): TimelineClip => {\r\n const state = editor.store.getState()\r\n const id = state.selectedClipIds[0]\r\n return state.project.clips.find((c) => c.id === id) ?? state.project.clips[0]!\r\n }, [])\r\n\r\n /** 右键:若命中 clip 则选中它;右键空白处不弹菜单 */\r\n const handleTrackContextMenu = useCallback((event: React.MouseEvent) => {\r\n const target = event.target as HTMLElement\r\n const el = target.closest('[data-clip-block]') as HTMLElement | null\r\n if (el == null) {\r\n event.preventDefault()\r\n return\r\n }\r\n const clipId = el.dataset.clipBlock\r\n if (clipId == null) return\r\n const state = editor.store.getState()\r\n if (!state.selectedClipIds.includes(clipId)) state.selectClips([clipId])\r\n }, [])\r\n\r\n const handlers = useMemo<ClipHandlers>(\r\n () => ({\r\n beginMove: drag.beginMove,\r\n beginTrimStart: drag.beginTrimStart,\r\n beginTrimEnd: drag.beginTrimEnd,\r\n select: selectClip,\r\n split: splitClip,\r\n duplicate: duplicateClip,\r\n remove: removeClip,\r\n showInfo: (clip) => selectClip(clip, false),\r\n seekTo: seekToClip,\r\n selectOnly: (clip) => selectClip(clip, false),\r\n }),\r\n [\r\n drag.beginMove,\r\n drag.beginTrimEnd,\r\n drag.beginTrimStart,\r\n duplicateClip,\r\n removeClip,\r\n selectClip,\r\n seekToClip,\r\n splitClip,\r\n ],\r\n )\r\n\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <TimelineToolbar\r\n playheadUs={playheadUs}\r\n durationUs={durationUs}\r\n pxPerSecond={zoom.pxPerSecond}\r\n selectionCount={selectedClipIds.length}\r\n magnetEnabled={magnet}\r\n onToggleMagnet={() => setMagnet((m) => !m)}\r\n onSplit={splitSelection}\r\n onDuplicate={() => duplicateSelection(500_000)}\r\n onRemove={() => removeClips(selectedClipIds)}\r\n onZoomIn={zoom.zoomIn}\r\n onZoomOut={zoom.zoomOut}\r\n onZoomToFit={zoomToFit}\r\n onAddTrack={(kind) => addTrack(kind)}\r\n canZoomIn={zoom.canZoomIn}\r\n canZoomOut={zoom.canZoomOut}\r\n />\r\n\r\n <div className=\"relative flex min-h-0 flex-1\">\r\n {/* 左列:轨道头(固定,不随横向滚动) */}\r\n <div\r\n className=\"thin-scrollbar flex shrink-0 flex-col overflow-y-auto border-r\"\r\n style={{ width: TRACK_HEADER_WIDTH_PX }}\r\n data-track-header-column\r\n >\r\n <div\r\n className=\"flex shrink-0 items-center border-b px-2 text-[10px] text-muted-foreground\"\r\n style={{ height: RULER_HEIGHT_PX }}\r\n >\r\n <Layers className=\"mr-1 size-3\" />\r\n 轨道\r\n </div>\r\n {project.tracks.map((track) => (\r\n <TrackHeader\r\n key={track.id}\r\n track={track}\r\n onToggleMute={() => updateTrack(track.id, { muted: !track.muted })}\r\n onToggleLock={() => updateTrack(track.id, { locked: !track.locked })}\r\n onToggleHidden={() => updateTrack(track.id, { hidden: !track.hidden })}\r\n onRemove={() => removeTrack(track.id)}\r\n />\r\n ))}\r\n <div className=\"flex gap-1 p-1.5\">\r\n <button\r\n type=\"button\"\r\n className=\"flex-1 rounded border border-dashed py-1 text-[10px] text-muted-foreground hover:border-primary/50 hover:text-foreground\"\r\n onClick={() => addTrack('visual')}\r\n >\r\n + 视频轨\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"flex-1 rounded border border-dashed py-1 text-[10px] text-muted-foreground hover:border-primary/50 hover:text-foreground\"\r\n onClick={() => addTrack('audio')}\r\n >\r\n + 音频轨\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* 右列:刻度尺 + 轨道内容(唯一横向滚动容器) */}\r\n <div\r\n ref={scrollRef}\r\n className=\"thin-scrollbar min-w-0 flex-1 overflow-x-auto overflow-y-auto\"\r\n >\r\n <div style={{ width: contentWidthPx }} className=\"relative\">\r\n <div\r\n className=\"sticky top-0 z-30 bg-timeline-bg\"\r\n style={{ width: contentWidthPx }}\r\n onPointerDown={beginScrub}\r\n >\r\n <Ruler\r\n pxPerSecond={zoom.pxPerSecond}\r\n widthPx={contentWidthPx}\r\n playheadPx={playheadPx}\r\n scrollLeftPx={scrollLeft}\r\n />\r\n </div>\r\n\r\n <ClipContextMenu\r\n onSplit={() => handlers.split(firstSelectedClip())}\r\n onDuplicate={() => handlers.duplicate(firstSelectedClip())}\r\n onRemove={() => handlers.remove(firstSelectedClip())}\r\n onShowInfo={() => undefined}\r\n >\r\n <div\r\n ref={tracksRef}\r\n className=\"relative select-none\"\r\n onPointerDown={handleBackgroundPointerDown}\r\n onContextMenu={handleTrackContextMenu}\r\n data-timeline-tracks\r\n >\r\n {project.tracks.map((track, index) => (\r\n <div\r\n key={track.id}\r\n className={cn(\r\n 'relative border-b border-timeline-grid',\r\n track.kind === 'audio' ? 'bg-timeline-track/30' : 'bg-timeline-track/50',\r\n track.hidden && 'opacity-40',\r\n )}\r\n style={{ height: TRACK_HEIGHT_PX }}\r\n data-track-row={track.id}\r\n >\r\n <div\r\n className=\"pointer-events-none absolute inset-0 opacity-40\"\r\n style={{\r\n backgroundImage:\r\n 'repeating-linear-gradient(to right, var(--timeline-grid) 0 1px, transparent 1px 80px)',\r\n }}\r\n />\r\n {(clipsByTrack.get(track.id) ?? []).map((clip) => {\r\n const isPreview = preview?.clipId === clip.id\r\n const isSelected = selectedClipIds.includes(clip.id)\r\n return (\r\n <ClipBlock\r\n key={clip.id}\r\n clip={clip}\r\n asset={assetMap.get(clip.assetId)}\r\n pxPerSecond={zoom.pxPerSecond}\r\n selected={isSelected}\r\n locked={track.locked}\r\n trackKind={track.kind}\r\n previewOffsetPx={\r\n isPreview && preview != null\r\n ? usToPx(preview.startUs - clip.startUs, zoom.pxPerSecond)\r\n : 0\r\n }\r\n previewWidthPx={\r\n isPreview && preview != null\r\n ? usToPx(preview.durationUs, zoom.pxPerSecond)\r\n : undefined\r\n }\r\n handlers={handlers}\r\n />\r\n )\r\n })}\r\n\r\n {index === project.tracks.length - 1 && preview != null && (\r\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-px bg-primary/40\" />\r\n )}\r\n </div>\r\n ))}\r\n\r\n {preview?.guidePx.map((px) => (\r\n <div\r\n key={`guide-${px}`}\r\n className=\"pointer-events-none absolute top-0 bottom-0 z-30 w-px bg-warning\"\r\n style={{ left: px }}\r\n />\r\n ))}\r\n\r\n {marquee != null && <Marquee {...marquee} />}\r\n </div>\r\n </ClipContextMenu>\r\n\r\n <Playhead px={playheadPx} playing={playing} />\r\n\r\n {project.clips.length === 0 && (\r\n <div className=\"pointer-events-none absolute inset-x-0 top-16 z-10\">\r\n <EmptyState\r\n icon={Layers}\r\n title=\"时间轴为空\"\r\n description=\"从左侧素材库双击素材,或点击卡片上的「+」加入时间轴\"\r\n compact\r\n />\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* 轨道顺序提示:轨道数组顺序即 zIndex(越靠下越在上层) */}\r\n <div className=\"flex h-5 shrink-0 items-center justify-end gap-2 border-t px-2 text-[10px] text-muted-foreground\">\r\n <span>Shift+点击多选 · 框选 · Ctrl+滚轮缩放</span>\r\n <button\r\n type=\"button\"\r\n className=\"hover:text-foreground\"\r\n onClick={() => {\r\n for (const track of trackMap.values()) compactTrack(track.id)\r\n }}\r\n >\r\n 轨道磁吸排序\r\n </button>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Tabs as TabsPrimitive } from \"radix-ui\"\n\nfunction Tabs({\n className,\n orientation = \"horizontal\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>) {\n return (\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n data-orientation={orientation}\n orientation={orientation}\n className={cn(\n \"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col\",\n className\n )}\n {...props}\n />\n )\n}\n\nconst tabsListVariants = cva(\n \"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none\",\n {\n variants: {\n variant: {\n default: \"bg-muted\",\n line: \"gap-1 bg-transparent\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction TabsList({\n className,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.List> &\n VariantProps<typeof tabsListVariants>) {\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n data-variant={variant}\n className={cn(tabsListVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nfunction TabsTrigger({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n return (\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n \"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n \"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent\",\n \"data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground\",\n \"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction TabsContent({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return (\n <TabsPrimitive.Content\n data-slot=\"tabs-content\"\n className={cn(\"flex-1 outline-none\", className)}\n {...props}\n />\n )\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Input }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon } from \"lucide-react\"\nimport { Select as SelectPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction Select({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Root>) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />\n}\n\nfunction SelectGroup({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nfunction SelectTrigger({\n className,\n size = \"default\",\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"default\"\n}) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon className=\"size-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n}\n\nfunction SelectContent({\n className,\n children,\n position = \"item-aligned\",\n align = \"center\",\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Content>) {\n // ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n // utility 不生效(下拉菜单没有背景、出现在视口外)。原因详见 dialog.tsx 的 DialogPortal。\n const container = usePortalContainer()\n return (\n <SelectPrimitive.Portal container={container ?? undefined}>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n align={align}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Label>) {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(\"px-2 py-1.5 text-xs text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n \"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2\",\n className\n )}\n {...props}\n >\n <span\n data-slot=\"select-item-indicator\"\n className=\"absolute right-2 flex size-3.5 items-center justify-center\"\n >\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\nfunction SelectSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Separator>) {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(\"pointer-events-none -mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n {...props}\n >\n <ChevronUpIcon className=\"size-4\" />\n </SelectPrimitive.ScrollUpButton>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n {...props}\n >\n <ChevronDownIcon className=\"size-4\" />\n </SelectPrimitive.ScrollDownButton>\n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n","import type { ReactNode } from 'react'\nimport { cn } from '@/lib/cn'\nimport { Label } from '@/components/ui/label'\n\ninterface FieldProps {\n label: string\n hint?: string\n children: ReactNode\n className?: string\n htmlFor?: string\n}\n\n/** 属性面板的一行:标签 + 控件(标签在左,控件占满剩余宽度) */\nexport function Field({ label, hint, children, className, htmlFor }: FieldProps) {\n return (\n <div className={cn('flex items-center gap-2', className)}>\n <Label htmlFor={htmlFor} className=\"w-16 shrink-0 text-[11px] text-muted-foreground\">\n {label}\n </Label>\n <div className=\"min-w-0 flex-1\">{children}</div>\n {hint != null && <span className=\"text-[10px] text-muted-foreground\">{hint}</span>}\n </div>\n )\n}\n\ninterface SectionProps {\n title: string\n children: ReactNode\n action?: ReactNode\n className?: string\n}\n\n/** 属性分组 */\nexport function Section({ title, children, action, className }: SectionProps) {\n return (\n <section className={cn('space-y-2', className)}>\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">\n {title}\n </h3>\n {action}\n </div>\n <div className=\"space-y-2\">{children}</div>\n </section>\n )\n}\n\ninterface NumberFieldProps {\n label: string\n value: number\n min?: number\n max?: number\n step?: number\n suffix?: string\n onChange: (value: number) => void\n disabled?: boolean\n}\n\n/** 数字输入:onChange 立即生效,但通过 number 输入避免字符串中间态 */\nexport function NumberField({\n label,\n value,\n min,\n max,\n step = 1,\n suffix,\n onChange,\n disabled,\n}: NumberFieldProps) {\n return (\n <Field label={label} hint={suffix}>\n <input\n type=\"number\"\n className=\"h-7 w-full rounded-md border bg-transparent px-2 text-xs tabular-nums outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 disabled:opacity-50\"\n value={Number.isFinite(value) ? Math.round(value * 100) / 100 : 0}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n onChange={(e) => {\n const next = Number(e.target.value)\n if (Number.isFinite(next)) onChange(next)\n }}\n />\n </Field>\n )\n}\n\ninterface SliderFieldProps {\n label: string\n value: number\n min: number\n max: number\n step: number\n /** 显示值格式化 */\n format?: (value: number) => string\n onChange: (value: number) => void\n disabled?: boolean\n}\n\n/** 滑杆 + 数值展示 */\nexport function SliderField({\n label,\n value,\n min,\n max,\n step,\n format,\n onChange,\n disabled,\n}: SliderFieldProps) {\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[11px] text-muted-foreground\">{label}</span>\n <span className=\"font-mono text-[10px] tabular-nums\">\n {format ? format(value) : value}\n </span>\n </div>\n <input\n type=\"range\"\n className=\"h-1 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary disabled:opacity-50\"\n min={min}\n max={max}\n step={step}\n value={value}\n disabled={disabled}\n onChange={(e) => onChange(Number(e.target.value))}\n aria-label={label}\n />\n </div>\n )\n}\n","import { FlipHorizontal2, FlipVertical2, Link2, Link2Off, RotateCcw } from 'lucide-react'\r\nimport type { TimelineClip } from '@cut-workbench/core'\r\nimport { Button } from '@/components/ui/button'\r\nimport { Input } from '@/components/ui/input'\r\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\r\nimport { formatSeconds } from '@/lib/format'\r\nimport { usToSec } from '@cut-workbench/core'\r\nimport { ratioLabel } from '@cut-workbench/core'\r\nimport { Field, NumberField, Section, SliderField } from './Field'\r\n\r\ninterface VideoPropsProps {\r\n clip: TimelineClip\r\n onPatch: (patch: Partial<TimelineClip>) => void\r\n onPatchTransform: (patch: Partial<TimelineClip['transform']>) => void\r\n /** 修改尺寸(自动处理宽高比锁定);未传时退化为 onPatchTransform */\r\n onPatchSize?: (axis: 'w' | 'h', value: number) => void\r\n onRebuild?: () => void\r\n}\r\n\r\n/**\r\n * 视频 / 图片共用的几何与外观属性。\r\n *\r\n * 变速会同时改变「时间轴可见时长」:为了让画面内容完整,变速时同步缩放 durationUs,\r\n * 保持源素材消费长度不变(duration = sourceSpan / rate)。\r\n */\r\nexport function VideoProps({ clip, onPatch, onPatchTransform, onPatchSize }: VideoPropsProps) {\r\n const handleRateChange = (rate: number) => {\r\n const safe = Math.max(0.1, Math.min(5, rate))\r\n const sourceSpan = Math.round(clip.durationUs * clip.playbackRate)\r\n onPatch({\r\n playbackRate: safe,\r\n durationUs: Math.max(100_000, Math.round(sourceSpan / safe)) as TimelineClip['durationUs'],\r\n })\r\n }\r\n\r\n const setSize = (axis: 'w' | 'h', value: number) => {\r\n if (onPatchSize) onPatchSize(axis, value)\r\n else onPatchTransform({ [axis]: Math.max(1, value) } as Partial<TimelineClip['transform']>)\r\n }\r\n const locked = clip.aspectLocked === true\r\n // 用整数比展示(640×360 → 16:9),比 1.778:1 直观\r\n const ratioText = ratioLabel(Math.round(clip.transform.w), Math.round(clip.transform.h))\r\n\r\n return (\r\n <>\r\n <Section title=\"位置与尺寸\">\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n <NumberField label=\"X\" value={clip.transform.x} onChange={(x) => onPatchTransform({ x })} />\r\n <NumberField label=\"Y\" value={clip.transform.y} onChange={(y) => onPatchTransform({ y })} />\r\n <NumberField\r\n label=\"宽\"\r\n value={clip.transform.w}\r\n min={1}\r\n onChange={(w) => setSize('w', w)}\r\n />\r\n <NumberField\r\n label=\"高\"\r\n value={clip.transform.h}\r\n min={1}\r\n onChange={(h) => setSize('h', h)}\r\n />\r\n </div>\r\n <div className=\"flex items-center justify-between\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={locked ? 'secondary' : 'ghost'}\r\n size=\"icon-xs\"\r\n onClick={() => onPatch({ aspectLocked: !locked })}\r\n aria-label={locked ? '解锁宽高比' : '锁定宽高比'}\r\n aria-pressed={locked}\r\n >\r\n {locked ? <Link2 className=\"size-3\" /> : <Link2Off className=\"size-3\" />}\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>\r\n {locked ? '已锁定宽高比:改宽会自动改高' : '锁定宽高比'}\r\n </TooltipContent>\r\n </Tooltip>\r\n <span className=\"font-mono text-[10px] text-muted-foreground tabular-nums\">\r\n {locked ? `已锁定 ${ratioText}` : '未锁定'}\r\n </span>\r\n </div>\r\n <SliderField\r\n label=\"旋转\"\r\n value={clip.transform.angle}\r\n min={-180}\r\n max={180}\r\n step={1}\r\n format={(v) => `${v}°`}\r\n onChange={(angle) => onPatchTransform({ angle })}\r\n />\r\n </Section>\r\n\r\n <Section\r\n title=\"外观\"\r\n action={\r\n <div className=\"flex gap-0.5\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={clip.flip === 'horizontal' ? 'secondary' : 'ghost'}\r\n size=\"icon-xs\"\r\n onClick={() =>\r\n onPatch({ flip: clip.flip === 'horizontal' ? null : 'horizontal' })\r\n }\r\n aria-label=\"水平翻转\"\r\n >\r\n <FlipHorizontal2 className=\"size-3\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>水平翻转</TooltipContent>\r\n </Tooltip>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={clip.flip === 'vertical' ? 'secondary' : 'ghost'}\r\n size=\"icon-xs\"\r\n onClick={() => onPatch({ flip: clip.flip === 'vertical' ? null : 'vertical' })}\r\n aria-label=\"垂直翻转\"\r\n >\r\n <FlipVertical2 className=\"size-3\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>垂直翻转</TooltipContent>\r\n </Tooltip>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon-xs\"\r\n onClick={() => onPatchTransform({ angle: 0 })}\r\n aria-label=\"重置旋转\"\r\n >\r\n <RotateCcw className=\"size-3\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>重置旋转</TooltipContent>\r\n </Tooltip>\r\n </div>\r\n }\r\n >\r\n <SliderField\r\n label=\"不透明度\"\r\n value={clip.opacity}\r\n min={0}\r\n max={1}\r\n step={0.01}\r\n format={(v) => `${Math.round(v * 100)}%`}\r\n onChange={(opacity) => onPatch({ opacity })}\r\n />\r\n </Section>\r\n\r\n <Section title=\"时间\">\r\n <Field label=\"开始\">\r\n <Input\r\n type=\"number\"\r\n className=\"h-7 text-xs tabular-nums\"\r\n value={formatSeconds(clip.startUs)}\r\n step={0.1}\r\n min={0}\r\n onChange={(e) => {\r\n const sec = Number(e.target.value)\r\n if (Number.isFinite(sec)) {\r\n onPatch({ startUs: Math.max(0, Math.round(sec * 1_000_000)) as TimelineClip['startUs'] })\r\n }\r\n }}\r\n />\r\n </Field>\r\n <Field label=\"时长\">\r\n <Input\r\n type=\"number\"\r\n className=\"h-7 text-xs tabular-nums\"\r\n value={formatSeconds(clip.durationUs)}\r\n step={0.1}\r\n min={0.1}\r\n onChange={(e) => {\r\n const sec = Number(e.target.value)\r\n if (Number.isFinite(sec) && sec > 0) {\r\n onPatch({ durationUs: Math.round(sec * 1_000_000) as TimelineClip['durationUs'] })\r\n }\r\n }}\r\n />\r\n </Field>\r\n <Field label=\"变速\" hint={clip.playbackRate !== 1 ? '会变调' : undefined}>\r\n <Select\r\n value={String(clip.playbackRate)}\r\n onValueChange={(value) => handleRateChange(Number(value))}\r\n >\r\n <SelectTrigger className=\"h-7 text-xs\">\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {[0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4].map((rate) => (\r\n <SelectItem key={rate} value={String(rate)} className=\"text-xs\">\r\n {rate}×\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </Field>\r\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\r\n 变速使用线性插值,音频音调会随之改变(WebAV 限制)。\r\n {usToSec(clip.durationUs).toFixed(2)}s 对应源素材{' '}\r\n {(usToSec(clip.durationUs) * clip.playbackRate).toFixed(2)}s。\r\n </p>\r\n </Section>\r\n </>\r\n )\r\n}\r\n","import { Volume2, VolumeX } from 'lucide-react'\nimport type { TimelineClip } from '@cut-workbench/core'\nimport { Button } from '@/components/ui/button'\nimport { Section, SliderField } from './Field'\nimport { formatSeconds } from '@/lib/format'\n\ninterface AudioPropsProps {\n clip: TimelineClip\n onPatch: (patch: Partial<TimelineClip>, mergeKey?: string) => void\n}\n\n/**\n * 音频属性:音量 / 静音 / 淡入淡出。\n *\n * 淡入淡出在预览里由 PreviewAudioEngine 的 GainNode 包络实现;\n * 导出时由 exporter 的 tickInterceptor 烘焙到 PCM(两条路径行为一致)。\n */\nexport function AudioProps({ clip, onPatch }: AudioPropsProps) {\n return (\n <>\n <Section\n title=\"音量\"\n action={\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={() => onPatch({ muted: !clip.muted })}\n aria-label={clip.muted ? '取消静音' : '静音'}\n >\n {clip.muted ? <VolumeX className=\"size-3\" /> : <Volume2 className=\"size-3\" />}\n </Button>\n }\n >\n <SliderField\n label=\"音量\"\n value={clip.muted ? 0 : clip.volume}\n min={0}\n max={1}\n step={0.01}\n format={(v) => `${Math.round(v * 100)}%`}\n onChange={(volume) => onPatch({ volume, muted: volume === 0 ? true : clip.muted })}\n />\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\n 多轨音频在合成时直接相加,建议每轨不超过 80% 以避免削波。\n </p>\n </Section>\n\n <Section title=\"淡入淡出\">\n <SliderField\n label=\"淡入\"\n value={clip.fadeInUs / 1_000_000}\n min={0}\n max={Math.max(0.5, clip.durationUs / 1_000_000 / 2)}\n step={0.05}\n format={(v) => `${v.toFixed(2)}s`}\n onChange={(sec) =>\n onPatch({ fadeInUs: Math.round(sec * 1_000_000) as TimelineClip['fadeInUs'] })\n }\n />\n <SliderField\n label=\"淡出\"\n value={clip.fadeOutUs / 1_000_000}\n min={0}\n max={Math.max(0.5, clip.durationUs / 1_000_000 / 2)}\n step={0.05}\n format={(v) => `${v.toFixed(2)}s`}\n onChange={(sec) =>\n onPatch({ fadeOutUs: Math.round(sec * 1_000_000) as TimelineClip['fadeOutUs'] })\n }\n />\n <p className=\"text-[10px] text-muted-foreground\">\n 片段时长 {formatSeconds(clip.durationUs)}s,淡入使用 setAnimation 线性插值。\n </p>\n </Section>\n </>\n )\n}\n","import type { TextStyle, TimelineClip } from '@cut-workbench/core'\r\nimport { Textarea } from '@/components/ui/textarea'\r\nimport { Input } from '@/components/ui/input'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select'\r\nimport { Field, NumberField, Section, SliderField } from './Field'\r\n\r\ninterface TextPropsProps {\r\n clip: TimelineClip\r\n onPatch: (patch: Partial<TimelineClip>) => void\r\n /** 文字内容或样式变化后需要重建 TextClip */\r\n onRebuild: () => void\r\n}\r\n\r\nconst FONT_FAMILIES = [\r\n { value: 'sans-serif', label: '无衬线' },\r\n { value: 'serif', label: '衬线' },\r\n { value: 'monospace', label: '等宽' },\r\n { value: \"'PingFang SC', sans-serif\", label: '苹方' },\r\n { value: \"'Microsoft YaHei', sans-serif\", label: '微软雅黑' },\r\n { value: \"'SimSun', serif\", label: '宋体' },\r\n]\r\n\r\n/** 文字素材属性:内容 / 字号 / 颜色 / 描边 / 阴影 / 对齐 */\r\nexport function TextProps({ clip, onPatch, onRebuild }: TextPropsProps) {\r\n const style: TextStyle = {\r\n fontSize: 64,\r\n color: '#ffffff',\r\n fontFamily: 'sans-serif',\r\n fontWeight: 600,\r\n fontStyle: 'normal',\r\n strokeColor: '#000000',\r\n strokeWidth: 0,\r\n shadowColor: 'rgba(0,0,0,0.6)',\r\n shadowBlur: 8,\r\n align: 'center',\r\n background: 'transparent',\r\n ...(clip.textStyle ?? {}),\r\n }\r\n\r\n const patchStyle = (next: Partial<TextStyle>) => {\r\n onPatch({ textStyle: { ...style, ...next } })\r\n onRebuild()\r\n }\r\n\r\n return (\r\n <>\r\n <Section title=\"内容\">\r\n <Textarea\r\n value={clip.text ?? ''}\r\n onChange={(e) => {\r\n onPatch({ text: e.target.value })\r\n onRebuild()\r\n }}\r\n rows={2}\r\n className=\"text-xs\"\r\n placeholder=\"输入文字…\"\r\n />\r\n </Section>\r\n\r\n <Section title=\"排版\">\r\n <Field label=\"字体\">\r\n <Select value={style.fontFamily} onValueChange={(v) => patchStyle({ fontFamily: v })}>\r\n <SelectTrigger className=\"h-7 text-xs\">\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {FONT_FAMILIES.map((font) => (\r\n <SelectItem key={font.value} value={font.value} className=\"text-xs\">\r\n {font.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </Field>\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n <NumberField\r\n label=\"字号\"\r\n value={style.fontSize}\r\n min={8}\r\n max={400}\r\n onChange={(fontSize) => patchStyle({ fontSize: Math.max(8, fontSize) })}\r\n />\r\n <NumberField\r\n label=\"粗细\"\r\n value={style.fontWeight}\r\n min={100}\r\n max={900}\r\n step={100}\r\n onChange={(fontWeight) => patchStyle({ fontWeight })}\r\n />\r\n </div>\r\n <SliderField\r\n label=\"对齐\"\r\n value={style.align === 'left' ? 0 : style.align === 'center' ? 1 : 2}\r\n min={0}\r\n max={2}\r\n step={1}\r\n format={(v) => (v === 0 ? '左' : v === 1 ? '中' : '右')}\r\n onChange={(v) => patchStyle({ align: v === 0 ? 'left' : v === 1 ? 'center' : 'right' })}\r\n />\r\n </Section>\r\n\r\n <Section title=\"颜色与描边\">\r\n <Field label=\"文字\">\r\n <ColorInput value={style.color} onChange={(color) => patchStyle({ color })} />\r\n </Field>\r\n <Field label=\"描边\">\r\n <ColorInput value={style.strokeColor} onChange={(strokeColor) => patchStyle({ strokeColor })} />\r\n </Field>\r\n <SliderField\r\n label=\"描边宽度\"\r\n value={style.strokeWidth}\r\n min={0}\r\n max={8}\r\n step={0.5}\r\n format={(v) => `${v}px`}\r\n onChange={(strokeWidth) => patchStyle({ strokeWidth })}\r\n />\r\n <Field label=\"背景\">\r\n <ColorInput\r\n value={style.background === 'transparent' ? '#000000' : style.background}\r\n onChange={(background) => patchStyle({ background })}\r\n withAlpha\r\n />\r\n </Field>\r\n </Section>\r\n\r\n <Section title=\"阴影\">\r\n <Field label=\"颜色\">\r\n <ColorInput\r\n value={style.shadowColor}\r\n onChange={(shadowColor) => patchStyle({ shadowColor })}\r\n />\r\n </Field>\r\n <SliderField\r\n label=\"模糊\"\r\n value={style.shadowBlur}\r\n min={0}\r\n max={40}\r\n step={1}\r\n format={(v) => `${v}px`}\r\n onChange={(shadowBlur) => patchStyle({ shadowBlur })}\r\n />\r\n </Section>\r\n </>\r\n )\r\n}\r\n\r\n/** 简易颜色输入(原生 color picker + 十六进制文本) */\r\nexport function ColorInput({\r\n value,\r\n onChange,\r\n withAlpha,\r\n}: {\r\n value: string\r\n onChange: (value: string) => void\r\n withAlpha?: boolean\r\n}) {\r\n void withAlpha\r\n const safeHex = /^#[0-9a-f]{6}$/i.test(value) ? value : '#ffffff'\r\n return (\r\n <div className=\"flex items-center gap-1.5\">\r\n <input\r\n type=\"color\"\r\n value={safeHex}\r\n onChange={(e) => onChange(e.target.value)}\r\n className=\"size-7 shrink-0 cursor-pointer rounded border bg-transparent p-0.5\"\r\n aria-label=\"选择颜色\"\r\n />\r\n <Input\r\n value={value}\r\n onChange={(e) => onChange(e.target.value)}\r\n className=\"h-7 font-mono text-[11px]\"\r\n aria-label=\"颜色值\"\r\n />\r\n </div>\r\n )\r\n}\r\n","import type { SubtitleStyle, TimelineClip } from '@cut-workbench/core'\r\nimport { Textarea } from '@/components/ui/textarea'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select'\r\nimport { formatSeconds } from '@/lib/format'\r\nimport { parseSrt } from '@cut-workbench/core'\r\nimport { Field, NumberField, Section, SliderField } from './Field'\r\nimport { ColorInput } from './TextProps'\r\n\r\ninterface SubtitlePropsProps {\r\n clip: TimelineClip\r\n onPatch: (patch: Partial<TimelineClip>) => void\r\n onRebuild: () => void\r\n}\r\n\r\nconst FONT_OPTIONS = [\r\n { value: 'sans-serif', label: '系统无衬线(推荐)' },\r\n { value: \"'Microsoft YaHei', sans-serif\", label: '微软雅黑' },\r\n { value: \"'PingFang SC', sans-serif\", label: '苹方' },\r\n { value: 'serif', label: '系统衬线' },\r\n]\r\n\r\n/**\r\n * 字幕属性(EmbedSubtitlesClip)。\r\n *\r\n * 注意:字号 / 字体 / 颜色等参数在 EmbedSubtitlesClip 构造时传入,修改后需要重建实例\r\n * (`onRebuild`),不会像普通 sprite 属性那样自动生效。\r\n */\r\nexport function SubtitleProps({ clip, onPatch, onRebuild }: SubtitlePropsProps) {\r\n const style: SubtitleStyle = {\r\n color: '#ffffff',\r\n textBgColor: null,\r\n fontFamily: 'sans-serif',\r\n fontSize: 30,\r\n strokeStyle: '#000000',\r\n lineWidth: null,\r\n bottomOffset: 30,\r\n ...(clip.subtitleStyle ?? {}),\r\n }\r\n\r\n const patchStyle = (next: Partial<SubtitleStyle>) => {\r\n onPatch({ subtitleStyle: { ...style, ...next } })\r\n onRebuild()\r\n }\r\n\r\n const cues = parseSrt(clip.srt ?? '')\r\n\r\n return (\r\n <>\r\n <Section title=\"SRT 内容\" action={<span className=\"text-[10px] text-muted-foreground\">{cues.length} 条</span>}>\r\n <Textarea\r\n value={clip.srt ?? ''}\r\n onChange={(e) => {\r\n onPatch({ srt: e.target.value })\r\n onRebuild()\r\n }}\r\n rows={7}\r\n className=\"font-mono text-[11px]\"\r\n placeholder={'1\\n00:00:00,000 --> 00:00:02,000\\n字幕内容'}\r\n />\r\n <p className=\"text-[10px] text-muted-foreground\">\r\n 片段时长 {formatSeconds(clip.durationUs)}s\r\n {cues.length > 0 && ` · 最后一条结束于 ${(cues[cues.length - 1]!.endMs / 1000).toFixed(2)}s`}\r\n </p>\r\n </Section>\r\n\r\n <Section title=\"样式\">\r\n <Field label=\"字体\">\r\n <Select value={style.fontFamily} onValueChange={(v) => patchStyle({ fontFamily: v })}>\r\n <SelectTrigger className=\"h-7 text-xs\">\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {FONT_OPTIONS.map((font) => (\r\n <SelectItem key={font.value} value={font.value} className=\"text-xs\">\r\n {font.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </Field>\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n <NumberField\r\n label=\"字号\"\r\n value={style.fontSize}\r\n min={8}\r\n max={200}\r\n onChange={(fontSize) => patchStyle({ fontSize: Math.max(8, fontSize) })}\r\n />\r\n <NumberField\r\n label=\"底距\"\r\n value={style.bottomOffset}\r\n min={0}\r\n max={600}\r\n onChange={(bottomOffset) => patchStyle({ bottomOffset: Math.max(0, bottomOffset) })}\r\n />\r\n </div>\r\n <Field label=\"文字色\">\r\n <ColorInput value={style.color} onChange={(color) => patchStyle({ color })} />\r\n </Field>\r\n <Field label=\"描边色\">\r\n <ColorInput value={style.strokeStyle} onChange={(strokeStyle) => patchStyle({ strokeStyle })} />\r\n </Field>\r\n <SliderField\r\n label=\"描边宽度\"\r\n value={style.lineWidth ?? 0}\r\n min={0}\r\n max={8}\r\n step={1}\r\n format={(v) => (v === 0 ? '无' : `${v}px`)}\r\n onChange={(lineWidth) => patchStyle({ lineWidth: lineWidth === 0 ? null : lineWidth })}\r\n />\r\n <Field label=\"底色\">\r\n <ColorInput\r\n value={style.textBgColor ?? '#000000'}\r\n onChange={(v) => patchStyle({ textBgColor: v })}\r\n />\r\n </Field>\r\n {style.textBgColor != null && (\r\n <button\r\n type=\"button\"\r\n className=\"text-[10px] text-primary hover:underline\"\r\n onClick={() => patchStyle({ textBgColor: null })}\r\n >\r\n 移除字幕底色\r\n </button>\r\n )}\r\n </Section>\r\n </>\r\n )\r\n}\r\n","import type { MediaAsset, TimelineClip } from '@cut-workbench/core'\nimport { SliderField, Section } from './Field'\nimport { VideoProps } from './VideoProps'\n\ninterface ImagePropsProps {\n clip: TimelineClip\n asset: MediaAsset | undefined\n onPatch: (patch: Partial<TimelineClip>, mergeKey?: string) => void\n onPatchTransform: (patch: Partial<TimelineClip['transform']>, field?: string) => void\n onPatchSize: (axis: 'w' | 'h', value: number) => void\n}\n\n/**\n * 图片属性 = 视频几何属性 + 静态素材专属的时长控制。\n *\n * ⚠️ ImgClip 的 meta.duration 是 Infinity,时间轴 clip 的 durationUs 是唯一的时长来源,\n * 所以这里必须能显式设置时长(否则 Combinator 无法确定结束时间)。\n */\nexport function ImageProps({ clip, asset, onPatch, onPatchTransform, onPatchSize }: ImagePropsProps) {\n return (\n <>\n <VideoProps\n clip={clip}\n onPatch={onPatch}\n onPatchTransform={onPatchTransform}\n onPatchSize={onPatchSize}\n />\n <Section title=\"静态素材\">\n <SliderField\n label=\"显示时长\"\n value={clip.durationUs / 1_000_000}\n min={0.5}\n max={60}\n step={0.5}\n format={(v) => `${v.toFixed(1)}s`}\n onChange={(sec) =>\n onPatch({ durationUs: Math.round(sec * 1_000_000) as TimelineClip['durationUs'] })\n }\n />\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\n {asset?.kind === 'image'\n ? '静态图片在 WebAV 中的 duration 为 Infinity,导出时必须由时间轴显式指定有限时长。'\n : '静态素材需要有限时长,否则 Combinator 无法推断结束时间。'}\n </p>\n </Section>\n </>\n )\n}\n","import { useCallback } from 'react'\r\nimport type { TimelineClip } from '@cut-workbench/core'\r\nimport { useEditor } from '@cut-workbench/react'\r\n\r\n/**\r\n * 属性面板的写操作集合。\r\n *\r\n * 所有修改都走 store 的 `updateClip`,由 preview 的「属性变化 → 重绘」路径生效;\r\n * 对需要重建 IClip 的属性(文字内容、SRT、入点),调用方额外触发 rebuild。\r\n *\r\n * **撤销粒度**:属性面板的滑块 / 数字输入会连续触发 onChange(一次拖动可能几十次)。\r\n * 若每次都压栈,用户按 Ctrl+Z 只会看到数值回退一小格,感觉「撤销不生效」。\r\n * 因此这里给每个字段生成合并键,让「同一字段的连续修改」合并成一步撤销。\r\n *\r\n * 字段名从 `patch` 的 key 自动推导,调用方无需关心。\r\n */\r\nexport function useClipInspector(clip: TimelineClip | null) {\r\n const updateClip = useEditor((s) => s.updateClip)\r\n\r\n /**\r\n * 修改片段顶层字段。\r\n *\r\n * 合并键按「片段 + 字段」生成;`transform` 这类嵌套对象再带上子字段名,\r\n * 保证「拖 X 再拖 Y」是两步,而「连续拖 X」是一步。\r\n */\r\n const patch = useCallback(\r\n (next: Partial<TimelineClip>) => {\r\n if (clip == null) return\r\n const keys = Object.keys(next)\r\n const field = keys.length === 1 ? keys[0] : keys.join('+')\r\n const nested =\r\n keys.length === 1 && next.transform != null\r\n ? `:${Object.keys(next.transform).join('+')}`\r\n : ''\r\n updateClip(clip.id, next, true, `${clip.id}:${field}${nested}`)\r\n },\r\n [clip, updateClip],\r\n )\r\n\r\n /** 修改 transform 字段(x / y / w / h / angle) */\r\n const patchTransform = useCallback(\r\n (next: Partial<TimelineClip['transform']>) => {\r\n if (clip == null) return\r\n const field = Object.keys(next).join('+')\r\n updateClip(\r\n clip.id,\r\n { transform: { ...clip.transform, ...next } },\r\n true,\r\n `${clip.id}:transform:${field}`,\r\n )\r\n },\r\n [clip, updateClip],\r\n )\r\n\r\n /**\r\n * 修改尺寸,尊重「锁定宽高比」。\r\n *\r\n * 锁定后:改宽 → 高按原比例跟随;改高 → 宽按原比例跟随。\r\n * 比例取**当前** transform 的 w/h(而不是素材原始分辨率),这样用户先自由\r\n * 改成 4:3、再上锁,后续锁定的是 4:3。\r\n *\r\n * @param axis 用户手动改的那个维度\r\n * @param value 新值(像素,已由调用方 clamp)\r\n */\r\n const patchSize = useCallback(\r\n (axis: 'w' | 'h', value: number) => {\r\n if (clip == null) return\r\n const safe = Math.max(1, value)\r\n const locked = clip.aspectLocked === true\r\n if (!locked || clip.transform.w <= 0 || clip.transform.h <= 0) {\r\n patchTransform({ [axis]: safe } as Partial<TimelineClip['transform']>)\r\n return\r\n }\r\n const ratio = clip.transform.w / clip.transform.h\r\n if (axis === 'w') {\r\n patchTransform({ w: safe, h: Math.max(1, Math.round(safe / ratio)) })\r\n } else {\r\n patchTransform({ h: safe, w: Math.max(1, Math.round(safe * ratio)) })\r\n }\r\n },\r\n [clip, patchTransform],\r\n )\r\n\r\n return { patch, patchTransform, patchSize }\r\n}\r\n","import { useCallback, useMemo } from 'react'\r\nimport { Film, Info, Music, Settings2, Subtitles, Type } from 'lucide-react'\r\nimport type { MediaAsset, TimelineClip } from '@cut-workbench/core'\r\nimport { getTrack } from '@cut-workbench/core'\r\nimport { formatDuration, formatResolution } from '@/lib/format'\r\nimport { ScrollArea } from '@/components/ui/scroll-area'\r\nimport { Separator } from '@/components/ui/separator'\r\nimport { Badge } from '@/components/ui/badge'\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { Timecode } from '@/components/common/Timecode'\r\nimport { VideoProps } from './VideoProps'\r\nimport { AudioProps } from './AudioProps'\r\nimport { TextProps } from './TextProps'\r\nimport { SubtitleProps } from './SubtitleProps'\r\nimport { ImageProps } from './ImageProps'\r\nimport { useClipInspector } from '../hooks/useClipInspector'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\n\r\nconst KIND_ICON = {\r\n video: Film,\r\n audio: Music,\r\n image: Film,\r\n text: Type,\r\n subtitle: Subtitles,\r\n} as const\r\n\r\nconst KIND_LABEL = {\r\n video: '视频',\r\n audio: '音频',\r\n image: '图片',\r\n text: '文字',\r\n subtitle: '字幕',\r\n} as const\r\n\r\nfunction useSelected(): { clip: TimelineClip | null; asset: MediaAsset | undefined; multi: boolean } {\r\n const selectedIds = useEditor((s) => s.selectedClipIds)\r\n const clips = useEditor((s) => s.project.clips)\r\n const assets = useEditor((s) => s.assets)\r\n return useMemo(() => {\r\n if (selectedIds.length !== 1) {\r\n return { clip: null, asset: undefined, multi: selectedIds.length > 1 }\r\n }\r\n const id = selectedIds[0]\r\n const clip = clips.find((c) => c.id === id) ?? null\r\n const asset = clip ? assets.find((a) => a.id === clip.assetId) : undefined\r\n return { clip, asset, multi: false }\r\n }, [assets, clips, selectedIds])\r\n}\r\n\r\n/**\r\n * 属性面板:根据选中 clip 的素材类型切换编辑表单。\r\n *\r\n * 所有修改通过 `updateClip` 写入;需要重建 IClip 的类型(文字 / 字幕)额外调用 rebuild。\r\n */\r\nexport function InspectorPanel() {\r\n const clips = useCutEditor().runtime.clips\r\n const { clip, asset, multi } = useSelected()\r\n const project = useEditor((s) => s.project)\r\n const { patch, patchTransform, patchSize } = useClipInspector(clip)\r\n\r\n const rebuild = useCallback(() => {\r\n if (clip == null || asset == null) return\r\n void clips.createInstance({\r\n timelineClipId: clip.id,\r\n assetId: clip.assetId,\r\n inPointUs: clip.inPointUs,\r\n durationUs: clip.durationUs * clip.playbackRate,\r\n text: clip.text,\r\n textStyle: clip.textStyle,\r\n srt: clip.srt,\r\n subtitleStyle: clip.subtitleStyle,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n })\r\n .catch((err: unknown) => {\r\n console.warn('[Inspector] rebuild failed', err)\r\n })\r\n }, [asset, clip, project.height, project.width])\r\n\r\n if (multi) {\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <PanelHeader title=\"多选\" />\r\n <EmptyState\r\n icon={Settings2}\r\n title=\"已选中多个片段\"\r\n description=\"多选状态下暂不支持批量编辑属性;可执行分割、删除、复制等操作。\"\r\n compact\r\n />\r\n </div>\r\n )\r\n }\r\n\r\n if (clip == null || asset == null) {\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <PanelHeader title=\"属性\" />\r\n <EmptyState\r\n icon={Settings2}\r\n title=\"未选中片段\"\r\n description=\"在时间轴或预览画布中选中一个片段以编辑属性\"\r\n compact\r\n />\r\n </div>\r\n )\r\n }\r\n\r\n const track = getTrack(project, clip.trackId)\r\n const Icon = KIND_ICON[asset.kind]\r\n\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <PanelHeader title=\"属性\" />\r\n\r\n <div className=\"shrink-0 space-y-1.5 border-b px-3 py-2.5\">\r\n <div className=\"flex items-center gap-2\">\r\n <Icon className=\"size-3.5 text-muted-foreground\" />\r\n <p className=\"min-w-0 flex-1 truncate text-xs font-medium\" title={asset.name}>\r\n {asset.name}\r\n </p>\r\n <Badge variant=\"outline\" className=\"text-[10px]\">\r\n {KIND_LABEL[asset.kind]}\r\n </Badge>\r\n </div>\r\n <div className=\"flex items-center justify-between text-[10px] text-muted-foreground\">\r\n <span>{track?.name ?? '—'}</span>\r\n <span className=\"font-mono tabular-nums\">\r\n <Timecode us={clip.startUs} /> → <Timecode us={clip.startUs + clip.durationUs} />\r\n </span>\r\n </div>\r\n <div className=\"flex items-center justify-between text-[10px] text-muted-foreground\">\r\n <span>\r\n {formatDuration(clip.durationUs)} · {formatResolution(asset.width, asset.height)}\r\n </span>\r\n <span>入点 {(clip.inPointUs / 1_000_000).toFixed(2)}s</span>\r\n </div>\r\n </div>\r\n\r\n <ScrollArea className=\"thin-scrollbar min-h-0 flex-1\">\r\n <div className=\"space-y-4 px-3 py-3\">\r\n {asset.kind === 'audio' || (track?.kind === 'audio' && asset.kind !== 'text') ? (\r\n <Tabs defaultValue=\"audio\">\r\n <TabsList className=\"w-full\">\r\n <TabsTrigger value=\"audio\" className=\"flex-1 text-xs\">\r\n 音频\r\n </TabsTrigger>\r\n <TabsTrigger value=\"time\" className=\"flex-1 text-xs\">\r\n 位置\r\n </TabsTrigger>\r\n </TabsList>\r\n <TabsContent value=\"audio\" className=\"space-y-4 pt-3\">\r\n <AudioProps clip={clip} onPatch={patch} />\r\n </TabsContent>\r\n <TabsContent value=\"time\" className=\"space-y-4 pt-3\">\r\n <VideoProps clip={clip} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n <AudioProps clip={clip} onPatch={patch} />\r\n </TabsContent>\r\n </Tabs>\r\n ) : asset.kind === 'text' ? (\r\n <>\r\n <TextProps clip={clip} onPatch={patch} onRebuild={rebuild} />\r\n <Separator />\r\n <VideoProps clip={clip} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n </>\r\n ) : asset.kind === 'subtitle' ? (\r\n <SubtitleProps clip={clip} onPatch={patch} onRebuild={rebuild} />\r\n ) : asset.kind === 'image' ? (\r\n <ImageProps clip={clip} asset={asset} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n ) : (\r\n <VideoProps clip={clip} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n )}\r\n\r\n <Separator />\r\n <div className=\"flex items-start gap-1.5 text-[10px] leading-relaxed text-muted-foreground\">\r\n <Info className=\"mt-0.5 size-3 shrink-0\" />\r\n <span>\r\n 属性修改实时反映到预览。导出时以时间轴数据为准,预览与导出使用同一套\r\n Combinator 参数。\r\n </span>\r\n </div>\r\n </div>\r\n </ScrollArea>\r\n </div>\r\n )\r\n}\r\n\r\nfunction PanelHeader({ title }: { title: string }) {\r\n return (\r\n <div className=\"flex h-9 shrink-0 items-center border-b px-3\">\r\n <h2 className=\"text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\r\n {title}\r\n </h2>\r\n </div>\r\n )\r\n}\r\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Switch as SwitchPrimitive } from \"radix-ui\"\n\nfunction Switch({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof SwitchPrimitive.Root> & {\n size?: \"sm\" | \"default\"\n}) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n data-size={size}\n className={cn(\n \"peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80\",\n className\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground\"\n )}\n />\n </SwitchPrimitive.Root>\n )\n}\n\nexport { Switch }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Progress as ProgressPrimitive } from \"radix-ui\"\n\nfunction Progress({\n className,\n value,\n ...props\n}: React.ComponentProps<typeof ProgressPrimitive.Root>) {\n return (\n <ProgressPrimitive.Root\n data-slot=\"progress\"\n className={cn(\n \"relative h-2 w-full overflow-hidden rounded-full bg-primary/20\",\n className\n )}\n {...props}\n >\n <ProgressPrimitive.Indicator\n data-slot=\"progress-indicator\"\n className=\"h-full w-full flex-1 bg-primary transition-all\"\n style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n />\n </ProgressPrimitive.Root>\n )\n}\n\nexport { Progress }\n","/**\n * 导出 Worker 的 URL(包产物形态)。\n *\n * 为什么单独抽一个模块:UMD 构建会把 Worker 内联成字符串,不需要也不应该\n * 保留随包的 worker 文件;而 ESM / CJS 产物需要一个**相对于 dist/index.js**\n * 的 URL。两者靠 `build-umd.mjs` 的 alias 把本模块换成 `worker-url.umd.ts`\n * 来切换,这样 UMD 里连 `new URL(...)` 都不会被求值。\n *\n * `?worker&url` 让 Vite 把 Worker 作为独立 chunk 产出,并给出它的 URL;\n * 配合 `base: './'`,ESM 产物里是 `new URL(\"assets/export.worker-xxx.js\", import.meta.url).href`、\n * CJS 产物里是 `pathToFileURL(__dirname + \"/assets/...\")` —— 装进 node_modules 后\n * 都能正确解析。若用默认的绝对 base,产物会是 `\"/assets/...\"`,解析到使用方\n * 站点的根目录,Worker 必然 404。\n */\nimport workerUrl from './export.worker.ts?worker&url'\n\nexport const exportWorkerUrl: string = workerUrl\n","/**\n * 导出 Worker 工厂(构建产物形态相关)。\n *\n * 同一个导出流程要跑在两种产物里:\n * - **ESM / CJS**:Worker 是独立 chunk,URL 由 `worker-url.ts` 在构建期给出;\n * - **UMD 单文件**:没有独立 chunk,`import.meta.url` 也不可靠,必须内联成\n * base64 blob URL。\n *\n * 这里用构建期常量 `__CUT_INLINE_WORKER__` 二选一,避免运行时探测:\n * 探测(例如判断是否 UMD)在打包后往往会得到错误结论,而且两种路径的\n * 打包行为本身不同 —— Worker 是否被抽取成独立 chunk 是构建期决定的。\n *\n * 接入方还可以通过 `HostBridge.createExportWorker` 完全接管(例如 Electron\n * 里换成 utilityProcess,或严格 CSP 下换成自定义 URL)。\n */\n\ndeclare const __CUT_INLINE_WORKER__: boolean\n\nimport { exportWorkerUrl } from './worker-url'\nimport type { HostBridge } from '@cut-workbench/core'\n\n/** 内联 Worker 的源码(仅内联模式注入) */\ndeclare const __CUT_WORKER_SOURCE__: string | undefined\n\nlet cachedBlobUrl: string | null = null\n\n/**\n * 创建导出 Worker。\n *\n * @throws 内联模式下缺失 Worker 源码时抛出(构建配置错误,应在构建期暴露)\n */\nexport function createExportWorker(bridge: HostBridge): Worker {\n // 1. 宿主完全接管\n const custom = bridge.createExportWorker?.()\n if (custom != null) return custom\n\n // 2. 内联模式(UMD)\n if (typeof __CUT_INLINE_WORKER__ !== 'undefined' && __CUT_INLINE_WORKER__) {\n if (typeof __CUT_WORKER_SOURCE__ === 'undefined' || __CUT_WORKER_SOURCE__ === '') {\n throw new Error('[cut-workbench] 内联 Worker 模式缺少 Worker 源码,请检查构建配置')\n }\n cachedBlobUrl ??= URL.createObjectURL(\n new Blob([__CUT_WORKER_SOURCE__], { type: 'text/javascript' }),\n )\n return new Worker(cachedBlobUrl, { name: 'cut-workbench-export' })\n }\n\n // 3. 默认:独立 chunk(`exportWorkerUrl` 在构建期已按包内 dist 目录解析成绝对 URL)\n return new Worker(exportWorkerUrl, {\n type: 'module',\n name: 'cut-workbench-export',\n })\n}\n\n/** 释放缓存的 blob URL(dispose 时调用) */\nexport function releaseExportWorker(): void {\n if (cachedBlobUrl == null) return\n URL.revokeObjectURL(cachedBlobUrl)\n cachedBlobUrl = null\n}\n","/**\r\n * 导出编排:构建 payload、派发 Worker、回退主线程、下载与修复时长。\r\n *\r\n * 分层:本模块在 features 层,不直接 import `@webav/*`;\r\n * WebAV 调用全部经 `@/core/webav/exporter` 与 Worker runtime。\r\n *\r\n * 两条路径的选择:\r\n * - 默认走 Worker(不阻塞 UI);\r\n * - 含文字片段时回退主线程(文字渲染依赖 DOM);\r\n * - 含音频 / 视频音轨时,主线程会先解码 PCM 再传给 Worker\r\n * (Worker 内没有 AudioContext,AudioClip 无法自行解码)。\r\n */\r\n\r\nimport {\r\n checkExportSupport,\r\n exportProject,\r\n type ExportClipInput,\r\n} from '@cut-workbench/core/engine'\r\nimport { loadPcmForAsset } from '@cut-workbench/core/engine'\r\nimport type { ClipRegistry } from '@cut-workbench/core/engine'\r\nimport type {\r\n WorkerAssetPayload,\r\n WorkerResponse,\r\n WorkerTimelinePayload,\r\n} from '@cut-workbench/core/engine'\r\nimport type { ExportSettings, MediaAsset, Project } from '@cut-workbench/core'\r\n\r\nexport interface ExportPlanOptions {\r\n /** clip 实例注册表(由调用方从编辑器 runtime 取,避免本模块依赖 React) */\r\n registry: ClipRegistry\r\n /**\r\n * Worker 工厂。由调用方注入(通常来自 `editor.bridge`),\r\n * 使本模块不依赖具体宿主与构建形态。\r\n */\r\n createWorker: () => Worker\r\n project: Project\r\n assets: MediaAsset[]\r\n settings: ExportSettings\r\n /** 导出上限(微秒),默认全片 */\r\n maxTimeUs?: number\r\n}\r\n\r\nexport interface ExportResult {\r\n blob: Blob\r\n elapsedMs: number\r\n /** 走的是 Worker 还是主线程回退 */\r\n mode: 'worker' | 'main'\r\n skipped: Array<{ clipId: string; reason: string }>\r\n}\r\n\r\n/** 判断是否可以用 Worker 导出(文字素材依赖 DOM) */\r\nexport function canUseWorker(project: Project, assets: MediaAsset[]): boolean {\r\n const kinds = new Map(assets.map((a) => [a.id, a.kind]))\r\n return !project.clips.some((clip) => kinds.get(clip.assetId) === 'text')\r\n}\r\n\r\n/**\r\n * 构建 Worker 载荷。\r\n *\r\n * 音频素材需要主线程预解码 PCM(Worker 无 AudioContext)。为避免无谓解码,\r\n * 只有「音频轨素材」或「含音轨且会被播放的视频」才解码。\r\n */\r\nexport async function buildWorkerPayload(\r\n registry: ClipRegistry,\r\n project: Project,\r\n assets: MediaAsset[],\r\n settings: ExportSettings,\r\n maxTimeUs?: number,\r\n): Promise<{ assets: WorkerAssetPayload[]; timeline: WorkerTimelinePayload; missing: string[] }> {\r\n const usedAssetIds = new Set(project.clips.map((c) => c.assetId))\r\n const trackKindOf = new Map(project.tracks.map((t) => [t.id, t.kind]))\r\n const clipKinds = new Map<string, 'visual' | 'audio'>()\r\n for (const clip of project.clips) {\r\n clipKinds.set(clip.assetId, trackKindOf.get(clip.trackId) ?? 'visual')\r\n }\r\n\r\n const missing: string[] = []\r\n const payloadAssets: WorkerAssetPayload[] = []\r\n\r\n for (const asset of assets) {\r\n if (!usedAssetIds.has(asset.id)) continue\r\n const file = registry.getFile(asset.id)\r\n if (file == null && asset.kind !== 'text' && asset.kind !== 'subtitle') {\r\n missing.push(asset.name)\r\n continue\r\n }\r\n\r\n let pcm: Float32Array[] | undefined\r\n const needsAudio =\r\n settings.includeAudio &&\r\n (asset.kind === 'audio' || (asset.kind === 'video' && asset.hasAudio))\r\n if (needsAudio) {\r\n pcm = await loadPcmForAsset(registry, asset.id)\r\n }\r\n\r\n payloadAssets.push({\r\n id: asset.id,\r\n kind: asset.kind,\r\n file,\r\n width: asset.width,\r\n height: asset.height,\r\n durationUs: asset.durationUs,\r\n hasAudio: asset.hasAudio,\r\n pcm,\r\n text: asset.text,\r\n textStyle: asset.textStyle,\r\n srt: asset.srt,\r\n subtitleStyle: asset.subtitleStyle,\r\n })\r\n }\r\n\r\n const kindOf = new Map(assets.map((a) => [a.id, a.kind]))\r\n const clips = project.clips.map((clip) => ({\r\n clip,\r\n kind: kindOf.get(clip.assetId) ?? 'video',\r\n }))\r\n\r\n return {\r\n assets: payloadAssets,\r\n timeline: {\r\n width: settings.width,\r\n height: settings.height,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n fps: settings.fps,\r\n bitrate: settings.bitrate,\r\n includeAudio: settings.includeAudio,\r\n bgColor: '#000000',\r\n maxTimeUs,\r\n clips,\r\n },\r\n missing,\r\n }\r\n}\r\n\r\n/**\r\n * 主线程回退导出:为每个 clip 从 registry 建实例后交给 core exporter。\r\n *\r\n * 注意:实例用 `export:<clipId>` 作为临时 key 注册进 registry,导出结束后统一销毁,\r\n * 避免污染预览用的实例(两者不可共用,因为共享 tick 状态)。\r\n */\r\nasync function exportOnMainThread(\r\n registry: ClipRegistry,\r\n project: Project,\r\n settings: ExportSettings,\r\n onProgress: (progress: number) => void,\r\n shouldCancel: () => boolean,\r\n): Promise<ExportResult> {\r\n const trackMap = new Map(project.tracks.map((t) => [t.id, t]))\r\n const inputs: ExportClipInput[] = []\r\n const skipped: Array<{ clipId: string; reason: string }> = []\r\n const tempIds: string[] = []\r\n\r\n try {\r\n for (const clip of project.clips) {\r\n if (shouldCancel()) throw new Error('导出已取消')\r\n const track = trackMap.get(clip.trackId)\r\n if (!track || track.hidden) continue\r\n const asset = registry.getAsset(clip.assetId)\r\n if (!asset) {\r\n skipped.push({ clipId: clip.id, reason: '素材缺失' })\r\n continue\r\n }\r\n const tempId = `export:${clip.id}`\r\n try {\r\n const instance = await registry.createInstance({\r\n timelineClipId: tempId,\r\n assetId: clip.assetId,\r\n inPointUs: clip.inPointUs,\r\n durationUs: clip.durationUs * clip.playbackRate,\r\n text: clip.text,\r\n textStyle: clip.textStyle,\r\n srt: clip.srt,\r\n subtitleStyle: clip.subtitleStyle,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n })\r\n tempIds.push(tempId)\r\n inputs.push({ clip, instance, kind: asset.kind, track })\r\n } catch (err) {\r\n skipped.push({\r\n clipId: clip.id,\r\n reason: err instanceof Error ? err.message : String(err),\r\n })\r\n }\r\n }\r\n\r\n if (inputs.length === 0) throw new Error('时间轴为空,无法导出')\r\n\r\n const startedAt = performance.now()\r\n const { buffer, skipped: buildSkipped } = await exportProject(\r\n inputs,\r\n {\r\n width: settings.width,\r\n height: settings.height,\r\n fps: settings.fps,\r\n bitrate: settings.bitrate,\r\n includeAudio: settings.includeAudio,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n },\r\n onProgress,\r\n )\r\n const blob = new Blob([buffer], { type: 'video/mp4' })\r\n return {\r\n blob,\r\n elapsedMs: performance.now() - startedAt,\r\n mode: 'main',\r\n skipped: [...skipped, ...buildSkipped],\r\n }\r\n } finally {\r\n for (const id of tempIds) registry.destroyInstance(id)\r\n }\r\n}\r\n\r\nexport interface ExportHandle {\r\n promise: Promise<ExportResult>\r\n cancel: () => void\r\n}\r\n\r\n/**\r\n * 启动导出。\r\n *\r\n * 优先走 Worker(不阻塞 UI);含文字片段或 Worker 失败时回退主线程。\r\n */\r\nexport function startExport(\r\n options: ExportPlanOptions,\r\n onProgress: (progress: number) => void,\r\n): ExportHandle {\r\n const { registry, createWorker, project, assets, settings, maxTimeUs } = options\r\n let cancelled = false\r\n let worker: Worker | null = null\r\n\r\n const cancel = () => {\r\n cancelled = true\r\n if (worker != null) {\r\n worker.postMessage({ type: 'cancel' })\r\n worker.terminate()\r\n worker = null\r\n }\r\n }\r\n\r\n const promise = (async (): Promise<ExportResult> => {\r\n const supported = await checkExportSupport({\r\n width: settings.width,\r\n height: settings.height,\r\n bitrate: settings.bitrate,\r\n })\r\n if (!supported) {\r\n throw new Error(\r\n `当前设备不支持编码 ${settings.width}×${settings.height} @ ${(settings.bitrate / 1e6).toFixed(1)}Mbps,请降低分辨率或码率`,\r\n )\r\n }\r\n\r\n if (!canUseWorker(project, assets)) {\r\n return exportOnMainThread(registry, project, settings, onProgress, () => cancelled)\r\n }\r\n\r\n try {\r\n return await exportViaWorker(\r\n createWorker,\r\n registry,\r\n project,\r\n assets,\r\n settings,\r\n maxTimeUs,\r\n onProgress,\r\n () => cancelled,\r\n (w) => {\r\n worker = w\r\n },\r\n )\r\n } catch (err) {\r\n if (cancelled) throw new Error('导出已取消', { cause: err })\r\n console.warn('[export] Worker 导出失败,回退主线程', err)\r\n return exportOnMainThread(registry, project, settings, onProgress, () => cancelled)\r\n }\r\n })()\r\n\r\n return { promise, cancel }\r\n}\r\n\r\nasync function exportViaWorker(\r\n createWorker: () => Worker,\r\n registry: ClipRegistry,\r\n project: Project,\r\n assets: MediaAsset[],\r\n settings: ExportSettings,\r\n maxTimeUs: number | undefined,\r\n onProgress: (progress: number) => void,\r\n shouldCancel: () => boolean,\r\n register: (worker: Worker) => void,\r\n): Promise<ExportResult> {\r\n const { assets: payloadAssets, timeline, missing } = await buildWorkerPayload(\r\n registry,\r\n project,\r\n assets,\r\n settings,\r\n maxTimeUs,\r\n )\r\n if (missing.length > 0) {\r\n throw new Error(`以下素材缺少源文件,无法导出:${missing.join('、')}`)\r\n }\r\n\r\n return new Promise<ExportResult>((resolve, reject) => {\r\n const worker = createWorker()\r\n register(worker)\r\n const startedAt = performance.now()\r\n\r\n worker.onmessage = (event: MessageEvent<WorkerResponse>) => {\r\n const message = event.data\r\n if (message.type === 'progress') {\r\n onProgress(message.progress)\r\n return\r\n }\r\n if (message.type === 'done') {\r\n const blob = new Blob([message.buffer], { type: 'video/mp4' })\r\n worker.terminate()\r\n resolve({\r\n blob,\r\n elapsedMs: performance.now() - startedAt,\r\n mode: 'worker',\r\n skipped: message.skipped ?? [],\r\n })\r\n return\r\n }\r\n if (message.type === 'error') {\r\n worker.terminate()\r\n reject(new Error(message.message))\r\n }\r\n }\r\n\r\n worker.onerror = (event) => {\r\n worker.terminate()\r\n reject(new Error(event.message || '导出 Worker 异常'))\r\n }\r\n\r\n if (shouldCancel()) {\r\n worker.terminate()\r\n reject(new Error('导出已取消'))\r\n return\r\n }\r\n\r\n worker.postMessage({ type: 'export', assets: payloadAssets, timeline, fixDuration: true })\r\n })\r\n}\r\n\r\n/** 触发下载 */\r\nexport function downloadBlob(blob: Blob, filename: string): void {\r\n const url = URL.createObjectURL(blob)\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n document.body.appendChild(link)\r\n link.click()\r\n link.remove()\r\n // 立即 revoke 会让部分浏览器来不及下载,延迟释放\r\n window.setTimeout(() => URL.revokeObjectURL(url), 10_000)\r\n}\r\n\r\nexport function defaultFilename(): string {\r\n const now = new Date()\r\n const pad = (n: number) => String(n).padStart(2, '0')\r\n return `cut-test-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.mp4`\r\n}\r\n","import { useCallback, useRef, useState } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport { createExportWorker } from '../worker-factory'\r\nimport type { ExportSettings, ExportPreset, MediaAsset, Project } from '@cut-workbench/core'\r\nimport { DEFAULT_BITRATE } from '@/lib/constants'\r\nimport { computeTimelineEnd } from '@cut-workbench/core/engine'\r\nimport { fixFMP4DurationStream } from '@cut-workbench/core/engine'\r\nimport {\r\n defaultFilename,\r\n downloadBlob,\r\n startExport,\r\n type ExportResult,\r\n} from '../export-runner'\r\n\r\nexport type ExportPhase = 'idle' | 'preparing' | 'running' | 'done' | 'error' | 'cancelled'\r\n\r\nexport interface UseExportState {\r\n phase: ExportPhase\r\n progress: number\r\n error: string | null\r\n result: ExportResult | null\r\n /** 正在修复时长 */\r\n fixing: boolean\r\n}\r\n\r\nexport interface ExportConfigInput {\r\n preset: ExportPreset\r\n fps: ExportSettings['fps']\r\n bitrate: number\r\n includeAudio: boolean\r\n}\r\n\r\n/** 按预设推导输出分辨率(preset = source 时取工程尺寸并向下对齐到偶数) */\r\nexport function resolveDimensions(\r\n preset: ExportPreset,\r\n project: Project,\r\n): { width: number; height: number } {\r\n const even = (n: number) => Math.max(2, Math.round(n / 2) * 2)\r\n switch (preset) {\r\n case '1080p':\r\n return { width: 1920, height: 1080 }\r\n case '720p':\r\n return { width: 1280, height: 720 }\r\n case 'source':\r\n default:\r\n return { width: even(project.width), height: even(project.height) }\r\n }\r\n}\r\n\r\nexport function buildExportSettings(\r\n config: ExportConfigInput,\r\n project: Project,\r\n): ExportSettings {\r\n const { width, height } = resolveDimensions(config.preset, project)\r\n return {\r\n preset: config.preset,\r\n width,\r\n height,\r\n fps: config.fps,\r\n bitrate: config.bitrate,\r\n includeAudio: config.includeAudio,\r\n }\r\n}\r\n\r\n/**\r\n * 导出状态机。\r\n *\r\n * - `start` 触发导出(Worker 优先,文字素材自动回退主线程);\r\n * - `cancel` 终止 Worker 并丢弃结果,不残留悬挂线程;\r\n * - `fixDuration` 对已有结果调用 fixFMP4Duration 修正 moov 时长。\r\n */\r\nexport function useExport(project: Project, assets: MediaAsset[]) {\r\n const editor = useCutEditor()\r\n // 运行时实例在编辑器生命周期内恒定;解构出 clips 供导出使用\r\n const clips = editor.runtime.clips\r\n const [state, setState] = useState<UseExportState>({\r\n phase: 'idle',\r\n progress: 0,\r\n error: null,\r\n result: null,\r\n fixing: false,\r\n })\r\n const handleRef = useRef<ReturnType<typeof startExport> | null>(null)\r\n\r\n const start = useCallback(\r\n async (config: ExportConfigInput) => {\r\n if (handleRef.current != null) return\r\n const settings = buildExportSettings(config, project)\r\n setState({ phase: 'preparing', progress: 0, error: null, result: null, fixing: false })\r\n\r\n const maxTimeUs = computeTimelineEnd(project.clips)\r\n const handle = startExport(\r\n {\r\n registry: clips,\r\n createWorker: () => createExportWorker(editor.bridge),\r\n project,\r\n assets,\r\n settings,\r\n maxTimeUs,\r\n },\r\n (progress) => {\r\n setState((prev) =>\r\n prev.phase === 'running' || prev.phase === 'preparing'\r\n ? { ...prev, phase: 'running', progress }\r\n : prev,\r\n )\r\n },\r\n )\r\n handleRef.current = handle\r\n\r\n try {\r\n const result = await handle.promise\r\n if (handleRef.current !== handle) return\r\n handleRef.current = null\r\n setState({ phase: 'done', progress: 1, error: null, result, fixing: false })\r\n } catch (err) {\r\n if (handleRef.current !== handle) return\r\n handleRef.current = null\r\n const message = err instanceof Error ? err.message : String(err)\r\n const cancelled = message.includes('取消')\r\n setState({\r\n phase: cancelled ? 'cancelled' : 'error',\r\n progress: 0,\r\n error: cancelled ? null : message,\r\n result: null,\r\n fixing: false,\r\n })\r\n }\r\n },\r\n [assets, project],\r\n )\r\n\r\n const cancel = useCallback(() => {\r\n handleRef.current?.cancel()\r\n handleRef.current = null\r\n setState((prev) => ({ ...prev, phase: 'cancelled', error: null }))\r\n }, [])\r\n\r\n const reset = useCallback(() => {\r\n setState({ phase: 'idle', progress: 0, error: null, result: null, fixing: false })\r\n }, [])\r\n\r\n /**\r\n * 把导出结果交给宿主。\r\n *\r\n * 优先走 `HostBridge.files.saveBlob`:Electron 里宿主会弹保存对话框让用户选路径,\r\n * 浏览器里宿主可能上传到自己的服务。只有宿主没有实现(或明确返回未处理)时,\r\n * 才回退到 `<a download>`。\r\n *\r\n * 不接管会导致桌面端表现错误:Electron 的 `<a download>` 会静默落到下载目录,\r\n * 用户既看不到存哪了,也没法选位置。\r\n */\r\n const download = useCallback(\r\n (filename?: string) => {\r\n setState((prev) => {\r\n if (prev.result != null) {\r\n const name = filename ?? defaultFilename()\r\n const save = editor.bridge.files?.saveBlob\r\n if (save == null) {\r\n downloadBlob(prev.result.blob, name)\r\n return prev\r\n }\r\n void save\r\n .call(editor.bridge.files, prev.result.blob, name)\r\n .then((result) => {\r\n // 用户取消不算失败,不提示;真正的错误才透出去\r\n if (!result.ok && !result.cancelled) {\r\n setState((s) => ({ ...s, error: result.error ?? '保存失败' }))\r\n }\r\n })\r\n .catch((err: unknown) => {\r\n setState((s) => ({\r\n ...s,\r\n error: err instanceof Error ? err.message : String(err),\r\n }))\r\n })\r\n }\r\n return prev\r\n })\r\n },\r\n [editor],\r\n )\r\n\r\n const fixDuration = useCallback(async () => {\r\n setState((prev) => ({ ...prev, fixing: true }))\r\n try {\r\n const current = state.result\r\n if (current == null) return\r\n const fixed = await fixFMP4DurationStream(current.blob)\r\n setState((prev) => ({\r\n ...prev,\r\n fixing: false,\r\n result: prev.result == null ? prev.result : { ...prev.result, blob: fixed },\r\n }))\r\n } catch (err) {\r\n setState((prev) => ({\r\n ...prev,\r\n fixing: false,\r\n error: err instanceof Error ? err.message : String(err),\r\n }))\r\n }\r\n }, [state.result])\r\n\r\n return { state, start, cancel, reset, download, fixDuration, defaultBitrate: DEFAULT_BITRATE }\r\n}\r\n","import { Download, Wrench } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport type { ExportResult } from '../export-runner'\n\ninterface ExportResultViewProps {\n result: ExportResult\n fixing: boolean\n onDownload: () => void\n onFixDuration: () => void\n}\n\n/**\n * 导出结果:下载 + 修复时长。\n *\n * WebAV 输出的是 fMP4(moof/mdat 分片),moov 里的 duration 为 0 时,\n * 部分播放器会显示「时长未知」。fixFMP4Duration 会重写该字段。\n */\nexport function ExportResultView({\n result,\n fixing,\n onDownload,\n onFixDuration,\n}: ExportResultViewProps) {\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button size=\"sm\" onClick={onDownload}>\n <Download className=\"size-3.5\" />\n 下载视频\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onFixDuration} disabled={fixing}>\n <Wrench className=\"size-3.5\" />\n {fixing ? '修复中…' : '修复时长'}\n </Button>\n {result.skipped.length > 0 && (\n <div className=\"w-full space-y-0.5 rounded-md bg-warning/10 p-2 text-[11px] text-warning\">\n <p>{result.skipped.length} 个片段被跳过:</p>\n <ul className=\"list-disc pl-4\">\n {result.skipped.slice(0, 4).map((item, index) => (\n <li key={`${item.clipId}-${index}`} className=\"truncate\">\n {item.reason}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { AlertTriangle, CheckCircle2, Download, Loader2, RotateCw, XCircle } from 'lucide-react'\nimport type { ExportFps, ExportPreset } from '@cut-workbench/core'\nimport { useUi } from '../../../context'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport { Label } from '@/components/ui/label'\nimport { Switch } from '@/components/ui/switch'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { Progress } from '@/components/ui/progress'\nimport { Separator } from '@/components/ui/separator'\nimport { formatBytes, formatDuration, formatElapsed } from '@/lib/format'\nimport { DEFAULT_BITRATE } from '@/lib/constants'\nimport { getProjectDuration } from '@cut-workbench/core'\nimport { resolveDimensions, useExport } from '../hooks/useExportState'\nimport { ExportResultView } from './ExportResult'\nimport { useEditor } from '@cut-workbench/react'\n\nconst PRESETS: Array<{ value: ExportPreset; label: string; hint: string }> = [\n { value: '1080p', label: '1080p', hint: '1920×1080' },\n { value: '720p', label: '720p', hint: '1280×720' },\n { value: 'source', label: '原尺寸', hint: '与工程一致' },\n]\n\nconst FPS_OPTIONS: ExportFps[] = [24, 30, 60]\n\n/**\n * 导出对话框:配置 → 进度 → 结果。\n *\n * 所有重活都在 Worker 里;这里只负责 UI 与状态机。\n */\nexport function ExportDialog() {\n const open = useUi((s) => s.dialog === 'export')\n const closeDialog = useUi((s) => s.closeDialog)\n const project = useEditor((s) => s.project)\n const assets = useEditor((s) => s.assets)\n\n const [preset, setPreset] = useState<ExportPreset>('1080p')\n const [fps, setFps] = useState<ExportFps>(30)\n const [bitrateMbps, setBitrateMbps] = useState(DEFAULT_BITRATE / 1_000_000)\n const [includeAudio, setIncludeAudio] = useState(true)\n\n const { state, start, cancel, reset, download, fixDuration } = useExport(project, assets)\n\n const dimensions = useMemo(() => resolveDimensions(preset, project), [preset, project])\n const durationUs = getProjectDuration(project)\n const estimatedBytes = useMemo(() => {\n const seconds = durationUs / 1_000_000\n return Math.round((bitrateMbps * 1_000_000 * seconds) / 8)\n }, [bitrateMbps, durationUs])\n\n const running = state.phase === 'preparing' || state.phase === 'running'\n const hasTextClips = useMemo(() => {\n const kinds = new Map(assets.map((a) => [a.id, a.kind]))\n return project.clips.some((c) => kinds.get(c.assetId) === 'text')\n }, [assets, project.clips])\n\n // 关闭对话框时重置\n useEffect(() => {\n if (!open && !running) reset()\n }, [open, reset, running])\n\n const handleStart = () => {\n void start({ preset, fps, bitrate: Math.round(bitrateMbps * 1_000_000), includeAudio })\n }\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && !running && closeDialog()}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>导出视频</DialogTitle>\n <DialogDescription>\n 输出格式固定为 MP4(H.264 + AAC 48kHz 双声道)。WebAV 生成的是 fMP4 分片,\n 部分播放器需要「修复时长」才能正确显示总长。\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label className=\"text-xs\">分辨率</Label>\n <Select\n value={preset}\n onValueChange={(v) => setPreset(v as ExportPreset)}\n disabled={running}\n >\n <SelectTrigger className=\"h-8\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRESETS.map((item) => (\n <SelectItem key={item.value} value={item.value}>\n {item.label}\n <span className=\"text-muted-foreground\"> · {item.hint}</span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label className=\"text-xs\">帧率</Label>\n <Select\n value={String(fps)}\n onValueChange={(v) => setFps(Number(v) as ExportFps)}\n disabled={running}\n >\n <SelectTrigger className=\"h-8\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FPS_OPTIONS.map((option) => (\n <SelectItem key={option} value={String(option)}>\n {option} fps\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-xs\">码率</Label>\n <span className=\"font-mono text-[11px] text-muted-foreground tabular-nums\">\n {bitrateMbps.toFixed(1)} Mbps\n </span>\n </div>\n <input\n type=\"range\"\n className=\"h-1 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary\"\n min={1}\n max={20}\n step={0.5}\n value={bitrateMbps}\n disabled={running}\n onChange={(e) => setBitrateMbps(Number(e.target.value))}\n />\n </div>\n\n <div className=\"flex items-center justify-between rounded-md border px-3 py-2\">\n <div>\n <Label className=\"text-xs\">包含音轨</Label>\n <p className=\"text-[10px] text-muted-foreground\">\n 关闭后输出无声音(Combinator audio: false)\n </p>\n </div>\n <Switch checked={includeAudio} onCheckedChange={setIncludeAudio} disabled={running} />\n </div>\n\n <div className=\"space-y-1 rounded-md bg-muted/50 px-3 py-2 text-[11px] text-muted-foreground\">\n <div className=\"flex justify-between\">\n <span>输出尺寸</span>\n <span className=\"font-mono tabular-nums\">\n {dimensions.width}×{dimensions.height}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span>时长</span>\n <span className=\"font-mono tabular-nums\">{formatDuration(durationUs)}</span>\n </div>\n <div className=\"flex justify-between\">\n <span>预估大小</span>\n <span className=\"font-mono tabular-nums\">{formatBytes(estimatedBytes)}</span>\n </div>\n {hasTextClips && (\n <div className=\"flex items-start gap-1 pt-1 text-warning\">\n <AlertTriangle className=\"mt-0.5 size-3 shrink-0\" />\n <span>包含文字片段,导出会在主线程执行(文字渲染依赖 DOM),期间界面会短暂卡顿。</span>\n </div>\n )}\n </div>\n\n {running && (\n <div className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"flex items-center gap-1.5\">\n <Loader2 className=\"size-3 animate-spin\" />\n {state.phase === 'preparing' ? '准备中…' : '正在合成…'}\n </span>\n <span className=\"font-mono tabular-nums\">{Math.round(state.progress * 100)}%</span>\n </div>\n <Progress value={state.progress * 100} />\n </div>\n )}\n\n {state.phase === 'error' && (\n <div className=\"flex items-start gap-2 rounded-md bg-destructive/10 p-2.5 text-xs text-destructive\">\n <XCircle className=\"mt-0.5 size-3.5 shrink-0\" />\n <span>{state.error}</span>\n </div>\n )}\n\n {state.phase === 'cancelled' && (\n <div className=\"flex items-center gap-2 rounded-md bg-muted p-2.5 text-xs text-muted-foreground\">\n <XCircle className=\"size-3.5\" />\n 导出已取消,未生成文件。\n </div>\n )}\n\n {state.phase === 'done' && state.result != null && (\n <>\n <Separator />\n <div className=\"flex items-center gap-2 text-xs text-success\">\n <CheckCircle2 className=\"size-3.5\" />\n 导出完成 · 耗时 {formatElapsed(state.result.elapsedMs / 1000)} ·{' '}\n {formatBytes(state.result.blob.size)}\n {state.result.mode === 'main' && '(主线程)'}\n </div>\n <ExportResultView\n result={state.result}\n fixing={state.fixing}\n onDownload={() => download()}\n onFixDuration={() => void fixDuration()}\n />\n </>\n )}\n </div>\n\n <DialogFooter>\n {running ? (\n <Button variant=\"outline\" size=\"sm\" onClick={cancel}>\n 取消导出\n </Button>\n ) : state.phase === 'done' ? (\n <>\n <Button variant=\"outline\" size=\"sm\" onClick={closeDialog}>\n 关闭\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={reset}>\n 返回配置\n </Button>\n <Button size=\"sm\" onClick={() => download()}>\n <Download className=\"size-3.5\" />\n 再次下载\n </Button>\n </>\n ) : (\n <>\n <Button variant=\"outline\" size=\"sm\" onClick={closeDialog}>\n 关闭\n </Button>\n <Button size=\"sm\" onClick={handleStart} disabled={project.clips.length === 0}>\n {state.phase === 'error' || state.phase === 'cancelled' ? (\n <>\n <RotateCw className=\"size-3.5\" />\n 重试导出\n </>\n ) : (\n '开始导出'\n )}\n </Button>\n </>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n","import { Loader2 } from 'lucide-react'\nimport { Progress } from '@/components/ui/progress'\nimport { formatPercent } from '@/lib/format'\n\ninterface ExportProgressProps {\n progress: number\n label?: string\n className?: string\n}\n\n/** 内嵌式进度条(导出对话框之外的场景,例如 Lab) */\nexport function ExportProgress({ progress, label = '正在合成…', className }: ExportProgressProps) {\n return (\n <div className={className}>\n <div className=\"mb-1 flex items-center justify-between text-[11px]\">\n <span className=\"flex items-center gap-1.5 text-muted-foreground\">\n <Loader2 className=\"size-3 animate-spin\" />\n {label}\n </span>\n <span className=\"font-mono tabular-nums\">{formatPercent(progress)}</span>\n </div>\n <Progress value={progress * 100} />\n </div>\n )\n}\n","import { useMemo, useState } from 'react'\nimport { Monitor } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { cn } from '@/lib/cn'\nimport { useEditor } from '@cut-workbench/react'\nimport {\n ASPECT_PRESETS,\n normalizeSize,\n ratioLabel,\n type SizeChangeMode,\n} from '@cut-workbench/core'\n\ninterface CanvasSettingsDialogProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n}\n\n/**\n * 画布设置:比例 / 自定义尺寸 / 已有素材如何处理。\n *\n * 这是 setProjectSize 的唯一入口。之前 store 里有这个方法却没有任何 UI,\n * 用户自然找不到「设置画布比例」的地方;而即便直接调用,素材坐标也不会跟着\n * 重映射,看起来就像「改了没用」。\n *\n * 表单状态放在内层组件:Dialog 关闭时内容会被卸载,下次打开自然重新初始化,\n * 不需要用 effect 去同步外部值(那会触发级联渲染)。\n */\nexport function CanvasSettingsDialog({ open, onOpenChange }: CanvasSettingsDialogProps) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <CanvasSettingsBody onDone={() => onOpenChange(false)} />\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction CanvasSettingsBody({ onDone }: { onDone: () => void }) {\n const width = useEditor((s) => s.project.width)\n const height = useEditor((s) => s.project.height)\n const clipCount = useEditor((s) => s.project.clips.length)\n const setProjectSize = useEditor((s) => s.setProjectSize)\n\n // 初始值直接取自 store;组件随 Dialog 一起挂载,因此每次打开都是最新值\n const [draftW, setDraftW] = useState(String(width))\n const [draftH, setDraftH] = useState(String(height))\n const [mode, setMode] = useState<SizeChangeMode>('scale')\n\n const parsed = useMemo(() => {\n const w = Number(draftW)\n const h = Number(draftH)\n if (!Number.isFinite(w) || !Number.isFinite(h) || w < 16 || h < 16) return null\n return normalizeSize(w, h)\n }, [draftW, draftH])\n\n const changed = parsed != null && (parsed.width !== width || parsed.height !== height)\n const activePreset = ASPECT_PRESETS.find(\n (p) => p.width === (parsed?.width ?? width) && p.height === (parsed?.height ?? height),\n )\n\n const apply = () => {\n if (parsed == null) return\n setProjectSize(parsed.width, parsed.height, mode)\n onDone()\n }\n\n return (\n <>\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <Monitor className=\"size-4\" />\n 画布设置\n </DialogTitle>\n <DialogDescription>\n 画布尺寸决定预览构图与「原尺寸导出」的分辨率。当前 {width}×{height}(\n {ratioLabel(width, height)})。\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs\">比例预设</Label>\n <div className=\"grid grid-cols-4 gap-1.5\">\n {ASPECT_PRESETS.map((preset) => (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => {\n setDraftW(String(preset.width))\n setDraftH(String(preset.height))\n }}\n className={cn(\n 'flex flex-col items-center gap-1 rounded-md border px-2 py-2 text-[11px] transition-colors',\n activePreset?.id === preset.id\n ? 'border-primary bg-primary/10 text-foreground'\n : 'text-muted-foreground hover:border-primary/50 hover:text-foreground',\n )}\n title={preset.hint}\n >\n <span\n className=\"block rounded-[2px] border border-current opacity-70\"\n style={{\n width: preset.width >= preset.height ? 22 : (22 * preset.width) / preset.height,\n height: preset.height >= preset.width ? 22 : (22 * preset.height) / preset.width,\n }}\n />\n <span className=\"font-medium\">{preset.label}</span>\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"canvas-width\" className=\"text-xs\">\n 宽度\n </Label>\n <Input\n id=\"canvas-width\"\n type=\"number\"\n min={16}\n step={2}\n value={draftW}\n onChange={(e) => setDraftW(e.target.value)}\n className=\"h-8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"canvas-height\" className=\"text-xs\">\n 高度\n </Label>\n <Input\n id=\"canvas-height\"\n type=\"number\"\n min={16}\n step={2}\n value={draftH}\n onChange={(e) => setDraftH(e.target.value)}\n className=\"h-8\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs\">已有素材</Label>\n <div className=\"grid grid-cols-2 gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setMode('scale')}\n className={cn(\n 'rounded-md border px-2.5 py-2 text-left text-[11px] transition-colors',\n mode === 'scale'\n ? 'border-primary bg-primary/10'\n : 'text-muted-foreground hover:border-primary/50',\n )}\n >\n <span className=\"block font-medium text-foreground\">随画布缩放</span>\n <span className=\"mt-0.5 block leading-snug\">保持原有构图,按比例适配新画布</span>\n </button>\n <button\n type=\"button\"\n onClick={() => setMode('keep')}\n className={cn(\n 'rounded-md border px-2.5 py-2 text-left text-[11px] transition-colors',\n mode === 'keep'\n ? 'border-primary bg-primary/10'\n : 'text-muted-foreground hover:border-primary/50',\n )}\n >\n <span className=\"block font-medium text-foreground\">保持原位置</span>\n <span className=\"mt-0.5 block leading-snug\">素材坐标不变,画布留白变化</span>\n </button>\n </div>\n {mode === 'keep' && clipCount > 0 && (\n <p className=\"text-[10px] leading-relaxed text-warning\">\n 当前有 {clipCount} 个片段,保持原位置可能让部分素材跑到画布外。\n </p>\n )}\n </div>\n\n {parsed != null && (\n <p className=\"text-[10px] text-muted-foreground\">\n 将变为 {parsed.width}×{parsed.height}({ratioLabel(parsed.width, parsed.height)}),\n 可撤销(Ctrl+Z)。\n </p>\n )}\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" size=\"sm\" onClick={onDone}>\n 取消\n </Button>\n <Button size=\"sm\" onClick={apply} disabled={!changed}>\n {changed ? '应用' : '尺寸未变化'}\n </Button>\n </DialogFooter>\n </>\n )\n}\n","import { useCallback, useEffect } from 'react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { createClip } from '@cut-workbench/core'\r\nimport { getTrackEnd } from '@cut-workbench/core'\r\nimport { DEFAULT_STILL_DURATION_US } from '@/lib/constants'\r\nimport { toast } from 'sonner'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\n\r\n/**\r\n * 把素材添加到时间轴。\r\n *\r\n * 规则:\r\n * - 视频 / 图片 / 文字 / 字幕 → 视觉轨;\r\n * - 音频 → 音频轨(若存在);\r\n * - 起点接在该轨道最后一个 clip 的结束位置(磁吸追加),没有则从播放头开始。\r\n * - 静态素材(图片/文字/字幕)没有自然时长,用默认 5s,可后续在属性面板调整。\r\n */\r\nexport function useAddToTimeline() {\r\n const project = useEditor((s) => s.project)\r\n const assets = useEditor((s) => s.assets)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n const addClip = useEditor((s) => s.addClip)\r\n\r\n return useCallback(\r\n (assetId: string) => {\r\n const asset = assets.find((a) => a.id === assetId)\r\n if (!asset) {\r\n toast.error('素材不存在,可能已被删除')\r\n return\r\n }\r\n const kindIsAudio = asset.kind === 'audio'\r\n const track =\r\n project.tracks.find((t) => t.kind === (kindIsAudio ? 'audio' : 'visual')) ??\r\n project.tracks[0]\r\n if (!track) {\r\n toast.error('没有可用轨道')\r\n return\r\n }\r\n\r\n const trackEnd = getTrackEnd(project, track.id)\r\n const startUs = trackEnd > 0 ? trackEnd : playheadUs\r\n const durationUs = clampDuration(asset)\r\n\r\n const clip = createClip(\r\n {\r\n assetId: asset.id,\r\n trackId: track.id,\r\n startUs,\r\n durationUs,\r\n assetWidth: asset.width,\r\n assetHeight: asset.height,\r\n },\r\n project,\r\n )\r\n\r\n if (asset.kind === 'text') {\r\n clip.text = asset.text\r\n clip.textStyle = asset.textStyle\r\n }\r\n if (asset.kind === 'subtitle') {\r\n clip.srt = asset.srt\r\n clip.subtitleStyle = asset.subtitleStyle\r\n // 字幕自身有负空间(透明遮罩层),这里把它放大到整个画布\r\n clip.transform = {\r\n x: 0,\r\n y: 0,\r\n w: project.width,\r\n h: project.height,\r\n angle: 0,\r\n }\r\n }\r\n\r\n addClip(clip)\r\n },\r\n [addClip, assets, playheadUs, project],\r\n )\r\n}\r\n\r\nfunction clampDuration(asset: MediaAsset): number {\r\n const duration = asset.durationUs\r\n if (!Number.isFinite(duration) || duration <= 0) return DEFAULT_STILL_DURATION_US\r\n return duration\r\n}\r\n\r\n/**\r\n * 监听素材被删除事件,回收 registry 中的母版与缩略图 URL。\r\n * 放在 App 层调用一次。\r\n */\r\nexport function useAssetLifecycle(): void {\r\n const editor = useCutEditor()\r\n\r\n useEffect(() => {\r\n const onRemoved = (event: Event) => {\r\n const detail = (event as CustomEvent<{ assetId: string }>).detail\r\n if (detail?.assetId == null) return\r\n editor.runtime.clips.removeAsset(detail.assetId)\r\n }\r\n window.addEventListener('cut-test:asset-removed', onRemoved)\r\n return () => window.removeEventListener('cut-test:asset-removed', onRemoved)\r\n }, [editor])\r\n\r\n useEffect(() => {\r\n return () => {\r\n void editor.runtime.clips.dispose()\r\n }\r\n }, [editor])\r\n}\r\n","import { useCallback, useEffect } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport { useUiStoreApi } from '../context'\r\nimport { MAX_PX_PER_SECOND, MIN_PX_PER_SECOND } from '@/lib/constants'\r\nimport { clamp } from '@cut-workbench/core'\r\n\r\nconst FRAME_US = 1_000_000 / 30\r\n\r\n/**\r\n * 全局快捷键。\r\n *\r\n * | 键 | 行为 |\r\n * |---|---|\r\n * | Space | 播放 / 暂停 |\r\n * | S | 在播放头分割选中片段(未选中则分割命中的全部片段) |\r\n * | Delete / Backspace | 删除选中片段 |\r\n * | Ctrl+Z / Ctrl+Shift+Z | 撤销 / 重做 |\r\n * | Ctrl+D | 复制选中片段 |\r\n * | Ctrl+A | 全选 |\r\n * | ← / → | 逐帧(1/30 s) |\r\n * | Home / End | 跳首 / 跳尾 |\r\n * | + / - | 时间轴缩放 |\r\n * | Esc | 清空选择 |\r\n *\r\n * 设计:编辑类操作直接调 store;播放类操作通过 `cut-test:*` 自定义事件通知预览面板,\r\n * 避免 store → features 的反向依赖。\r\n */\r\nexport function useKeyboardShortcuts(): void {\r\n const editor = useCutEditor()\r\n const uiApi = useUiStoreApi()\r\n const handler = useCallback((event: KeyboardEvent) => {\r\n const target = event.target as HTMLElement | null\r\n const isEditable =\r\n target != null &&\r\n (target.tagName === 'INPUT' ||\r\n target.tagName === 'TEXTAREA' ||\r\n target.isContentEditable ||\r\n target.getAttribute('role') === 'textbox' ||\r\n target.closest('[role=menu]') != null)\r\n const mod = event.ctrlKey || event.metaKey\r\n\r\n // 撤销 / 重做 / 复制 / 全选:即使焦点在输入框也接管(避免触发浏览器文本撤销)\r\n if (mod && event.key.toLowerCase() === 'z') {\r\n event.preventDefault()\r\n if (event.shiftKey) editor.store.getState().redo()\r\n else editor.store.getState().undo()\r\n return\r\n }\r\n if (mod && event.key.toLowerCase() === 'd') {\r\n event.preventDefault()\r\n editor.store.getState().duplicateSelection(500_000)\r\n return\r\n }\r\n if (mod && event.key.toLowerCase() === 'a' && !isEditable) {\r\n event.preventDefault()\r\n const state = editor.store.getState()\r\n state.selectClips(state.project.clips.map((c) => c.id))\r\n return\r\n }\r\n if (mod && (event.key === '+' || event.key === '=' || event.key === '-' || event.key === '_')) {\r\n event.preventDefault()\r\n const ui = uiApi.getState()\r\n const factor = event.key === '-' || event.key === '_' ? 1 / 1.25 : 1.25\r\n ui.setPxPerSecond(clamp(ui.pxPerSecond * factor, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND))\r\n return\r\n }\r\n\r\n if (isEditable || mod) return\r\n\r\n switch (event.key) {\r\n case ' ':\r\n event.preventDefault()\r\n window.dispatchEvent(new CustomEvent('cut-test:toggle-play'))\r\n break\r\n case 's':\r\n case 'S': {\r\n event.preventDefault()\r\n const state = editor.store.getState()\r\n state.splitClipsAt(\r\n state.playheadUs,\r\n state.selectedClipIds.length > 0 ? state.selectedClipIds : undefined,\r\n )\r\n break\r\n }\r\n case 'Delete':\r\n case 'Backspace': {\r\n const state = editor.store.getState()\r\n if (state.selectedClipIds.length === 0) return\r\n event.preventDefault()\r\n state.removeClips(state.selectedClipIds)\r\n break\r\n }\r\n case 'ArrowLeft':\r\n case 'ArrowRight': {\r\n event.preventDefault()\r\n const direction = event.key === 'ArrowLeft' ? -1 : 1\r\n const state = editor.store.getState()\r\n const total = state.project.clips.reduce(\r\n (max, c) => Math.max(max, c.startUs + c.durationUs),\r\n 0,\r\n )\r\n const next = Math.max(\r\n 0,\r\n Math.min(total, state.playheadUs + direction * FRAME_US),\r\n )\r\n state.setPlayhead(next)\r\n window.dispatchEvent(\r\n new CustomEvent('cut-test:step-frame', { detail: { direction } }),\r\n )\r\n break\r\n }\r\n case 'Home': {\r\n event.preventDefault()\r\n editor.store.getState().setPlayhead(0)\r\n window.dispatchEvent(new CustomEvent('cut-test:seek', { detail: { us: 0 } }))\r\n break\r\n }\r\n case 'End': {\r\n event.preventDefault()\r\n const state = editor.store.getState()\r\n const total = state.project.clips.reduce(\r\n (max, c) => Math.max(max, c.startUs + c.durationUs),\r\n 0,\r\n )\r\n state.setPlayhead(total)\r\n window.dispatchEvent(new CustomEvent('cut-test:seek', { detail: { us: total } }))\r\n break\r\n }\r\n case '+':\r\n case '=': {\r\n event.preventDefault()\r\n const ui = uiApi.getState()\r\n ui.setPxPerSecond(clamp(ui.pxPerSecond * 1.25, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND))\r\n break\r\n }\r\n case '-':\r\n case '_': {\r\n event.preventDefault()\r\n const ui = uiApi.getState()\r\n ui.setPxPerSecond(clamp(ui.pxPerSecond / 1.25, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND))\r\n break\r\n }\r\n case 'Escape':\r\n editor.store.getState().selectClips([])\r\n break\r\n default:\r\n break\r\n }\r\n }, [])\r\n\r\n useEffect(() => {\r\n window.addEventListener('keydown', handler)\r\n return () => window.removeEventListener('keydown', handler)\r\n }, [handler])\r\n}\r\n\r\n","import { useEffect, useState } from 'react'\nimport { detectEnvironment, type CapabilityReport } from '@cut-workbench/core/engine'\n\ninterface CapabilityState {\n report: CapabilityReport | null\n loading: boolean\n error: string | null\n}\n\nlet cached: CapabilityReport | null = null\n\n/**\n * 启动时探测一次浏览器能力(结果在会话内缓存)。\n *\n * 必须在渲染任何 WebAV 组件前完成,否则 opfs-tools 的模块副作用可能直接抛错。\n */\nexport function useCapability(): CapabilityState {\n const [state, setState] = useState<CapabilityState>({\n report: cached,\n loading: cached == null,\n error: null,\n })\n\n useEffect(() => {\n if (cached != null) return\n let cancelled = false\n detectEnvironment()\n .then((report) => {\n cached = report\n if (!cancelled) setState({ report, loading: false, error: null })\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setState({\n report: null,\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n })\n return () => {\n cancelled = true\n }\n }, [])\n\n return state\n}\n","import { useCapability } from '@/hooks/use-capability'\nimport type { CapabilityReport } from '@cut-workbench/core/engine'\nimport { summarizeCapabilities } from '@cut-workbench/core/engine'\n\nexport type CapabilityStatus = 'checking' | 'ready' | 'degraded' | 'unsupported'\n\nexport interface CapabilityInfo {\n report: CapabilityReport | null\n loading: boolean\n error: string | null\n status: CapabilityStatus\n summary: string\n}\n\nfunction deriveStatus(report: CapabilityReport | null, loading: boolean): CapabilityStatus {\n if (loading) return 'checking'\n if (report == null) return 'unsupported'\n if (report.opfs && report.composerSupported && report.issues.length === 0) return 'ready'\n if (report.opfs && report.composerSupported) return 'degraded'\n return 'unsupported'\n}\n\n/** 能力探测的 UI 适配层:把 core 的报告转成状态与文案 */\nexport function useCapabilityInfo(): CapabilityInfo {\n const { report, loading, error } = useCapability()\n return {\n report,\n loading,\n error,\n status: deriveStatus(report, loading),\n summary: summarizeCapabilities(report),\n }\n}\n","import type { ReactNode } from 'react'\nimport { FlaskConical } from 'lucide-react'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { cn } from '@/lib/cn'\n\ninterface LabCardProps {\n title: string\n description: string\n apis: string[]\n children: ReactNode\n unavailable?: string | null\n className?: string\n}\n\n/** Lab 卡片外壳:标题 + 用到的 API + (不可用时的原因) */\nexport function LabCard({\n title,\n description,\n apis,\n children,\n unavailable,\n className,\n}: LabCardProps) {\n return (\n <Card className={cn('flex flex-col', className)}>\n <CardHeader className=\"gap-1\">\n <div className=\"flex items-center gap-2\">\n <div className=\"flex size-6 items-center justify-center rounded bg-primary/10 text-primary\">\n <FlaskConical className=\"size-3.5\" />\n </div>\n <CardTitle className=\"text-sm\">{title}</CardTitle>\n </div>\n <CardDescription className=\"text-xs\">{description}</CardDescription>\n <div className=\"flex flex-wrap gap-1 pt-1\">\n {apis.map((api) => (\n <Badge\n key={api}\n variant=\"outline\"\n className=\"font-mono text-[10px] font-normal text-muted-foreground\"\n >\n {api}\n </Badge>\n ))}\n </div>\n </CardHeader>\n <CardContent className=\"flex flex-1 flex-col gap-2.5\">\n {unavailable != null ? (\n <div className=\"rounded-md border border-dashed border-warning/40 bg-warning/5 p-3 text-xs text-warning\">\n {unavailable}\n </div>\n ) : (\n children\n )}\n </CardContent>\n </Card>\n )\n}\n","import { useCallback, useRef } from 'react'\nimport { useState } from 'react'\nimport { toast } from 'sonner'\n\n/** 生成可下载链接(Lab 通用) */\nexport function useDownload(): (blob: Blob, filename: string) => void {\n const urlsRef = useRef<string[]>([])\n return useCallback((blob: Blob, filename: string) => {\n const url = URL.createObjectURL(blob)\n urlsRef.current.push(url)\n const link = document.createElement('a')\n link.href = url\n link.download = filename\n document.body.appendChild(link)\n link.click()\n link.remove()\n window.setTimeout(() => URL.revokeObjectURL(url), 15_000)\n }, [])\n}\n\n/** 带 busy 状态的异步执行器:统一错误 toast,禁止静默吞异常 */\nexport function useLabAction() {\n const [busy, setBusy] = useState<string | null>(null)\n const run = useCallback(\n async <T,>(label: string, task: () => Promise<T>): Promise<T | null> => {\n setBusy(label)\n try {\n return await task()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n console.error(err)\n toast.error('操作失败', { description: message })\n return null\n } finally {\n setBusy(null)\n }\n },\n [],\n )\n return { busy, run }\n}\n","import { Loader2 } from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\nimport { useDownload } from '../hooks/use-lab-action'\n\n/**\n * Lab 共享的可视组件。\n *\n * 只导出组件(React Fast Refresh 要求单文件只导出组件);\n * 工具函数与 hook 放在 `../hooks/use-lab-action`。\n */\n\n/** 文件选择按钮 */\nexport function FilePickButton({\n label,\n accept,\n multiple,\n onFiles,\n disabled,\n}: {\n label: string\n accept: string\n multiple?: boolean\n onFiles: (files: File[]) => void\n disabled?: boolean\n}) {\n const inputRef = useRef<HTMLInputElement | null>(null)\n return (\n <>\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n className=\"hidden\"\n onChange={(e) => {\n const files = Array.from(e.target.files ?? [])\n if (files.length > 0) onFiles(files)\n e.target.value = ''\n }}\n />\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => inputRef.current?.click()}\n className=\"flex w-full items-center justify-center gap-1.5 rounded-md border border-dashed px-3 py-3.5 text-xs text-muted-foreground transition-colors hover:border-primary/50 hover:text-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"max-w-full truncate\">{label}</span>\n </button>\n </>\n )\n}\n\n/** 结果预览:视频 + 下载 */\nexport function ResultPreview({ blob, filename }: { blob: Blob; filename: string }) {\n const download = useDownload()\n const [url] = useState(() => URL.createObjectURL(blob))\n useEffect(() => () => URL.revokeObjectURL(url), [url])\n\n return (\n <div className=\"space-y-1.5\">\n <video\n src={url}\n controls\n className=\"w-full rounded-md border bg-stage-bg\"\n style={{ maxHeight: 200 }}\n />\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"min-w-0 flex-1 truncate text-[10px] text-muted-foreground\">\n {(blob.size / 1024 / 1024).toFixed(2)} MB\n </span>\n <button\n type=\"button\"\n onClick={() => download(blob, filename)}\n className=\"rounded-md bg-primary px-2 py-1 text-[11px] text-primary-foreground hover:bg-primary/90\"\n >\n 下载\n </button>\n </div>\n </div>\n )\n}\n\n/** 忙碌提示 */\nexport function Busy({ label }: { label: string }) {\n return (\n <div className=\"flex items-center gap-2 rounded-md bg-muted px-3 py-2 text-xs text-muted-foreground\">\n <Loader2 className=\"size-3 animate-spin\" />\n {label}\n </div>\n )\n}\n\n/** 简易滑杆 */\nexport function SliderRow({\n label,\n value,\n min,\n max,\n step,\n format,\n onChange,\n}: {\n label: string\n value: number\n min: number\n max: number\n step: number\n format?: (value: number) => string\n onChange: (value: number) => void\n}) {\n return (\n <div className=\"space-y-1\">\n <div className=\"flex justify-between text-[10px] text-muted-foreground\">\n <span>{label}</span>\n <span className=\"font-mono tabular-nums\">{format ? format(value) : value.toFixed(2)}</span>\n </div>\n <input\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={value}\n onChange={(e) => onChange(Number(e.target.value))}\n className=\"h-1 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary\"\n />\n </div>\n )\n}\n","import { useState } from 'react'\nimport { AlertTriangle } from 'lucide-react'\nimport { concatMp4, readMp4Info, type Mp4Info } from '@cut-workbench/core/lab'\nimport { formatDuration, formatResolution } from '@/lib/format'\nimport { LabCard } from './LabCard'\nimport { useLabAction } from '../hooks/use-lab-action'\nimport { Busy, FilePickButton, ResultPreview } from './shared'\n\n/**\n * Lab 1:无损拼接。\n *\n * 用 `fastConcatMP4` 直接拼接多个 MP4 的样本,不重编码。\n * 前提是编码 / 分辨率 / 采样率完全一致;不一致时本 Lab 会先预检并给出具体差异。\n */\nexport function ConcatenateLab() {\n const [files, setFiles] = useState<File[]>([])\n const [infos, setInfos] = useState<Mp4Info[]>([])\n const [result, setResult] = useState<Blob | null>(null)\n const { busy, run } = useLabAction()\n\n const handleFiles = async (picked: File[]) => {\n setFiles(picked)\n setResult(null)\n setInfos([])\n const detected = await run('正在读取文件信息…', async () => {\n const list: Mp4Info[] = []\n for (const file of picked) list.push(await readMp4Info(file))\n return list\n })\n if (detected != null) setInfos(detected)\n }\n\n const handleConcat = async () => {\n const output = await run('正在无损拼接…', () => concatMp4(files))\n if (output != null) {\n setResult(output.blob)\n setInfos(output.infos)\n }\n }\n\n const first = infos[0]\n const mismatch =\n first != null &&\n infos.some(\n (info) =>\n info.width !== first.width ||\n info.height !== first.height ||\n info.codec !== first.codec ||\n info.audioSampleRate !== first.audioSampleRate,\n )\n\n return (\n <LabCard\n title=\"无损拼接\"\n description=\"直接把多个 MP4 的样本拼在一起,不重编码,速度极快。要求规格完全一致。\"\n apis={['fastConcatMP4', 'MP4Clip.getFileHeaderBinData']}\n >\n <FilePickButton\n label={files.length > 0 ? `已选 ${files.length} 个文件(重新选择)` : '选择 2 个以上 MP4'}\n accept=\"video/mp4\"\n multiple\n onFiles={(picked) => void handleFiles(picked)}\n disabled={busy != null}\n />\n\n {files.length > 0 && (\n <ul className=\"space-y-1 text-[10px] text-muted-foreground\">\n {files.map((file, index) => {\n const info = infos[index]\n return (\n <li key={`${file.name}-${index}`} className=\"flex justify-between gap-2\">\n <span className=\"truncate\">{file.name}</span>\n <span className=\"shrink-0 font-mono\">\n {info != null\n ? `${formatResolution(info.width, info.height)} · ${formatDuration(info.durationUs)} · ${info.codec}`\n : '检测中…'}\n </span>\n </li>\n )\n })}\n </ul>\n )}\n\n {mismatch && (\n <div className=\"flex items-start gap-1.5 rounded-md bg-warning/10 p-2 text-[10px] text-warning\">\n <AlertTriangle className=\"mt-0.5 size-3 shrink-0\" />\n 检测到规格不一致,拼接会失败并给出具体原因(fastConcatMP4 的硬性要求)。\n </div>\n )}\n\n <button\n type=\"button\"\n disabled={files.length < 2 || busy != null}\n onClick={() => void handleConcat()}\n className=\"rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground disabled:opacity-50\"\n >\n 开始拼接\n </button>\n\n {busy != null && <Busy label={busy} />}\n {result != null && <ResultPreview blob={result} filename=\"concat.mp4\" />}\n </LabCard>\n )\n}\n","import { useState } from 'react'\r\nimport { mixAudioIntoMp4 } from '@cut-workbench/core/lab'\r\nimport { LabCard } from './LabCard'\r\nimport { useLabAction } from '../hooks/use-lab-action'\r\nimport { Busy, FilePickButton, ResultPreview, SliderRow } from './shared'\r\n\r\n/**\r\n * Lab 2:配音。\r\n *\r\n * `mixinMP4AndAudio` 只重编码音频轨,视频轨原样复制 —— 因此速度快、画质无损。\r\n */\r\nexport function RemuxLab() {\r\n const [video, setVideo] = useState<File | null>(null)\r\n const [audio, setAudio] = useState<File | null>(null)\r\n const [volume, setVolume] = useState(1)\r\n const [loop, setLoop] = useState(true)\r\n const [result, setResult] = useState<Blob | null>(null)\r\n const { busy, run } = useLabAction()\r\n\r\n const handleMix = async () => {\r\n if (video == null || audio == null) return\r\n const output = await run('正在混合音频…', () =>\r\n mixAudioIntoMp4(video, audio, volume, loop),\r\n )\r\n if (output != null) setResult(output)\r\n }\r\n\r\n return (\r\n <LabCard\r\n title=\"视频配音\"\r\n description=\"把音轨混入 MP4:只重编码音频,视频轨不动,画质零损失。\"\r\n apis={['mixinMP4AndAudio']}\r\n >\r\n <FilePickButton\r\n label={video == null ? '选择视频(MP4)' : video.name}\r\n accept=\"video/mp4\"\r\n onFiles={(files) => {\r\n setVideo(files[0] ?? null)\r\n setResult(null)\r\n }}\r\n disabled={busy != null}\r\n />\r\n <FilePickButton\r\n label={audio == null ? '选择音频(MP3/WAV/M4A)' : audio.name}\r\n accept=\"audio/*\"\r\n onFiles={(files) => {\r\n setAudio(files[0] ?? null)\r\n setResult(null)\r\n }}\r\n disabled={busy != null}\r\n />\r\n\r\n <SliderRow\r\n label=\"音量\"\r\n value={volume}\r\n min={0}\r\n max={2}\r\n step={0.05}\r\n format={(v) => `${Math.round(v * 100)}%`}\r\n onChange={setVolume}\r\n />\r\n <label className=\"flex items-center gap-2 text-[11px] text-muted-foreground\">\r\n <input\r\n type=\"checkbox\"\r\n checked={loop}\r\n onChange={(e) => setLoop(e.target.checked)}\r\n className=\"accent-primary\"\r\n />\r\n 音频短于视频时循环填充\r\n </label>\r\n\r\n <button\r\n type=\"button\"\r\n disabled={video == null || audio == null || busy != null}\r\n onClick={() => void handleMix()}\r\n className=\"rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground disabled:opacity-50\"\r\n >\r\n 开始混合\r\n </button>\r\n\r\n {busy != null && <Busy label={busy} />}\r\n {result != null && <ResultPreview blob={result} filename=\"mixin.mp4\" />}\r\n </LabCard>\r\n )\r\n}\r\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { isWebGL2Available, renderChromakeyFrame, createMp4Clip, type Mp4ClipHandle } from '@cut-workbench/core/lab'\nimport { LabCard } from './LabCard'\nimport { useLabAction } from '../hooks/use-lab-action'\nimport { Busy, FilePickButton, SliderRow } from './shared'\nimport { toast } from 'sonner'\n\n/**\n * Lab 3:绿幕抠像。\n *\n * `createChromakey` 返回一个函数,把 VideoFrame 转成去背后的 ImageBitmap。\n * 配合 `MP4Clip.tickInterceptor` 即可对整段视频生效(导出时同样有效)。\n *\n * ⚠️ 依赖 WebGL2;不支持时本卡片隐藏操作、仅显示原因。\n */\n/**\n * 抠像结果的兜底底色:读取主题令牌,而不是写死 `#101018`。\n *\n * 抠像后的 VideoFrame 带透明通道,需要一层底色才能看清;用 `--cw-stage-bg`\n * 让 Lab 预览区与工作台舞台保持一致,宿主换主题时一起变。\n * 取不到时退回深色(纯逻辑环境 / 未包 `.cw-root`)。\n */\nfunction readStageBackground(): string {\n if (typeof document === 'undefined') return '#101018'\n const root = document.querySelector('.cw-root')\n if (root == null) return '#101018'\n const value = getComputedStyle(root).getPropertyValue('--cw-stage-bg').trim()\n return value.length > 0 ? value : '#101018'\n}\n\nexport function ChromakeyLab() {\n // WebGL2 探测是纯函数,可安全在 useState 初始化中执行\n const [webgl2] = useState(isWebGL2Available)\n const [file, setFile] = useState<File | null>(null)\n const [similarity, setSimilarity] = useState(0.4)\n const [smoothness, setSmoothness] = useState(0.1)\n const [spill, setSpill] = useState(0.1)\n const [timeUs, setTimeUs] = useState(0)\n const [durationUs, setDurationUs] = useState(0)\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const clipRef = useRef<Mp4ClipHandle | null>(null)\n const { busy, run } = useLabAction()\n\n useEffect(() => {\n return () => {\n clipRef.current?.destroy()\n clipRef.current = null\n }\n }, [])\n\n const render = useCallback(async () => {\n const clip = clipRef.current\n const canvas = canvasRef.current\n if (clip == null || canvas == null) return\n await renderChromakeyFrame(\n clip,\n timeUs,\n { similarity, smoothness, spill, keyColor: [0, 255, 0] },\n canvas,\n // 底色由主题令牌驱动:抠像结果画在透明画布上,用舞台色兜底,\n // 这样宿主换主题时 Lab 预览区也跟着变\n readStageBackground(),\n )\n }, [similarity, smoothness, spill, timeUs])\n\n const handleFile = async (files: File[]) => {\n const picked = files[0]\n if (picked == null) return\n const result = await run('正在解析视频…', async () => {\n clipRef.current?.destroy()\n const created = await createMp4Clip(picked)\n return created\n })\n if (result == null) return\n clipRef.current = result.clip\n setFile(picked)\n setDurationUs(result.meta.duration)\n setTimeUs(Math.min(result.meta.duration / 2, 1_000_000))\n toast.success('已载入视频,拖动参数查看抠像效果')\n }\n\n useEffect(() => {\n if (clipRef.current == null || busy != null) return\n void render()\n }, [busy, render])\n\n return (\n <LabCard\n title=\"绿幕抠像\"\n description=\"用 WebGL2 实时扣除背景色,通过 tickInterceptor 作用到整段视频。\"\n apis={['createChromakey', 'MP4Clip.tickInterceptor']}\n unavailable={webgl2 ? null : '当前设备不支持 WebGL2,createChromakey 无法运行。'}\n >\n <FilePickButton\n label={file?.name ?? '选择绿幕视频(MP4)'}\n accept=\"video/mp4\"\n onFiles={(files) => void handleFile(files)}\n disabled={busy != null}\n />\n\n <div className=\"relative overflow-hidden rounded-md border bg-stage-bg\">\n <canvas ref={canvasRef} className=\"block w-full\" style={{ maxHeight: 180 }} />\n {file == null && (\n <div className=\"pointer-events-none absolute inset-0 flex h-24 items-center justify-center text-[10px] text-muted-foreground\">\n 选择视频后显示抠像结果\n </div>\n )}\n </div>\n\n <SliderRow\n label=\"相似度 similarity\"\n value={similarity}\n min={0}\n max={1}\n step={0.01}\n format={(v) => v.toFixed(2)}\n onChange={setSimilarity}\n />\n <SliderRow\n label=\"平滑度 smoothness\"\n value={smoothness}\n min={0}\n max={1}\n step={0.01}\n format={(v) => v.toFixed(2)}\n onChange={setSmoothness}\n />\n <SliderRow\n label=\"饱和度 spill\"\n value={spill}\n min={0}\n max={1}\n step={0.01}\n format={(v) => v.toFixed(2)}\n onChange={setSpill}\n />\n {durationUs > 0 && (\n <SliderRow\n label=\"取帧时间\"\n value={timeUs}\n min={0}\n max={Math.max(1, durationUs - 1)}\n step={100_000}\n format={(v) => `${(v / 1e6).toFixed(2)}s`}\n onChange={setTimeUs}\n />\n )}\n {busy != null && <Busy label={busy} />}\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\n keyColor 固定为纯绿 (0,255,0)。实际项目中可读取首帧像素自动取色。\n </p>\n </LabCard>\n )\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { Play, Square } from 'lucide-react'\nimport {\n burnSubtitles,\n createSubtitlePreviewClip,\n renderSubtitleFrame,\n type SubtitleClipHandle,\n} from '@cut-workbench/core/lab'\nimport { parseSrt } from '@cut-workbench/core'\nimport { LabCard } from './LabCard'\nimport { useLabAction } from '../hooks/use-lab-action'\nimport { Busy, ResultPreview, SliderRow } from './shared'\n\nconst SAMPLE_SRT = `1\n00:00:00,000 --> 00:00:02,000\n这是第一句字幕\n\n2\n00:00:02,000 --> 00:00:04,000\n第二句会带描边\n\n3\n00:00:04,000 --> 00:00:06,000\nEmbedSubtitlesClip 烧录进画面\n`\n\n/**\n * Lab 4:字幕烧录。\n *\n * `EmbedSubtitlesClip` 本身就是一个 IClip:tick 返回带字幕的 VideoFrame。\n * 因此既能放进 Combinator 导出,也能直接逐帧预览。\n */\nexport function SubtitleLab() {\n const [srt, setSrt] = useState(SAMPLE_SRT)\n const [timeUs, setTimeUs] = useState(0)\n const [playing, setPlaying] = useState(false)\n const [result, setResult] = useState<Blob | null>(null)\n const { busy, run } = useLabAction()\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const clipRef = useRef<SubtitleClipHandle | null>(null)\n const rafRef = useRef<number | null>(null)\n\n const cues = parseSrt(srt)\n const durationUs = cues.length > 0 ? cues[cues.length - 1]!.endMs * 1000 : 0\n\n // 重新创建字幕 clip\n useEffect(() => {\n clipRef.current?.destroy()\n if (cues.length === 0) {\n clipRef.current = null\n return\n }\n try {\n clipRef.current = createSubtitlePreviewClip(srt, 640, 360)\n } catch (err) {\n console.warn('[SubtitleLab] create failed', err)\n clipRef.current = null\n }\n return () => {\n clipRef.current?.destroy()\n clipRef.current = null\n }\n }, [cues.length, srt])\n\n const draw = useCallback(async () => {\n const clip = clipRef.current\n const canvas = canvasRef.current\n if (clip == null || canvas == null) return\n await renderSubtitleFrame(clip, timeUs, canvas)\n }, [timeUs])\n\n useEffect(() => {\n void draw()\n }, [draw])\n\n // 播放循环(简单 rAF 递增时间)\n useEffect(() => {\n if (!playing) return\n let last = performance.now()\n let current = timeUs\n const tick = () => {\n const now = performance.now()\n current += (now - last) * 1000\n last = now\n if (durationUs > 0 && current >= durationUs) {\n setTimeUs(durationUs)\n setPlaying(false)\n return\n }\n setTimeUs(current)\n rafRef.current = requestAnimationFrame(tick)\n }\n rafRef.current = requestAnimationFrame(tick)\n return () => {\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\n }\n // 只在开始播放时启动\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [playing, durationUs])\n\n const handleBurn = async () => {\n const width = 1280\n const height = 720\n const output = await run('正在烧录字幕…', () =>\n burnSubtitles(srt, width, height, durationUs),\n )\n if (output != null) setResult(output)\n }\n\n return (\n <LabCard\n title=\"字幕烧录\"\n description=\"SRT 解析后逐帧绘制到画面,最终编码进视频流(硬字幕)。\"\n apis={['EmbedSubtitlesClip', 'Combinator']}\n >\n <textarea\n value={srt}\n onChange={(e) => setSrt(e.target.value)}\n rows={5}\n className=\"w-full rounded-md border bg-transparent p-2 font-mono text-[10px] outline-none focus-visible:border-ring\"\n placeholder=\"粘贴 SRT 内容\"\n />\n\n <div className=\"overflow-hidden rounded-md border bg-stage-bg\">\n <canvas\n ref={canvasRef}\n width={640}\n height={360}\n className=\"block w-full\"\n style={{ maxHeight: 180 }}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setPlaying((p) => !p)}\n disabled={cues.length === 0}\n className=\"flex items-center gap-1 rounded-md bg-secondary px-2 py-1 text-[11px] disabled:opacity-50\"\n >\n {playing ? <Square className=\"size-3\" /> : <Play className=\"size-3\" />}\n {playing ? '停止' : '预览'}\n </button>\n <span className=\"text-[10px] text-muted-foreground\">\n {cues.length} 条 · {(timeUs / 1e6).toFixed(2)}s / {(durationUs / 1e6).toFixed(2)}s\n </span>\n </div>\n\n <SliderRow\n label=\"时间\"\n value={timeUs}\n min={0}\n max={Math.max(1, durationUs)}\n step={10_000}\n format={(v) => `${(v / 1e6).toFixed(2)}s`}\n onChange={(v) => {\n setPlaying(false)\n setTimeUs(v)\n }}\n />\n\n <button\n type=\"button\"\n disabled={cues.length === 0 || busy != null}\n onClick={() => void handleBurn()}\n className=\"rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground disabled:opacity-50\"\n >\n 烧录并导出 MP4\n </button>\n\n {busy != null && <Busy label={busy} />}\n {result != null && <ResultPreview blob={result} filename=\"subtitles.mp4\" />}\n </LabCard>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { Split } from 'lucide-react'\nimport { splitMp4Tracks, type TrackSplitResult } from '@cut-workbench/core/lab'\nimport { formatDuration } from '@/lib/format'\nimport { LabCard } from './LabCard'\nimport { useLabAction } from '../hooks/use-lab-action'\nimport { Busy, FilePickButton } from './shared'\n\n/**\n * Lab 5:轨道拆分。\n *\n * `splitTrack()` 从一个 MP4Clip 派生出「纯视频」与「纯音频」两个 clip,\n * 它们与源共享样本表,因此内存开销很小。\n */\nexport function TrackSplitLab() {\n const [file, setFile] = useState<File | null>(null)\n const [result, setResult] = useState<TrackSplitResult | null>(null)\n const { busy, run } = useLabAction()\n const cleanupRef = useRef<(() => void) | null>(null)\n\n useEffect(() => {\n return () => cleanupRef.current?.()\n }, [])\n\n const handleSplit = async () => {\n if (file == null) return\n cleanupRef.current?.()\n const output = await run('正在拆分轨道…', () => splitMp4Tracks(file))\n setResult(output)\n cleanupRef.current = () => {\n output?.videoClip?.destroy()\n output?.audioClip?.destroy()\n }\n }\n\n return (\n <LabCard\n title=\"轨道拆分\"\n description=\"把一个 MP4 拆成纯视频轨与纯音频轨两个独立 IClip,用于分别编辑。\"\n apis={['MP4Clip.splitTrack']}\n >\n <FilePickButton\n label={file?.name ?? '选择 MP4'}\n accept=\"video/mp4\"\n onFiles={(files) => {\n setFile(files[0] ?? null)\n setResult(null)\n }}\n disabled={busy != null}\n />\n\n <button\n type=\"button\"\n disabled={file == null || busy != null}\n onClick={() => void handleSplit()}\n className=\"flex items-center justify-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground disabled:opacity-50\"\n >\n <Split className=\"size-3\" />\n 拆分轨道\n </button>\n\n {busy != null && <Busy label={busy} />}\n\n {result != null && (\n <div className=\"grid gap-1.5 text-[10px]\">\n <TrackResult\n label=\"视频轨\"\n present={result.videoMeta != null}\n detail={\n result.videoMeta != null\n ? `${result.videoMeta.width}×${result.videoMeta.height} · ${formatDuration(result.videoMeta.duration)}`\n : '源文件无视频轨'\n }\n />\n <TrackResult\n label=\"音频轨\"\n present={result.audioMeta != null}\n detail={\n result.audioMeta != null\n ? `48kHz 双声道 · ${formatDuration(result.audioMeta.duration)}`\n : '源文件无音频轨'\n }\n />\n <p className=\"pt-1 leading-relaxed text-muted-foreground\">\n 拆分出的 clip 与源共享样本表,可分别创建 Sprite 放到不同轨道;两者在时间轴上对齐即等于原片。\n </p>\n </div>\n )}\n </LabCard>\n )\n}\n\nfunction TrackResult({\n label,\n present,\n detail,\n}: {\n label: string\n present: boolean\n detail: string\n}) {\n return (\n <div className=\"flex items-center justify-between gap-2 rounded-md border px-2 py-1.5\">\n <span className={present ? 'text-foreground' : 'text-muted-foreground'}>{label}</span>\n <span className={present ? 'font-mono text-success' : 'text-muted-foreground'}>\n {present ? detail : detail}\n </span>\n </div>\n )\n}\n","import { useEffect, useState } from 'react'\r\nimport { extractKeyframes } from '@cut-workbench/core/lab'\r\nimport { formatTimecode } from '@cut-workbench/core'\r\nimport { LabCard } from './LabCard'\r\nimport { useLabAction } from '../hooks/use-lab-action'\r\nimport { Busy, FilePickButton } from './shared'\r\n\r\ninterface FrameItem {\r\n ts: number\r\n url: string\r\n size: number\r\n}\r\n\r\n/**\r\n * Lab 6:缩略图抽帧。\r\n *\r\n * `MP4Clip.thumbnails()` 默认每个关键帧生成一张;也可以传 step 按固定间隔取。\r\n * 这里展示全部关键帧与时间戳。\r\n */\r\nexport function ThumbnailLab() {\r\n const [file, setFile] = useState<File | null>(null)\r\n const [frames, setFrames] = useState<FrameItem[]>([])\r\n const [width, setWidth] = useState(120)\r\n const { busy, run } = useLabAction()\r\n\r\n useEffect(() => {\r\n return () => {\r\n for (const frame of frames) URL.revokeObjectURL(frame.url)\r\n }\r\n }, [frames])\r\n\r\n const handleExtract = async () => {\r\n if (file == null) return\r\n const output = await run('正在抽取关键帧…', () => extractKeyframes(file, width))\r\n if (output != null) setFrames(output)\r\n }\r\n\r\n return (\r\n <LabCard\r\n title=\"缩略图抽帧\"\r\n description=\"按关键帧抽取缩略图,可用于时间轴缩略图带或场景切分预览。\"\r\n apis={['MP4Clip.thumbnails']}\r\n >\r\n <FilePickButton\r\n label={file?.name ?? '选择 MP4'}\r\n accept=\"video/mp4\"\r\n onFiles={(files) => {\r\n setFile(files[0] ?? null)\r\n setFrames([])\r\n }}\r\n disabled={busy != null}\r\n />\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <label className=\"text-[10px] text-muted-foreground\">宽度</label>\r\n <input\r\n type=\"number\"\r\n min={40}\r\n max={480}\r\n step={20}\r\n value={width}\r\n onChange={(e) => setWidth(Number(e.target.value))}\r\n className=\"h-6 w-16 rounded border bg-transparent px-1.5 text-[11px]\"\r\n />\r\n <button\r\n type=\"button\"\r\n disabled={file == null || busy != null}\r\n onClick={() => void handleExtract()}\r\n className=\"ml-auto rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground disabled:opacity-50\"\r\n >\r\n 抽取\r\n </button>\r\n </div>\r\n\r\n {busy != null && <Busy label={busy} />}\r\n\r\n {frames.length > 0 && (\r\n <>\r\n <p className=\"text-[10px] text-muted-foreground\">共 {frames.length} 张关键帧</p>\r\n <div className=\"thin-scrollbar grid max-h-52 grid-cols-3 gap-1.5 overflow-auto\">\r\n {frames.map((frame, index) => (\r\n <figure key={`${frame.ts}-${index}`} className=\"space-y-0.5\">\r\n <img\r\n src={frame.url}\r\n alt=\"\"\r\n className=\"w-full rounded border object-cover\"\r\n loading=\"lazy\"\r\n />\r\n <figcaption className=\"font-mono text-[9px] text-muted-foreground tabular-nums\">\r\n {formatTimecode(frame.ts)}\r\n </figcaption>\r\n </figure>\r\n ))}\r\n </div>\r\n </>\r\n )}\r\n </LabCard>\r\n )\r\n}\r\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { Circle, Pause, Play, Square } from 'lucide-react'\nimport { toast } from 'sonner'\nimport {\n AVRecorder,\n createDemoCanvasStream,\n fixRecordedDuration,\n formatRecorderState,\n type RecorderHandle,\n} from '@cut-workbench/core/lab'\nimport { LabCard } from './LabCard'\nimport { useLabAction } from '../hooks/use-lab-action'\nimport { ResultPreview } from './shared'\n\ntype Mode = 'demo' | 'camera'\n\n/**\n * Lab 7:屏幕 / 画布录制。\n *\n * `AVRecorder` 接收任意 MediaStream 并输出 MP4(H.264 + AAC)。\n * 这里提供两种源:\n * - 程序化画布(Canvas.captureStream,无需授权);\n * - 摄像头(getUserMedia,需要用户授权)。\n */\nexport function RecorderLab() {\n const [mode, setMode] = useState<Mode>('demo')\n const [recording, setRecording] = useState(false)\n const [paused, setPaused] = useState(false)\n const [bytes, setBytes] = useState(0)\n const [state, setState] = useState<string>('inactive')\n const [result, setResult] = useState<Blob | null>(null)\n const [previewStream, setPreviewStream] = useState<MediaStream | null>(null)\n const videoRef = useRef<HTMLVideoElement | null>(null)\n const recorderRef = useRef<RecorderHandle | null>(null)\n const demoRef = useRef<{ stream: MediaStream; stop: () => void } | null>(null)\n const cameraRef = useRef<MediaStream | null>(null)\n const { run } = useLabAction()\n\n // 预览流挂到 <video>\n useEffect(() => {\n const video = videoRef.current\n if (video == null) return\n video.srcObject = previewStream\n if (previewStream != null) void video.play().catch(() => undefined)\n return () => {\n if (video.srcObject === previewStream) video.srcObject = null\n }\n }, [previewStream])\n\n const cleanup = useCallback(() => {\n demoRef.current?.stop()\n demoRef.current = null\n cameraRef.current?.getTracks().forEach((t) => t.stop())\n cameraRef.current = null\n recorderRef.current?.dispose()\n recorderRef.current = null\n setPreviewStream(null)\n }, [])\n\n useEffect(() => cleanup, [cleanup])\n\n const acquireStream = async (): Promise<MediaStream | null> => {\n cleanup()\n if (mode === 'demo') {\n const demo = createDemoCanvasStream(1280, 720)\n demoRef.current = demo\n setPreviewStream(demo.stream)\n return demo.stream\n }\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })\n cameraRef.current = stream\n setPreviewStream(stream)\n return stream\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n toast.error('无法访问摄像头', { description: message })\n return null\n }\n }\n\n const handleStart = async () => {\n const stream = await acquireStream()\n if (stream == null) return\n setBytes(0)\n setResult(null)\n const recorder = createRecorderAdapter(stream, (total) => setBytes(total))\n recorderRef.current = recorder\n recorder.start()\n setRecording(true)\n setPaused(false)\n setState(recorder.state())\n }\n\n const handlePauseToggle = () => {\n const recorder = recorderRef.current\n if (recorder == null) return\n if (paused) {\n recorder.resume()\n setPaused(false)\n } else {\n recorder.pause()\n setPaused(true)\n }\n setState(recorder.state())\n }\n\n const handleStop = async () => {\n const recorder = recorderRef.current\n if (recorder == null) return\n const output = await run('正在封装 MP4…', async () => {\n const raw = await recorder.stop()\n // AVRecorder 输出的是 fMP4,moov 时长为 0,需修复后播放器才显示正确总长\n return fixRecordedDuration(raw)\n })\n setRecording(false)\n setPaused(false)\n setState(recorder.state())\n recorderRef.current = null\n if (output != null) {\n setResult(output)\n toast.success(`录制完成 · ${(output.size / 1024 / 1024).toFixed(2)} MB`)\n }\n }\n\n return (\n <LabCard\n title=\"屏幕 / 画布录制\"\n description=\"用 AVRecorder 把 MediaStream 实时编码为 MP4。可录制程序化画布或摄像头。\"\n apis={['AVCanvas.captureStream', 'AVRecorder.start/pause/resume/stop']}\n >\n <div className=\"flex gap-1.5\">\n {(['demo', 'camera'] as Mode[]).map((item) => (\n <button\n key={item}\n type=\"button\"\n disabled={recording}\n onClick={() => setMode(item)}\n className={`flex-1 rounded-md border px-2 py-1 text-[11px] disabled:opacity-50 ${\n mode === item ? 'border-primary bg-primary/10 text-primary' : 'text-muted-foreground'\n }`}\n >\n {item === 'demo' ? '动态画布' : '摄像头'}\n </button>\n ))}\n </div>\n\n <div className=\"overflow-hidden rounded-md border bg-stage-bg\">\n <video\n ref={videoRef}\n muted\n playsInline\n className=\"block w-full\"\n style={{ maxHeight: 170 }}\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {!recording ? (\n <button\n type=\"button\"\n onClick={() => void handleStart()}\n className=\"flex items-center gap-1 rounded-md bg-destructive px-2.5 py-1 text-[11px] text-destructive-foreground\"\n >\n <Circle className=\"size-3 fill-current\" />\n 开始录制\n </button>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={handlePauseToggle}\n className=\"flex items-center gap-1 rounded-md bg-secondary px-2.5 py-1 text-[11px]\"\n >\n {paused ? <Play className=\"size-3\" /> : <Pause className=\"size-3\" />}\n {paused ? '继续' : '暂停'}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleStop()}\n className=\"flex items-center gap-1 rounded-md bg-primary px-2.5 py-1 text-[11px] text-primary-foreground\"\n >\n <Square className=\"size-3\" />\n 停止\n </button>\n </>\n )}\n <span className=\"ml-auto font-mono text-[10px] text-muted-foreground tabular-nums\">\n {(bytes / 1024 / 1024).toFixed(2)} MB · {formatRecorderState(state)}\n </span>\n </div>\n\n {result != null && <ResultPreview blob={result} filename=\"recording.mp4\" />}\n </LabCard>\n )\n}\n\n/** 适配 RecorderHandle(隔离 AVRecorder 细节) */\nfunction createRecorderAdapter(\n stream: MediaStream,\n onChunk: (bytes: number) => void,\n): RecorderHandle {\n const recorder = new AVRecorder(stream, { expectFPS: 30 })\n const chunks: BlobPart[] = []\n let total = 0\n\n return {\n start: () => {\n const readable = recorder.start(300)\n void (async () => {\n const reader = readable.getReader()\n for (;;) {\n try {\n const { done, value } = await reader.read()\n if (done) break\n if (value != null) {\n chunks.push(new Uint8Array(value))\n total += value.byteLength\n onChunk(total)\n }\n } catch {\n break\n }\n }\n })()\n },\n pause: () => recorder.pause(),\n resume: () => recorder.resume(),\n stop: async () => {\n await recorder.stop()\n return new Blob(chunks, { type: 'video/mp4' })\n },\n state: () => recorder.state,\n dispose: () => {\n stream.getTracks().forEach((t) => t.stop())\n },\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { Play, Square } from 'lucide-react'\nimport {\n FrameCounterClip,\n exportCustomClip,\n renderClipFrameToCanvas,\n type CustomClipHandle,\n} from '@cut-workbench/core/lab'\nimport { formatTimecode } from '@cut-workbench/core'\nimport { LabCard } from './LabCard'\nimport { useLabAction } from '../hooks/use-lab-action'\nimport { Busy, ResultPreview, SliderRow } from './shared'\n\n/**\n * Lab 8:自定义 IClip。\n *\n * `FrameCounterClip` 是本项目实现的 IClip:不依赖任何媒体文件,\n * 逐帧用 OffscreenCanvas 画出帧号 / 进度条 / 色相变化。\n *\n * 它证明 IClip 接口(tick / ready / meta / clone / split / destroy)可以被任意扩展,\n * 且能直接被 Combinator 编码成 MP4。\n */\nexport function CustomClipLab() {\n const [durationSec, setDurationSec] = useState(5)\n const [timeUs, setTimeUs] = useState(0)\n const [playing, setPlaying] = useState(false)\n const [result, setResult] = useState<Blob | null>(null)\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const clipRef = useRef<CustomClipHandle | null>(null)\n const rafRef = useRef<number | null>(null)\n const { busy, run } = useLabAction()\n\n const durationUs = Math.round(durationSec * 1_000_000)\n\n // 重建 clip(时长变化时)\n useEffect(() => {\n clipRef.current?.destroy()\n try {\n clipRef.current = new FrameCounterClip(640, 360, durationUs, 30)\n } catch (err) {\n console.warn('[CustomClipLab] create failed', err)\n clipRef.current = null\n }\n return () => {\n clipRef.current?.destroy()\n clipRef.current = null\n }\n }, [durationUs])\n\n const draw = useCallback(async () => {\n const clip = clipRef.current\n const canvas = canvasRef.current\n if (clip == null || canvas == null) return\n await renderClipFrameToCanvas(clip, timeUs, canvas)\n }, [timeUs])\n\n useEffect(() => {\n void draw()\n }, [draw])\n\n // 播放\n useEffect(() => {\n if (!playing) return\n let last = performance.now()\n let current = timeUs\n const tick = () => {\n const now = performance.now()\n current += (now - last) * 1000\n last = now\n if (current >= durationUs) {\n setTimeUs(durationUs - 1)\n setPlaying(false)\n return\n }\n setTimeUs(current)\n rafRef.current = requestAnimationFrame(tick)\n }\n rafRef.current = requestAnimationFrame(tick)\n return () => {\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [playing, durationUs])\n\n const handleExport = async () => {\n const clip = clipRef.current\n if (clip == null) return\n const output = await run('正在编码自定义 Clip…', () => exportCustomClip(clip, durationUs))\n if (output != null) setResult(output)\n }\n\n return (\n <LabCard\n title=\"自定义 IClip\"\n description=\"纯程序化渲染的 Clip(帧计数器),不依赖任何媒体文件,证明接口可扩展。\"\n apis={['IClip.tick', 'IClip.clone', 'IClip.split', 'Combinator']}\n >\n <div className=\"overflow-hidden rounded-md border bg-stage-bg\">\n <canvas\n ref={canvasRef}\n width={640}\n height={360}\n className=\"block w-full\"\n style={{ maxHeight: 180 }}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n if (!playing && timeUs >= durationUs - 1) setTimeUs(0)\n setPlaying((p) => !p)\n }}\n className=\"flex items-center gap-1 rounded-md bg-secondary px-2 py-1 text-[11px]\"\n >\n {playing ? <Square className=\"size-3\" /> : <Play className=\"size-3\" />}\n {playing ? '停止' : '预览'}\n </button>\n <span className=\"font-mono text-[10px] text-muted-foreground tabular-nums\">\n {formatTimecode(timeUs)} / {formatTimecode(durationUs)}\n </span>\n </div>\n\n <SliderRow\n label=\"时间\"\n value={timeUs}\n min={0}\n max={Math.max(1, durationUs - 1)}\n step={10_000}\n format={(v) => `${(v / 1e6).toFixed(2)}s`}\n onChange={(v) => {\n setPlaying(false)\n setTimeUs(v)\n }}\n />\n <SliderRow\n label=\"时长\"\n value={durationSec}\n min={1}\n max={20}\n step={0.5}\n format={(v) => `${v.toFixed(1)}s`}\n onChange={(v) => {\n setPlaying(false)\n setDurationSec(v)\n setTimeUs(0)\n }}\n />\n\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void handleExport()}\n className=\"rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground disabled:opacity-50\"\n >\n 编码为 MP4\n </button>\n\n {busy != null && <Busy label={busy} />}\n {result != null && <ResultPreview blob={result} filename=\"custom-clip.mp4\" />}\n </LabCard>\n )\n}\n","import { FlaskConical } from 'lucide-react'\r\nimport { ConcatenateLab } from './ConcatenateLab'\r\nimport { RemuxLab } from './RemuxLab'\r\nimport { ChromakeyLab } from './ChromakeyLab'\r\nimport { SubtitleLab } from './SubtitleLab'\r\nimport { TrackSplitLab } from './TrackSplitLab'\r\nimport { ThumbnailLab } from './ThumbnailLab'\r\nimport { RecorderLab } from './RecorderLab'\r\nimport { CustomClipLab } from './CustomClipLab'\r\n\r\n/**\r\n * 能力实验室:每个 WebAV 高级 API 一个可独立运行的卡片。\r\n *\r\n * 与主编辑器共享同一套能力探测结果,因此这里不再重复检测。\r\n */\r\nexport function LabPage() {\r\n return (\r\n <div className=\"mx-auto max-w-7xl space-y-4 p-4\">\r\n <header className=\"space-y-1\">\r\n <div className=\"flex items-center gap-2\">\r\n <FlaskConical className=\"size-4 text-primary\" />\r\n <h1 className=\"text-sm font-semibold\">能力实验室</h1>\r\n </div>\r\n <p className=\"text-xs text-muted-foreground\">\r\n 覆盖 av-cliper / av-canvas / av-recorder 的进阶 API。每个卡片可独立运行,失败时给出可读原因。\r\n </p>\r\n </header>\r\n\r\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-3\">\r\n <ConcatenateLab />\r\n <RemuxLab />\r\n <ChromakeyLab />\r\n <SubtitleLab />\r\n <TrackSplitLab />\r\n <ThumbnailLab />\r\n <RecorderLab />\r\n <CustomClipLab />\r\n </div>\r\n\r\n <footer className=\"border-t pt-3 text-[10px] leading-relaxed text-muted-foreground\">\r\n 说明:所有输出均为 MP4(H.264 + AAC 48kHz 双声道)。WebAV 生成的是 fMP4 分片,\r\n 部分播放器需「修复时长」;浏览器本地处理,文件不会上传。\r\n </footer>\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 开发环境调试桥。\r\n *\r\n * 仅用于自动化测试 / DevTools 检查内部状态(例如验证音频是否真的排程、撤销是否正确)。\r\n * 生产构建会被 `import.meta.env.DEV` 条件完全剔除。\r\n */\r\n\r\nimport type { CutEditorHandle } from '@cut-workbench/core/engine'\r\n\r\nexport function installDebugBridge(editor: CutEditorHandle): void {\r\n if (!import.meta.env.DEV || typeof window === 'undefined') return\r\n Object.assign(window, {\r\n __cutTestEditor: editor,\r\n // 兼容既有测试脚本:它们直接调用 getState() / subscribe()\r\n __cutTestStore: { getState: () => editor.store.getState(), subscribe: editor.store.subscribe },\r\n __cutTestHistory: {\r\n getState: () => editor.history.getState(),\r\n subscribe: editor.history.subscribe,\r\n },\r\n __cutTestAudio: editor.runtime.audio,\r\n __cutTestClock: editor.runtime.clock,\r\n __cutTestRegistry: editor.runtime.clips,\r\n })\r\n}\r\n","/**\r\n * 工作台容器:把「三栏骨架 + 顶栏 + 弹窗」组装成一个可配置组件。\r\n *\r\n * 与旧 `App` 的区别:\r\n * - 不自己创建编辑器(由外层 Provider 注入),因此支持多实例;\r\n * - 顶栏的保存/打开走调用方传入的回调,不写死 IndexedDB;\r\n * - 每个面板都可以被 `slots` 替换,宿主想换掉哪块就换哪块;\r\n * - 不强制能力阻断页(宿主可自行决定)。\r\n */\r\n\r\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { isEnvironmentUsable } from '@cut-workbench/core/engine'\r\nimport { AppShell } from './components/layout/AppShell'\r\nimport { WorkbenchToolbar } from './components/layout/WorkbenchToolbar'\r\nimport { PanelResizer } from './components/layout/PanelResizer'\r\nimport { UnsupportedBrowser } from './components/common/UnsupportedBrowser'\r\nimport { LoadingOverlay } from './components/common/LoadingOverlay'\r\nimport { Toaster } from './components/ui/sonner'\r\nimport { MediaLibraryPanel } from './features/media-library'\r\nimport { PreviewPanel } from './features/preview'\r\nimport { Timeline } from './features/timeline'\r\nimport { InspectorPanel } from './features/inspector'\r\nimport { ExportDialog } from './features/export'\r\nimport { CanvasSettingsDialog } from './features/canvas-settings'\r\nimport { useAddToTimeline, useAssetLifecycle } from './hooks/use-editor-actions'\r\nimport { useKeyboardShortcuts } from './hooks/use-keyboard-shortcuts'\r\nimport { useCapabilityInfo } from './components/layout/use-capability-info'\r\nimport { reportError } from './hooks/use-toast-safe'\r\nimport { useUi, useWorkbench } from './context'\r\nimport { LabPage } from './features/lab'\r\nimport { installDebugBridge } from './app/debug-bridge'\r\n\r\nexport interface CutWorkbenchSlots {\r\n /** 替换内置顶栏;传 `null` 表示不要顶栏 */\r\n topBar?: ReactNode | null\r\n mediaLibrary?: ReactNode\r\n preview?: ReactNode\r\n inspector?: ReactNode\r\n timeline?: ReactNode\r\n}\r\n\r\nexport interface CutWorkbenchProps {\r\n slots?: CutWorkbenchSlots\r\n className?: string\r\n /**\r\n * 工程持久化回调。\r\n *\r\n * 不传则顶栏的「保存 / 打开」按钮隐藏 —— 库不假设宿主用 IndexedDB、文件还是云端。\r\n */\r\n onSaveProject?: () => Promise<void> | void\r\n onOpenProject?: () => Promise<void> | void\r\n /** 是否在能力不足时显示阻断页(宿主自己负责环境校验时可关闭) */\r\n enforceCapability?: boolean\r\n}\r\n\r\n/**\r\n * 工作台主体。\r\n *\r\n * 必须在 `<WorkbenchProvider>` + `<CutEditorProvider>` 之内使用。\r\n */\r\nexport function CutWorkbench({\r\n slots,\r\n className,\r\n onSaveProject,\r\n onOpenProject,\r\n enforceCapability = true,\r\n}: CutWorkbenchProps) {\r\n const editor = useCutEditor()\r\n const capability = useCapabilityInfo()\r\n const leftWidth = useUi((s) => s.leftWidth)\r\n const rightWidth = useUi((s) => s.rightWidth)\r\n const timelineHeight = useUi((s) => s.timelineHeight)\r\n const setLeftWidth = useUi((s) => s.setLeftWidth)\r\n const setRightWidth = useUi((s) => s.setRightWidth)\r\n const setTimelineHeight = useUi((s) => s.setTimelineHeight)\r\n const openDialog = useUi((s) => s.openDialog)\r\n const closeDialog = useUi((s) => s.closeDialog)\r\n const dialog = useUi((s) => s.dialog)\r\n const { config, theme } = useWorkbench()\r\n const route = useUi((s) => s.route)\r\n\r\n const addToTimeline = useAddToTimeline()\r\n const removeAsset = useEditor((s) => s.removeAsset)\r\n const [busy, setBusy] = useState<string | null>(null)\r\n\r\n useAssetLifecycle()\r\n useKeyboardShortcuts()\r\n\r\n // 调试桥只装一次:多次挂载不应重复覆盖 window 上的调试对象\r\n const installed = useRef(false)\r\n useEffect(() => {\r\n if (installed.current) return\r\n installed.current = true\r\n installDebugBridge(editor)\r\n }, [editor])\r\n\r\n // 素材卡片「+」/ 双击:通过事件解耦(feature 之间不互相 import)。\r\n // 事件名保持旧值,现有测试脚本依赖它。\r\n useEffect(() => {\r\n const onRequest = (event: Event) => {\r\n const detail = (event as CustomEvent<{ assetId: string }>).detail\r\n if (detail?.assetId) addToTimeline(detail.assetId)\r\n }\r\n window.addEventListener('cut-test:add-asset-request', onRequest)\r\n return () => window.removeEventListener('cut-test:add-asset-request', onRequest)\r\n }, [addToTimeline])\r\n\r\n const handleExport = useCallback(() => openDialog('export'), [openDialog])\r\n\r\n const handleSave = useCallback(async () => {\r\n if (onSaveProject == null) return\r\n setBusy('正在保存工程…')\r\n try {\r\n await onSaveProject()\r\n editor.store.getState().markSaved()\r\n } catch (err) {\r\n reportError(err, '保存工程失败')\r\n } finally {\r\n setBusy(null)\r\n }\r\n }, [onSaveProject, editor])\r\n\r\n const handleOpen = useCallback(async () => {\r\n if (onOpenProject == null) return\r\n setBusy('正在加载工程…')\r\n try {\r\n await onOpenProject()\r\n editor.history.getState().clear()\r\n } catch (err) {\r\n reportError(err, '加载工程失败')\r\n } finally {\r\n setBusy(null)\r\n }\r\n }, [onOpenProject, editor])\r\n\r\n const toolbar = useMemo(() => {\r\n if (slots?.topBar !== undefined) return slots.topBar\r\n return (\r\n <WorkbenchToolbar\r\n capability={capability}\r\n onExport={handleExport}\r\n onSaveProject={onSaveProject != null ? handleSave : undefined}\r\n onOpenProject={onOpenProject != null ? handleOpen : undefined}\r\n showLab={config.features.lab}\r\n />\r\n )\r\n }, [\r\n slots?.topBar,\r\n capability,\r\n handleExport,\r\n handleSave,\r\n handleOpen,\r\n onSaveProject,\r\n onOpenProject,\r\n config.features.lab,\r\n ])\r\n\r\n if (enforceCapability) {\r\n if (capability.loading) {\r\n return <UnsupportedBrowser report={null} loading error={null} />\r\n }\r\n if (!isEnvironmentUsable(capability.report)) {\r\n return (\r\n <UnsupportedBrowser report={capability.report} loading={false} error={capability.error} />\r\n )\r\n }\r\n }\r\n\r\n if (config.features.lab && route === 'lab') {\r\n return (\r\n <div className=\"flex h-full min-h-0 flex-col\">\r\n {toolbar}\r\n <div className=\"min-h-0 flex-1 overflow-auto\">\r\n <LabPage />\r\n </div>\r\n <ExportDialog />\r\n <CanvasSettingsDialog\r\n open={dialog === 'projectSettings'}\r\n onOpenChange={(next) => (next ? openDialog('projectSettings') : closeDialog())}\r\n />\r\n <LoadingOverlay visible={busy != null} title={busy ?? ''} fullscreen />\r\n <Toaster position=\"bottom-right\" richColors closeButton theme={theme.resolved} />\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex h-full min-h-0 flex-col\">\r\n <AppShell\r\n className={className}\r\n topBar={toolbar}\r\n left={\r\n slots?.mediaLibrary ?? (\r\n <MediaLibraryPanel\r\n onAddToTimeline={addToTimeline}\r\n onRemoveAsset={(assetId) => {\r\n removeAsset(assetId)\r\n window.dispatchEvent(\r\n new CustomEvent('cut-test:asset-removed', { detail: { assetId } }),\r\n )\r\n }}\r\n />\r\n )\r\n }\r\n center={slots?.preview ?? <PreviewPanel />}\r\n right={slots?.inspector ?? <InspectorPanel />}\r\n timeline={slots?.timeline ?? <Timeline />}\r\n leftWidth={leftWidth}\r\n rightWidth={rightWidth}\r\n timelineHeight={timelineHeight}\r\n leftResizer={\r\n <PanelResizer\r\n orientation=\"vertical\"\r\n value={leftWidth}\r\n min={240}\r\n max={520}\r\n onChange={setLeftWidth}\r\n />\r\n }\r\n rightResizer={\r\n <PanelResizer\r\n orientation=\"vertical\"\r\n value={rightWidth}\r\n min={240}\r\n max={480}\r\n invert\r\n onChange={setRightWidth}\r\n />\r\n }\r\n timelineResizer={\r\n <PanelResizer\r\n orientation=\"horizontal\"\r\n value={timelineHeight}\r\n min={180}\r\n max={560}\r\n invert\r\n onChange={setTimelineHeight}\r\n />\r\n }\r\n />\r\n <ExportDialog />\r\n <CanvasSettingsDialog\r\n open={dialog === 'projectSettings'}\r\n onOpenChange={(next) => (next ? openDialog('projectSettings') : closeDialog())}\r\n />\r\n <LoadingOverlay visible={busy != null} title={busy ?? ''} fullscreen />\r\n <Toaster position=\"bottom-right\" richColors closeButton theme={theme.resolved} />\r\n </div>\r\n )\r\n}\r\n","/**\n * 工作台默认宿主桥。\n *\n * 在 `createWebBridge()`(core 提供的基础能力)之上补一层「与构建形态相关」的\n * 部分:导出 Worker 怎么创建。\n *\n * 为什么要单独一层:\n * - core 不能知道 UI 包的 Worker 实现(那会形成反向依赖);\n * - 默认桥必须自己能创建 Worker,否则「不传 bridge」的接入方在 UMD 下会\n * 走不到内联分支,导出直接失败(本项目就是这样发现的)。\n */\n\nimport { createWebBridge, type HostBridge } from '@cut-workbench/core'\nimport { createExportWorker } from './features/export/worker-factory'\n\nexport interface CreateDefaultBridgeOptions {\n /** 覆盖下载文件名 */\n downloadName?: (suggested: string) => string\n}\n\n/** 浏览器内置桥 + 导出 Worker 工厂 */\nexport function createDefaultBridge(options: CreateDefaultBridgeOptions = {}): HostBridge {\n const base = createWebBridge({ downloadName: options.downloadName })\n return {\n ...base,\n createExportWorker: () => createExportWorker(base),\n }\n}\n","// src/utils/env.ts\nvar NOTHING = Symbol.for(\"immer-nothing\");\nvar DRAFTABLE = Symbol.for(\"immer-draftable\");\nvar DRAFT_STATE = Symbol.for(\"immer-state\");\n\n// src/utils/errors.ts\nvar errors = process.env.NODE_ENV !== \"production\" ? [\n // All error codes, starting by 0:\n function(plugin) {\n return `The plugin for '${plugin}' has not been loaded into Immer. To enable the plugin, import and call \\`enable${plugin}()\\` when initializing your application.`;\n },\n function(thing) {\n return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${thing}'`;\n },\n \"This object has been frozen and should not be mutated\",\n function(data) {\n return \"Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? \" + data;\n },\n \"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.\",\n \"Immer forbids circular references\",\n \"The first or second argument to `produce` must be a function\",\n \"The third argument to `produce` must be a function or undefined\",\n \"First argument to `createDraft` must be a plain object, an array, or an immerable object\",\n \"First argument to `finishDraft` must be a draft returned by `createDraft`\",\n function(thing) {\n return `'current' expects a draft, got: ${thing}`;\n },\n \"Object.defineProperty() cannot be used on an Immer draft\",\n \"Object.setPrototypeOf() cannot be used on an Immer draft\",\n \"Immer only supports deleting array indices\",\n \"Immer only supports setting array indices and the 'length' property\",\n function(thing) {\n return `'original' expects a draft, got: ${thing}`;\n }\n // Note: if more errors are added, the errorOffset in Patches.ts should be increased\n // See Patches.ts for additional errors\n] : [];\nfunction die(error, ...args) {\n if (process.env.NODE_ENV !== \"production\") {\n const e = errors[error];\n const msg = isFunction(e) ? e.apply(null, args) : e;\n throw new Error(`[Immer] ${msg}`);\n }\n throw new Error(\n `[Immer] minified error nr: ${error}. Full error at: https://bit.ly/3cXEKWf`\n );\n}\n\n// src/utils/common.ts\nvar O = Object;\nvar getPrototypeOf = O.getPrototypeOf;\nvar CONSTRUCTOR = \"constructor\";\nvar PROTOTYPE = \"prototype\";\nvar CONFIGURABLE = \"configurable\";\nvar ENUMERABLE = \"enumerable\";\nvar WRITABLE = \"writable\";\nvar VALUE = \"value\";\nvar isDraft = (value) => !!value && !!value[DRAFT_STATE];\nfunction isModified(value) {\n if (!isDraft(value))\n return false;\n return value[DRAFT_STATE].modified_;\n}\nfunction isDraftable(value) {\n if (!value)\n return false;\n return isPlainObject(value) || isArray(value) || !!value[DRAFTABLE] || !!value[CONSTRUCTOR]?.[DRAFTABLE] || isMap(value) || isSet(value);\n}\nvar objectCtorString = O[PROTOTYPE][CONSTRUCTOR].toString();\nvar cachedCtorStrings = /* @__PURE__ */ new WeakMap();\nfunction isPlainObject(value) {\n if (!value || !isObjectish(value))\n return false;\n const proto = getPrototypeOf(value);\n if (proto === null || proto === O[PROTOTYPE])\n return true;\n const Ctor = O.hasOwnProperty.call(proto, CONSTRUCTOR) && proto[CONSTRUCTOR];\n if (Ctor === Object)\n return true;\n if (!isFunction(Ctor))\n return false;\n let ctorString = cachedCtorStrings.get(Ctor);\n if (ctorString === void 0) {\n ctorString = Function.toString.call(Ctor);\n cachedCtorStrings.set(Ctor, ctorString);\n }\n return ctorString === objectCtorString;\n}\nfunction original(value) {\n if (!isDraft(value))\n die(15, value);\n return value[DRAFT_STATE].base_;\n}\nfunction each(obj, iter, strict = true) {\n if (getArchtype(obj) === 0 /* Object */) {\n const keys = strict ? Reflect.ownKeys(obj) : O.keys(obj);\n keys.forEach((key) => {\n iter(key, obj[key], obj);\n });\n } else {\n obj.forEach((entry, index) => iter(index, entry, obj));\n }\n}\nfunction getArchtype(thing) {\n const state = thing[DRAFT_STATE];\n return state ? state.type_ : isArray(thing) ? 1 /* Array */ : isMap(thing) ? 2 /* Map */ : isSet(thing) ? 3 /* Set */ : 0 /* Object */;\n}\nvar has = (thing, prop, type = getArchtype(thing)) => type === 2 /* Map */ ? thing.has(prop) : O[PROTOTYPE].hasOwnProperty.call(thing, prop);\nvar get = (thing, prop, type = getArchtype(thing)) => (\n // @ts-ignore\n type === 2 /* Map */ ? thing.get(prop) : thing[prop]\n);\nvar set = (thing, propOrOldValue, value, type = getArchtype(thing)) => {\n if (type === 2 /* Map */)\n thing.set(propOrOldValue, value);\n else if (type === 3 /* Set */) {\n thing.add(value);\n } else\n thing[propOrOldValue] = value;\n};\nfunction is(x, y) {\n if (x === y) {\n return x !== 0 || 1 / x === 1 / y;\n } else {\n return x !== x && y !== y;\n }\n}\nvar isArray = Array.isArray;\nvar isMap = (target) => target instanceof Map;\nvar isSet = (target) => target instanceof Set;\nvar isObjectish = (target) => typeof target === \"object\";\nvar isFunction = (target) => typeof target === \"function\";\nvar isBoolean = (target) => typeof target === \"boolean\";\nfunction isArrayIndex(value) {\n const n = +value;\n return Number.isInteger(n) && String(n) === value;\n}\nvar getProxyDraft = (value) => {\n if (!isObjectish(value))\n return null;\n return value?.[DRAFT_STATE];\n};\nvar latest = (state) => state.copy_ || state.base_;\nvar getValue = (value) => {\n const proxyDraft = getProxyDraft(value);\n return proxyDraft ? getFinalValue(proxyDraft) : value;\n};\nvar getFinalValue = (state) => state.modified_ ? state.copy_ : state.base_;\nfunction shallowCopy(base, strict) {\n if (isMap(base)) {\n return new Map(base);\n }\n if (isSet(base)) {\n return new Set(base);\n }\n if (isArray(base))\n return Array[PROTOTYPE].slice.call(base);\n const isPlain = isPlainObject(base);\n if (strict === true || strict === \"class_only\" && !isPlain) {\n const descriptors = O.getOwnPropertyDescriptors(base);\n delete descriptors[DRAFT_STATE];\n let keys = Reflect.ownKeys(descriptors);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const desc = descriptors[key];\n if (desc[WRITABLE] === false) {\n desc[WRITABLE] = true;\n desc[CONFIGURABLE] = true;\n }\n if (desc.get || desc.set)\n descriptors[key] = {\n [CONFIGURABLE]: true,\n [WRITABLE]: true,\n // could live with !!desc.set as well here...\n [ENUMERABLE]: desc[ENUMERABLE],\n [VALUE]: base[key]\n };\n }\n return O.create(getPrototypeOf(base), descriptors);\n } else {\n const proto = getPrototypeOf(base);\n if (proto !== null && isPlain) {\n return { ...base };\n }\n const obj = O.create(proto);\n return O.assign(obj, base);\n }\n}\nfunction freeze(obj, deep = false) {\n if (isFrozen(obj) || isDraft(obj) || !isDraftable(obj))\n return obj;\n if (getArchtype(obj) > 1) {\n O.defineProperties(obj, {\n set: dontMutateMethodOverride,\n add: dontMutateMethodOverride,\n clear: dontMutateMethodOverride,\n delete: dontMutateMethodOverride\n });\n }\n O.freeze(obj);\n if (deep)\n each(\n obj,\n (_key, value) => {\n freeze(value, true);\n },\n false\n );\n return obj;\n}\nfunction dontMutateFrozenCollections() {\n die(2);\n}\nvar dontMutateMethodOverride = {\n [VALUE]: dontMutateFrozenCollections\n};\nfunction isFrozen(obj) {\n if (obj === null || !isObjectish(obj))\n return true;\n return O.isFrozen(obj);\n}\n\n// src/utils/plugins.ts\nvar PluginMapSet = \"MapSet\";\nvar PluginPatches = \"Patches\";\nvar PluginArrayMethods = \"ArrayMethods\";\nvar plugins = {};\nfunction getPlugin(pluginKey) {\n const plugin = plugins[pluginKey];\n if (!plugin) {\n die(0, pluginKey);\n }\n return plugin;\n}\nvar isPluginLoaded = (pluginKey) => !!plugins[pluginKey];\nfunction loadPlugin(pluginKey, implementation) {\n if (!plugins[pluginKey])\n plugins[pluginKey] = implementation;\n}\n\n// src/core/scope.ts\nvar currentScope;\nvar getCurrentScope = () => currentScope;\nvar createScope = (parent_, immer_) => ({\n drafts_: [],\n parent_,\n immer_,\n // Whenever the modified draft contains a draft from another scope, we\n // need to prevent auto-freezing so the unowned draft can be finalized.\n canAutoFreeze_: true,\n unfinalizedDrafts_: 0,\n handledSet_: /* @__PURE__ */ new Set(),\n processedForPatches_: /* @__PURE__ */ new Set(),\n mapSetPlugin_: isPluginLoaded(PluginMapSet) ? getPlugin(PluginMapSet) : void 0,\n arrayMethodsPlugin_: isPluginLoaded(PluginArrayMethods) ? getPlugin(PluginArrayMethods) : void 0\n});\nfunction usePatchesInScope(scope, patchListener) {\n if (patchListener) {\n scope.patchPlugin_ = getPlugin(PluginPatches);\n scope.patches_ = [];\n scope.inversePatches_ = [];\n scope.patchListener_ = patchListener;\n }\n}\nfunction revokeScope(scope) {\n leaveScope(scope);\n scope.drafts_.forEach(revokeDraft);\n scope.drafts_ = null;\n}\nfunction leaveScope(scope) {\n if (scope === currentScope) {\n currentScope = scope.parent_;\n }\n}\nvar enterScope = (immer2) => currentScope = createScope(currentScope, immer2);\nfunction revokeDraft(draft) {\n const state = draft[DRAFT_STATE];\n if (state.type_ === 0 /* Object */ || state.type_ === 1 /* Array */)\n state.revoke_();\n else\n state.revoked_ = true;\n}\n\n// src/core/finalize.ts\nfunction processResult(result, scope) {\n scope.unfinalizedDrafts_ = scope.drafts_.length;\n const baseDraft = scope.drafts_[0];\n const isReplaced = result !== void 0 && result !== baseDraft;\n if (isReplaced) {\n if (baseDraft[DRAFT_STATE].modified_) {\n revokeScope(scope);\n die(4);\n }\n if (isDraftable(result)) {\n result = finalize(scope, result);\n }\n const { patchPlugin_ } = scope;\n if (patchPlugin_) {\n patchPlugin_.generateReplacementPatches_(\n baseDraft[DRAFT_STATE].base_,\n result,\n scope\n );\n }\n } else {\n result = finalize(scope, baseDraft);\n }\n maybeFreeze(scope, result, true);\n revokeScope(scope);\n if (scope.patches_) {\n scope.patchListener_(scope.patches_, scope.inversePatches_);\n }\n return result !== NOTHING ? result : void 0;\n}\nfunction finalize(rootScope, value) {\n if (isFrozen(value))\n return value;\n const state = value[DRAFT_STATE];\n if (!state) {\n const finalValue = handleValue(value, rootScope.handledSet_, rootScope);\n return finalValue;\n }\n if (!isSameScope(state, rootScope)) {\n return value;\n }\n if (!state.modified_) {\n return state.base_;\n }\n if (!state.finalized_) {\n const { callbacks_ } = state;\n if (callbacks_) {\n while (callbacks_.length > 0) {\n const callback = callbacks_.pop();\n callback(rootScope);\n }\n }\n generatePatchesAndFinalize(state, rootScope);\n }\n return state.copy_;\n}\nfunction maybeFreeze(scope, value, deep = false) {\n if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) {\n freeze(value, deep);\n }\n}\nfunction markStateFinalized(state) {\n state.finalized_ = true;\n state.scope_.unfinalizedDrafts_--;\n}\nvar isSameScope = (state, rootScope) => state.scope_ === rootScope;\nvar EMPTY_LOCATIONS_RESULT = [];\nfunction updateDraftInParent(parent, draftValue, finalizedValue, originalKey) {\n const parentCopy = latest(parent);\n const parentType = parent.type_;\n if (originalKey !== void 0) {\n const currentValue = get(parentCopy, originalKey, parentType);\n if (currentValue === draftValue) {\n set(parentCopy, originalKey, finalizedValue, parentType);\n return;\n }\n }\n if (!parent.draftLocations_) {\n const draftLocations = parent.draftLocations_ = /* @__PURE__ */ new Map();\n each(parentCopy, (key, value) => {\n if (isDraft(value)) {\n const keys = draftLocations.get(value) || [];\n keys.push(key);\n draftLocations.set(value, keys);\n }\n });\n }\n const locations = parent.draftLocations_.get(draftValue) ?? EMPTY_LOCATIONS_RESULT;\n for (const location of locations) {\n set(parentCopy, location, finalizedValue, parentType);\n }\n}\nfunction registerChildFinalizationCallback(parent, child, key) {\n parent.callbacks_.push(function childCleanup(rootScope) {\n const state = child;\n if (!state || !isSameScope(state, rootScope)) {\n return;\n }\n rootScope.mapSetPlugin_?.fixSetContents(state);\n const finalizedValue = getFinalValue(state);\n updateDraftInParent(parent, state.draft_ ?? state, finalizedValue, key);\n generatePatchesAndFinalize(state, rootScope);\n });\n}\nfunction generatePatchesAndFinalize(state, rootScope) {\n const shouldFinalize = state.modified_ && !state.finalized_ && (state.type_ === 3 /* Set */ || state.type_ === 1 /* Array */ && state.allIndicesReassigned_ || (state.assigned_?.size ?? 0) > 0);\n if (shouldFinalize) {\n const { patchPlugin_ } = rootScope;\n if (patchPlugin_) {\n const basePath = patchPlugin_.getPath(state);\n if (basePath) {\n patchPlugin_.generatePatches_(state, basePath, rootScope);\n }\n }\n markStateFinalized(state);\n }\n}\nfunction handleCrossReference(target, key, value) {\n const { scope_ } = target;\n if (isDraft(value)) {\n const state = value[DRAFT_STATE];\n if (isSameScope(state, scope_)) {\n state.callbacks_.push(function crossReferenceCleanup() {\n prepareCopy(target);\n const finalizedValue = getFinalValue(state);\n updateDraftInParent(target, value, finalizedValue, key);\n });\n }\n } else if (isDraftable(value)) {\n target.callbacks_.push(function nestedDraftCleanup() {\n const targetCopy = latest(target);\n if (target.type_ === 3 /* Set */) {\n if (targetCopy.has(value)) {\n handleValue(value, scope_.handledSet_, scope_);\n }\n } else {\n if (get(targetCopy, key, target.type_) === value) {\n if (scope_.drafts_.length > 1 && (target.assigned_.get(key) ?? false) === true && target.copy_) {\n handleValue(\n get(target.copy_, key, target.type_),\n scope_.handledSet_,\n scope_\n );\n }\n }\n }\n });\n }\n}\nfunction handleValue(target, handledSet, rootScope) {\n if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) {\n return target;\n }\n if (isDraft(target) || handledSet.has(target) || !isDraftable(target) || isFrozen(target)) {\n return target;\n }\n handledSet.add(target);\n each(target, (key, value) => {\n if (isDraft(value)) {\n const state = value[DRAFT_STATE];\n if (isSameScope(state, rootScope)) {\n const updatedValue = getFinalValue(state);\n set(target, key, updatedValue, target.type_);\n markStateFinalized(state);\n }\n } else if (isDraftable(value)) {\n handleValue(value, handledSet, rootScope);\n }\n });\n return target;\n}\n\n// src/core/proxy.ts\nfunction createProxyProxy(base, parent) {\n const baseIsArray = isArray(base);\n const state = {\n type_: baseIsArray ? 1 /* Array */ : 0 /* Object */,\n // Track which produce call this is associated with.\n scope_: parent ? parent.scope_ : getCurrentScope(),\n // True for both shallow and deep changes.\n modified_: false,\n // Used during finalization.\n finalized_: false,\n // Track which properties have been assigned (true) or deleted (false).\n // actually instantiated in `prepareCopy()`\n assigned_: void 0,\n // The parent draft state.\n parent_: parent,\n // The base state.\n base_: base,\n // The base proxy.\n draft_: null,\n // set below\n // The base copy with any updated values.\n copy_: null,\n // Called by the `produce` function.\n revoke_: null,\n isManual_: false,\n // `callbacks` actually gets assigned in `createProxy`\n callbacks_: void 0\n };\n let target = state;\n let traps = objectTraps;\n if (baseIsArray) {\n target = [state];\n traps = arrayTraps;\n }\n const { revoke, proxy } = Proxy.revocable(target, traps);\n state.draft_ = proxy;\n state.revoke_ = revoke;\n return [proxy, state];\n}\nvar objectTraps = {\n get(state, prop) {\n if (prop === DRAFT_STATE)\n return state;\n let arrayPlugin = state.scope_.arrayMethodsPlugin_;\n const isArrayWithStringProp = state.type_ === 1 /* Array */ && typeof prop === \"string\";\n if (isArrayWithStringProp) {\n if (arrayPlugin?.isArrayOperationMethod(prop)) {\n return arrayPlugin.createMethodInterceptor(state, prop);\n }\n }\n const source = latest(state);\n if (!has(source, prop, state.type_)) {\n return readPropFromProto(state, source, prop);\n }\n const value = source[prop];\n if (state.finalized_ || !isDraftable(value)) {\n return value;\n }\n if (isArrayWithStringProp && state.operationMethod && arrayPlugin?.isMutatingArrayMethod(\n state.operationMethod\n ) && isArrayIndex(prop)) {\n return value;\n }\n if (value === peek(state.base_, prop) || isRelocatedBaseRef(state, prop, value)) {\n prepareCopy(state);\n const childKey = state.type_ === 1 /* Array */ ? +prop : prop;\n const childDraft = createProxy(state.scope_, value, state, childKey);\n return state.copy_[childKey] = childDraft;\n }\n return value;\n },\n has(state, prop) {\n return prop in latest(state);\n },\n ownKeys(state) {\n return Reflect.ownKeys(latest(state));\n },\n set(state, prop, value) {\n const desc = getDescriptorFromProto(latest(state), prop);\n if (desc?.set) {\n desc.set.call(state.draft_, value);\n return true;\n }\n if (!state.modified_) {\n const current2 = peek(latest(state), prop);\n const currentState = current2?.[DRAFT_STATE];\n if (currentState && currentState.base_ === value) {\n state.copy_[prop] = value;\n state.assigned_.delete(prop);\n return true;\n }\n if (is(value, current2) && (value !== void 0 || has(state.base_, prop, state.type_)))\n return true;\n prepareCopy(state);\n markChanged(state);\n }\n if (state.copy_[prop] === value && // special case: handle new props with value 'undefined'\n (value !== void 0 || has(state.copy_, prop, state.type_)) || // special case: NaN\n Number.isNaN(value) && Number.isNaN(state.copy_[prop]))\n return true;\n state.copy_[prop] = value;\n state.assigned_.set(prop, true);\n handleCrossReference(state, prop, value);\n return true;\n },\n deleteProperty(state, prop) {\n prepareCopy(state);\n if (peek(state.base_, prop) !== void 0 || prop in state.base_) {\n state.assigned_.set(prop, false);\n markChanged(state);\n } else {\n state.assigned_.delete(prop);\n }\n if (state.copy_) {\n delete state.copy_[prop];\n }\n return true;\n },\n // Note: We never coerce `desc.value` into an Immer draft, because we can't make\n // the same guarantee in ES5 mode.\n getOwnPropertyDescriptor(state, prop) {\n const owner = latest(state);\n const desc = Reflect.getOwnPropertyDescriptor(owner, prop);\n if (!desc)\n return desc;\n return {\n [WRITABLE]: true,\n [CONFIGURABLE]: state.type_ !== 1 /* Array */ || prop !== \"length\",\n [ENUMERABLE]: desc[ENUMERABLE],\n [VALUE]: owner[prop]\n };\n },\n defineProperty() {\n die(11);\n },\n getPrototypeOf(state) {\n return getPrototypeOf(state.base_);\n },\n setPrototypeOf() {\n die(12);\n }\n};\nvar arrayTraps = {};\nfor (let key in objectTraps) {\n let fn = objectTraps[key];\n arrayTraps[key] = function() {\n const args = arguments;\n args[0] = args[0][0];\n return fn.apply(this, args);\n };\n}\narrayTraps.deleteProperty = function(state, prop) {\n if (process.env.NODE_ENV !== \"production\" && isNaN(parseInt(prop)))\n die(13);\n return arrayTraps.set.call(this, state, prop, void 0);\n};\narrayTraps.set = function(state, prop, value) {\n if (process.env.NODE_ENV !== \"production\" && prop !== \"length\" && isNaN(parseInt(prop)))\n die(14);\n return objectTraps.set.call(this, state[0], prop, value, state[0]);\n};\nfunction peek(draft, prop) {\n const state = draft[DRAFT_STATE];\n const source = state ? latest(state) : draft;\n return source[prop];\n}\nfunction isRelocatedBaseRef(state, prop, value) {\n if (state.type_ !== 1 /* Array */ || !state.allIndicesReassigned_ || !isDraftable(value) || value[DRAFT_STATE]) {\n return false;\n }\n return state.baseRefs_.has(value);\n}\nfunction readPropFromProto(state, source, prop) {\n const desc = getDescriptorFromProto(source, prop);\n return desc ? VALUE in desc ? desc[VALUE] : (\n // This is a very special case, if the prop is a getter defined by the\n // prototype, we should invoke it with the draft as context!\n desc.get?.call(state.draft_)\n ) : void 0;\n}\nfunction getDescriptorFromProto(source, prop) {\n if (!(prop in source))\n return void 0;\n let proto = getPrototypeOf(source);\n while (proto) {\n const desc = Object.getOwnPropertyDescriptor(proto, prop);\n if (desc)\n return desc;\n proto = getPrototypeOf(proto);\n }\n return void 0;\n}\nfunction markChanged(state) {\n if (!state.modified_) {\n state.modified_ = true;\n if (state.parent_) {\n markChanged(state.parent_);\n }\n }\n}\nfunction prepareCopy(state) {\n if (!state.copy_) {\n state.assigned_ = /* @__PURE__ */ new Map();\n state.copy_ = shallowCopy(\n state.base_,\n state.scope_.immer_.useStrictShallowCopy_\n );\n }\n}\n\n// src/core/immerClass.ts\nvar Immer2 = class {\n constructor(config) {\n this.autoFreeze_ = true;\n this.useStrictShallowCopy_ = false;\n this.useStrictIteration_ = false;\n /**\n * The `produce` function takes a value and a \"recipe function\" (whose\n * return value often depends on the base state). The recipe function is\n * free to mutate its first argument however it wants. All mutations are\n * only ever applied to a __copy__ of the base state.\n *\n * Pass only a function to create a \"curried producer\" which relieves you\n * from passing the recipe function every time.\n *\n * Only plain objects and arrays are made mutable. All other objects are\n * considered uncopyable.\n *\n * Note: This function is __bound__ to its `Immer` instance.\n *\n * @param {any} base - the initial state\n * @param {Function} recipe - function that receives a proxy of the base state as first argument and which can be freely modified\n * @param {Function} patchListener - optional function that will be called with all the patches produced here\n * @returns {any} a new state, or the initial state if nothing was modified\n */\n this.produce = (base, recipe, patchListener) => {\n if (isFunction(base) && !isFunction(recipe)) {\n const defaultBase = recipe;\n recipe = base;\n const self = this;\n return function curriedProduce(base2 = defaultBase, ...args) {\n return self.produce(base2, (draft) => recipe.call(this, draft, ...args));\n };\n }\n if (!isFunction(recipe))\n die(6);\n if (patchListener !== void 0 && !isFunction(patchListener))\n die(7);\n let result;\n if (isDraftable(base)) {\n const scope = enterScope(this);\n const proxy = createProxy(scope, base, void 0);\n let hasError = true;\n try {\n result = recipe(proxy);\n hasError = false;\n } finally {\n if (hasError)\n revokeScope(scope);\n else\n leaveScope(scope);\n }\n usePatchesInScope(scope, patchListener);\n return processResult(result, scope);\n } else if (!base || !isObjectish(base)) {\n result = recipe(base);\n if (result === void 0)\n result = base;\n if (result === NOTHING)\n result = void 0;\n if (this.autoFreeze_)\n freeze(result, true);\n if (patchListener) {\n const p = [];\n const ip = [];\n getPlugin(PluginPatches).generateReplacementPatches_(base, result, {\n patches_: p,\n inversePatches_: ip\n });\n patchListener(p, ip);\n }\n return result;\n } else\n die(1, base);\n };\n this.produceWithPatches = (base, recipe) => {\n if (isFunction(base)) {\n return (state, ...args) => this.produceWithPatches(state, (draft) => base(draft, ...args));\n }\n let patches, inversePatches;\n const result = this.produce(base, recipe, (p, ip) => {\n patches = p;\n inversePatches = ip;\n });\n return [result, patches, inversePatches];\n };\n if (isBoolean(config?.autoFreeze))\n this.setAutoFreeze(config.autoFreeze);\n if (isBoolean(config?.useStrictShallowCopy))\n this.setUseStrictShallowCopy(config.useStrictShallowCopy);\n if (isBoolean(config?.useStrictIteration))\n this.setUseStrictIteration(config.useStrictIteration);\n }\n createDraft(base) {\n if (!isDraftable(base))\n die(8);\n if (isDraft(base))\n base = current(base);\n const scope = enterScope(this);\n const proxy = createProxy(scope, base, void 0);\n proxy[DRAFT_STATE].isManual_ = true;\n leaveScope(scope);\n return proxy;\n }\n finishDraft(draft, patchListener) {\n const state = draft && draft[DRAFT_STATE];\n if (!state || !state.isManual_)\n die(9);\n const { scope_: scope } = state;\n usePatchesInScope(scope, patchListener);\n return processResult(void 0, scope);\n }\n /**\n * Pass true to automatically freeze all copies created by Immer.\n *\n * By default, auto-freezing is enabled.\n */\n setAutoFreeze(value) {\n this.autoFreeze_ = value;\n }\n /**\n * Pass true to enable strict shallow copy.\n *\n * By default, immer does not copy the object descriptors such as getter, setter and non-enumrable properties.\n */\n setUseStrictShallowCopy(value) {\n this.useStrictShallowCopy_ = value;\n }\n /**\n * Pass false to use faster iteration that skips non-enumerable properties\n * but still handles symbols for compatibility.\n *\n * By default, strict iteration is enabled (includes all own properties).\n */\n setUseStrictIteration(value) {\n this.useStrictIteration_ = value;\n }\n shouldUseStrictIteration() {\n return this.useStrictIteration_;\n }\n applyPatches(base, patches) {\n let i;\n for (i = patches.length - 1; i >= 0; i--) {\n const patch = patches[i];\n if (patch.path.length === 0 && patch.op === \"replace\") {\n base = patch.value;\n break;\n }\n }\n if (i > -1) {\n patches = patches.slice(i + 1);\n }\n const applyPatchesImpl = getPlugin(PluginPatches).applyPatches_;\n if (isDraft(base)) {\n return applyPatchesImpl(base, patches);\n }\n return this.produce(\n base,\n (draft) => applyPatchesImpl(draft, patches)\n );\n }\n};\nfunction createProxy(rootScope, value, parent, key) {\n const [draft, state] = isMap(value) ? getPlugin(PluginMapSet).proxyMap_(value, parent) : isSet(value) ? getPlugin(PluginMapSet).proxySet_(value, parent) : createProxyProxy(value, parent);\n const scope = parent?.scope_ ?? getCurrentScope();\n scope.drafts_.push(draft);\n state.callbacks_ = parent?.callbacks_ ?? [];\n state.key_ = key;\n if (parent && key !== void 0) {\n registerChildFinalizationCallback(parent, state, key);\n } else {\n state.callbacks_.push(function rootDraftCleanup(rootScope2) {\n rootScope2.mapSetPlugin_?.fixSetContents(state);\n const { patchPlugin_ } = rootScope2;\n if (state.modified_ && patchPlugin_) {\n patchPlugin_.generatePatches_(state, [], rootScope2);\n }\n });\n }\n return draft;\n}\n\n// src/core/current.ts\nfunction current(value) {\n if (!isDraft(value))\n die(10, value);\n return currentImpl(value);\n}\nfunction currentImpl(value) {\n if (!isDraftable(value) || isFrozen(value))\n return value;\n const state = value[DRAFT_STATE];\n let copy;\n let strict = true;\n if (state) {\n if (!state.modified_)\n return state.base_;\n state.finalized_ = true;\n copy = shallowCopy(value, state.scope_.immer_.useStrictShallowCopy_);\n strict = state.scope_.immer_.shouldUseStrictIteration();\n } else {\n copy = shallowCopy(value, true);\n }\n const source = copy;\n if (isSet(copy))\n copy = /* @__PURE__ */ new Set();\n each(\n source,\n (key, childValue) => {\n set(copy, key, currentImpl(childValue));\n },\n strict\n );\n if (state) {\n state.finalized_ = false;\n }\n return copy;\n}\n\n// src/plugins/patches.ts\nfunction enablePatches() {\n const errorOffset = 16;\n if (process.env.NODE_ENV !== \"production\") {\n errors.push(\n 'Sets cannot have \"replace\" patches.',\n function(op) {\n return \"Unsupported patch operation: \" + op;\n },\n function(path) {\n return \"Cannot apply patch, path doesn't resolve: \" + path;\n },\n \"Patching reserved attributes like __proto__, prototype and constructor is not allowed\"\n );\n }\n function getPath(state, path = []) {\n if (state.key_ !== void 0) {\n const parentCopy = state.parent_.copy_ ?? state.parent_.base_;\n const proxyDraft = getProxyDraft(get(parentCopy, state.key_));\n const valueAtKey = get(parentCopy, state.key_);\n if (valueAtKey === void 0) {\n return null;\n }\n if (valueAtKey !== state.draft_ && valueAtKey !== state.copy_) {\n return null;\n }\n if (proxyDraft != null && proxyDraft.base_ !== state.base_) {\n return null;\n }\n const isSet2 = state.parent_.type_ === 3 /* Set */;\n let key;\n if (isSet2) {\n const setParent = state.parent_;\n key = Array.from(setParent.drafts_.keys()).indexOf(state.key_);\n } else {\n key = state.key_;\n }\n if (!(isSet2 && parentCopy.size > key || has(parentCopy, key))) {\n return null;\n }\n if (state.parent_.type_ === 1 /* Array */ && state.parent_.allIndicesReassigned_) {\n return null;\n }\n path.push(key);\n }\n if (state.parent_) {\n return getPath(state.parent_, path);\n }\n path.reverse();\n try {\n resolvePath(state.copy_, path);\n } catch (e) {\n return null;\n }\n return path;\n }\n function resolvePath(base, path) {\n let current2 = base;\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i];\n current2 = get(current2, key);\n if (!isObjectish(current2) || current2 === null) {\n throw new Error(`Cannot resolve path at '${path.join(\"/\")}'`);\n }\n }\n return current2;\n }\n const REPLACE = \"replace\";\n const ADD = \"add\";\n const REMOVE = \"remove\";\n function generatePatches_(state, basePath, scope) {\n if (state.scope_.processedForPatches_.has(state)) {\n return;\n }\n state.scope_.processedForPatches_.add(state);\n const { patches_, inversePatches_ } = scope;\n switch (state.type_) {\n case 0 /* Object */:\n case 2 /* Map */:\n return generatePatchesFromAssigned(\n state,\n basePath,\n patches_,\n inversePatches_\n );\n case 1 /* Array */:\n return generateArrayPatches(\n state,\n basePath,\n patches_,\n inversePatches_\n );\n case 3 /* Set */:\n return generateSetPatches(\n state,\n basePath,\n patches_,\n inversePatches_\n );\n }\n }\n function generateArrayPatches(state, basePath, patches, inversePatches) {\n let { base_, assigned_ } = state;\n let copy_ = state.copy_;\n if (copy_.length < base_.length) {\n ;\n [base_, copy_] = [copy_, base_];\n [patches, inversePatches] = [inversePatches, patches];\n }\n const allReassigned = state.allIndicesReassigned_ === true;\n for (let i = 0; i < base_.length; i++) {\n const copiedItem = copy_[i];\n const baseItem = base_[i];\n const isAssigned = allReassigned || assigned_?.get(i.toString());\n if (isAssigned && copiedItem !== baseItem) {\n const childState = copiedItem?.[DRAFT_STATE];\n if (childState && childState.modified_) {\n continue;\n }\n const path = basePath.concat([i]);\n patches.push({\n op: REPLACE,\n path,\n // Need to maybe clone it, as it can in fact be the original value\n // due to the base/copy inversion at the start of this function\n value: clonePatchValueIfNeeded(copiedItem)\n });\n inversePatches.push({\n op: REPLACE,\n path,\n value: clonePatchValueIfNeeded(baseItem)\n });\n }\n }\n for (let i = base_.length; i < copy_.length; i++) {\n const path = basePath.concat([i]);\n patches.push({\n op: ADD,\n path,\n // Need to maybe clone it, as it can in fact be the original value\n // due to the base/copy inversion at the start of this function\n value: clonePatchValueIfNeeded(copy_[i])\n });\n }\n for (let i = copy_.length - 1; base_.length <= i; --i) {\n const path = basePath.concat([i]);\n inversePatches.push({\n op: REMOVE,\n path\n });\n }\n }\n function generatePatchesFromAssigned(state, basePath, patches, inversePatches) {\n const { base_, copy_, type_ } = state;\n each(state.assigned_, (key, assignedValue) => {\n const origValue = get(base_, key, type_);\n const value = get(copy_, key, type_);\n const op = !assignedValue ? REMOVE : has(base_, key) ? REPLACE : ADD;\n if (origValue === value && op === REPLACE)\n return;\n const path = basePath.concat(key);\n patches.push(\n op === REMOVE ? { op, path } : { op, path, value: clonePatchValueIfNeeded(value) }\n );\n inversePatches.push(\n op === ADD ? { op: REMOVE, path } : op === REMOVE ? { op: ADD, path, value: clonePatchValueIfNeeded(origValue) } : { op: REPLACE, path, value: clonePatchValueIfNeeded(origValue) }\n );\n });\n }\n function generateSetPatches(state, basePath, patches, inversePatches) {\n let { base_, copy_ } = state;\n let i = 0;\n base_.forEach((value) => {\n if (!copy_.has(value)) {\n const path = basePath.concat([i]);\n patches.push({\n op: REMOVE,\n path,\n value\n });\n inversePatches.unshift({\n op: ADD,\n path,\n value\n });\n }\n i++;\n });\n i = 0;\n copy_.forEach((value) => {\n if (!base_.has(value)) {\n const path = basePath.concat([i]);\n patches.push({\n op: ADD,\n path,\n value\n });\n inversePatches.unshift({\n op: REMOVE,\n path,\n value\n });\n }\n i++;\n });\n }\n function generateReplacementPatches_(baseValue, replacement, scope) {\n const { patches_, inversePatches_ } = scope;\n patches_.push({\n op: REPLACE,\n path: [],\n value: replacement === NOTHING ? void 0 : replacement\n });\n inversePatches_.push({\n op: REPLACE,\n path: [],\n value: baseValue\n });\n }\n function applyPatches_(draft, patches) {\n patches.forEach((patch) => {\n const { path, op } = patch;\n let base = draft;\n for (let i = 0; i < path.length - 1; i++) {\n const parentType = getArchtype(base);\n let p = path[i];\n if (typeof p !== \"string\" && typeof p !== \"number\") {\n p = \"\" + p;\n }\n if ((parentType === 0 /* Object */ || parentType === 1 /* Array */) && (p === \"__proto__\" || p === CONSTRUCTOR))\n die(errorOffset + 3);\n if (isFunction(base) && p === PROTOTYPE)\n die(errorOffset + 3);\n base = get(base, p);\n if (base === null || !isObjectish(base))\n die(errorOffset + 2, path.join(\"/\"));\n }\n const type = getArchtype(base);\n const value = deepClonePatchValue(patch.value);\n const key = path[path.length - 1];\n switch (op) {\n case REPLACE:\n switch (type) {\n case 2 /* Map */:\n return base.set(key, value);\n case 3 /* Set */:\n die(errorOffset);\n default:\n return base[key] = value;\n }\n case ADD:\n switch (type) {\n case 1 /* Array */:\n return key === \"-\" ? base.push(value) : base.splice(key, 0, value);\n case 2 /* Map */:\n return base.set(key, value);\n case 3 /* Set */:\n return base.add(value);\n default:\n return base[key] = value;\n }\n case REMOVE:\n switch (type) {\n case 1 /* Array */:\n return base.splice(key, 1);\n case 2 /* Map */:\n return base.delete(key);\n case 3 /* Set */:\n return base.delete(patch.value);\n default:\n return delete base[key];\n }\n default:\n die(errorOffset + 1, op);\n }\n });\n return draft;\n }\n function deepClonePatchValue(obj) {\n if (!isDraftable(obj))\n return obj;\n if (isArray(obj))\n return obj.map(deepClonePatchValue);\n if (isMap(obj))\n return new Map(\n Array.from(obj.entries()).map(([k, v]) => [k, deepClonePatchValue(v)])\n );\n if (isSet(obj))\n return new Set(Array.from(obj).map(deepClonePatchValue));\n const cloned = Object.create(getPrototypeOf(obj));\n for (const key in obj)\n cloned[key] = deepClonePatchValue(obj[key]);\n if (has(obj, DRAFTABLE))\n cloned[DRAFTABLE] = obj[DRAFTABLE];\n return cloned;\n }\n function clonePatchValueIfNeeded(obj) {\n if (isDraft(obj)) {\n return deepClonePatchValue(obj);\n } else\n return obj;\n }\n loadPlugin(PluginPatches, {\n applyPatches_,\n generatePatches_,\n generateReplacementPatches_,\n getPath\n });\n}\n\n// src/plugins/mapset.ts\nvar _globalIterator = globalThis.Iterator;\nvar hasIteratorFrom = typeof _globalIterator?.from === \"function\";\nfunction enableMapSet() {\n class DraftMap extends Map {\n constructor(target, parent) {\n super();\n this[DRAFT_STATE] = {\n type_: 2 /* Map */,\n parent_: parent,\n scope_: parent ? parent.scope_ : getCurrentScope(),\n modified_: false,\n finalized_: false,\n copy_: void 0,\n assigned_: void 0,\n base_: target,\n draft_: this,\n isManual_: false,\n revoked_: false,\n callbacks_: []\n };\n }\n get size() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n return latest(state).size;\n }\n has(key) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n return latest(state).has(key);\n }\n set(key, value) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (!latest(state).has(key) || latest(state).get(key) !== value) {\n prepareMapCopy(state);\n markChanged(state);\n state.assigned_.set(key, true);\n state.copy_.set(key, value);\n state.assigned_.set(key, true);\n handleCrossReference(state, key, value);\n }\n return this;\n }\n delete(key) {\n if (!this.has(key)) {\n return false;\n }\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareMapCopy(state);\n markChanged(state);\n if (state.base_.has(key)) {\n state.assigned_.set(key, false);\n } else {\n state.assigned_.delete(key);\n }\n state.copy_.delete(key);\n return true;\n }\n clear() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (latest(state).size) {\n prepareMapCopy(state);\n markChanged(state);\n state.assigned_ = /* @__PURE__ */ new Map();\n each(state.base_, (key) => {\n state.assigned_.set(key, false);\n });\n state.copy_.clear();\n }\n }\n forEach(cb, thisArg) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareMapCopy(state);\n latest(state).forEach((_value, key, _map) => {\n cb.call(thisArg, this.get(key), key, this);\n });\n }\n get(key) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n const value = latest(state).get(key);\n if (state.finalized_ || !isDraftable(value)) {\n return value;\n }\n if (value !== state.base_.get(key)) {\n return value;\n }\n const draft = createProxy(state.scope_, value, state, key);\n prepareMapCopy(state);\n state.copy_.set(key, draft);\n return draft;\n }\n keys() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareMapCopy(state);\n return latest(state).keys();\n }\n values() {\n const iterator = this.keys();\n return iteratorFrom({\n next: () => {\n const r = iterator.next();\n if (r.done)\n return r;\n const value = this.get(r.value);\n return {\n done: false,\n value\n };\n }\n });\n }\n entries() {\n const iterator = this.keys();\n return iteratorFrom({\n next: () => {\n const r = iterator.next();\n if (r.done)\n return r;\n const value = this.get(r.value);\n return {\n done: false,\n value: [r.value, value]\n };\n }\n });\n }\n [(DRAFT_STATE, Symbol.iterator)]() {\n return this.entries();\n }\n }\n function iteratorFrom(iterable) {\n if (hasIteratorFrom) {\n return _globalIterator.from(iterable);\n }\n const iterator = {\n ...iterable,\n [Symbol.iterator]: () => iterator\n };\n return iterator;\n }\n function proxyMap_(target, parent) {\n const map = new DraftMap(target, parent);\n return [map, map[DRAFT_STATE]];\n }\n function prepareMapCopy(state) {\n if (!state.copy_) {\n state.assigned_ = /* @__PURE__ */ new Map();\n state.copy_ = new Map(state.base_);\n }\n }\n class DraftSet extends Set {\n constructor(target, parent) {\n super();\n this[DRAFT_STATE] = {\n type_: 3 /* Set */,\n parent_: parent,\n scope_: parent ? parent.scope_ : getCurrentScope(),\n modified_: false,\n finalized_: false,\n copy_: void 0,\n base_: target,\n draft_: this,\n drafts_: /* @__PURE__ */ new Map(),\n revoked_: false,\n isManual_: false,\n assigned_: void 0,\n callbacks_: []\n };\n }\n get size() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n return latest(state).size;\n }\n has(value) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (!state.copy_) {\n return state.base_.has(value);\n }\n if (state.copy_.has(value))\n return true;\n if (state.drafts_.has(value) && state.copy_.has(state.drafts_.get(value)))\n return true;\n return false;\n }\n add(value) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (!this.has(value)) {\n prepareSetCopy(state);\n markChanged(state);\n state.copy_.add(value);\n handleCrossReference(state, value, value);\n }\n return this;\n }\n delete(value) {\n if (!this.has(value)) {\n return false;\n }\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareSetCopy(state);\n markChanged(state);\n return state.copy_.delete(value) || (state.drafts_.has(value) ? state.copy_.delete(state.drafts_.get(value)) : (\n /* istanbul ignore next */\n false\n ));\n }\n clear() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (latest(state).size) {\n prepareSetCopy(state);\n markChanged(state);\n state.copy_.clear();\n }\n }\n values() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareSetCopy(state);\n return state.copy_.values();\n }\n entries() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareSetCopy(state);\n return state.copy_.entries();\n }\n keys() {\n return this.values();\n }\n [(DRAFT_STATE, Symbol.iterator)]() {\n return this.values();\n }\n forEach(cb, thisArg) {\n const iterator = this.values();\n let result = iterator.next();\n while (!result.done) {\n cb.call(thisArg, result.value, result.value, this);\n result = iterator.next();\n }\n }\n }\n function proxySet_(target, parent) {\n const set2 = new DraftSet(target, parent);\n return [set2, set2[DRAFT_STATE]];\n }\n function prepareSetCopy(state) {\n if (!state.copy_) {\n state.copy_ = /* @__PURE__ */ new Set();\n state.base_.forEach((value) => {\n if (isDraftable(value)) {\n const draft = createProxy(state.scope_, value, state, value);\n state.drafts_.set(value, draft);\n state.copy_.add(draft);\n } else {\n state.copy_.add(value);\n }\n });\n }\n }\n function assertUnrevoked(state) {\n if (state.revoked_)\n die(3, JSON.stringify(latest(state)));\n }\n function fixSetContents(target) {\n if (target.type_ === 3 /* Set */ && target.copy_) {\n const copy = new Set(target.copy_);\n target.copy_.clear();\n copy.forEach((value) => {\n target.copy_.add(getValue(value));\n });\n }\n }\n loadPlugin(PluginMapSet, { proxyMap_, proxySet_, fixSetContents });\n}\n\n// src/plugins/arrayMethods.ts\nfunction enableArrayMethods() {\n const SHIFTING_METHODS = /* @__PURE__ */ new Set([\"shift\", \"unshift\"]);\n const QUEUE_METHODS = /* @__PURE__ */ new Set([\"push\", \"pop\"]);\n const RESULT_RETURNING_METHODS = /* @__PURE__ */ new Set([\n ...QUEUE_METHODS,\n ...SHIFTING_METHODS\n ]);\n const REORDERING_METHODS = /* @__PURE__ */ new Set([\"reverse\", \"sort\"]);\n const MUTATING_METHODS = /* @__PURE__ */ new Set([\n ...RESULT_RETURNING_METHODS,\n ...REORDERING_METHODS,\n \"splice\"\n ]);\n const FIND_METHODS = /* @__PURE__ */ new Set([\"find\", \"findLast\"]);\n const NON_MUTATING_METHODS = /* @__PURE__ */ new Set([\n \"filter\",\n \"slice\",\n \"concat\",\n \"flat\",\n ...FIND_METHODS,\n \"findIndex\",\n \"findLastIndex\",\n \"some\",\n \"every\",\n \"indexOf\",\n \"lastIndexOf\",\n \"includes\",\n \"join\",\n \"toString\",\n \"toLocaleString\"\n ]);\n function isMutatingArrayMethod(method) {\n return MUTATING_METHODS.has(method);\n }\n function isNonMutatingArrayMethod(method) {\n return NON_MUTATING_METHODS.has(method);\n }\n function isArrayOperationMethod(method) {\n return isMutatingArrayMethod(method) || isNonMutatingArrayMethod(method);\n }\n function enterOperation(state, method) {\n state.operationMethod = method;\n }\n function exitOperation(state) {\n state.operationMethod = void 0;\n }\n function executeArrayMethod(state, operation, markLength = true) {\n prepareCopy(state);\n const result = operation();\n markArrayChanged(state, markLength);\n return result;\n }\n function markArrayChanged(state, markLength = true) {\n markChanged(state);\n if (markLength)\n state.assigned_.set(\"length\", true);\n }\n function markAllIndicesReassigned(state) {\n state.allIndicesReassigned_ = true;\n state.baseRefs_ = new Set(state.base_);\n }\n function normalizeSliceIndex(index, length) {\n if (index < 0) {\n return Math.max(length + index, 0);\n }\n return Math.min(index, length);\n }\n function handleInsertedValues(state, startIndex, values) {\n for (let i = 0; i < values.length; i++) {\n const index = \"\" + (startIndex + i);\n state.assigned_.set(index, true);\n handleCrossReference(state, index, values[i]);\n }\n }\n function handleSimpleOperation(state, method, args) {\n const isInsert = method === \"push\" || method === \"unshift\";\n if (isInsert ? args.length === 0 : latest(state).length === 0) {\n return isInsert ? latest(state).length : void 0;\n }\n prepareCopy(state);\n const lengthBefore = state.copy_.length;\n const result = state.copy_[method](...args);\n markArrayChanged(state);\n if (SHIFTING_METHODS.has(method)) {\n markAllIndicesReassigned(state);\n }\n if (method === \"push\") {\n handleInsertedValues(state, lengthBefore, args);\n } else if (method === \"unshift\") {\n handleInsertedValues(state, 0, args);\n }\n return RESULT_RETURNING_METHODS.has(method) ? result : state.draft_;\n }\n function handleReorderingOperation(state, method, args) {\n if (latest(state).length <= 1)\n return state.draft_;\n return executeArrayMethod(\n state,\n () => {\n ;\n state.copy_[method](...args);\n markAllIndicesReassigned(state);\n return state.draft_;\n },\n false\n );\n }\n function createMethodInterceptor(state, originalMethod) {\n return function interceptedMethod(...args) {\n const method = originalMethod;\n enterOperation(state, method);\n try {\n if (isMutatingArrayMethod(method)) {\n if (RESULT_RETURNING_METHODS.has(method)) {\n return handleSimpleOperation(state, method, args);\n }\n if (REORDERING_METHODS.has(method)) {\n return handleReorderingOperation(state, method, args);\n }\n if (method === \"splice\") {\n const insertCount = args.length > 2 ? args.length - 2 : 0;\n if (insertCount === 0) {\n const length = latest(state).length;\n if (args.length === 0 || normalizeSliceIndex(args[0] ?? 0, length) === length || args.length > 1 && !(args[1] >= 1)) {\n return [];\n }\n }\n prepareCopy(state);\n const lengthBefore = state.copy_.length;\n const res = state.copy_.splice(\n ...args\n );\n if (res.length === 0 && insertCount === 0) {\n return res;\n }\n markArrayChanged(state);\n markAllIndicesReassigned(state);\n if (insertCount > 0) {\n const startIndex = normalizeSliceIndex(args[0] ?? 0, lengthBefore);\n handleInsertedValues(state, startIndex, args.slice(2));\n }\n return res;\n }\n } else {\n return handleNonMutatingOperation(state, method, args);\n }\n } finally {\n exitOperation(state);\n }\n };\n }\n function handleNonMutatingOperation(state, method, args) {\n const source = latest(state);\n if (method === \"filter\") {\n const predicate = args[0];\n const thisArg = args[1];\n const result = [];\n const length = source.length;\n for (let i = 0; i < length; i++) {\n if (predicate.call(thisArg, source[i], i, source)) {\n result.push(state.draft_[i]);\n }\n }\n return result;\n }\n if (FIND_METHODS.has(method)) {\n const predicate = args[0];\n const thisArg = args[1];\n const length = source.length;\n const isForward = method === \"find\";\n const step = isForward ? 1 : -1;\n const start = isForward ? 0 : length - 1;\n for (let i = start; i >= 0 && i < length; i += step) {\n if (predicate.call(thisArg, source[i], i, source)) {\n return state.draft_[i];\n }\n }\n return void 0;\n }\n if (method === \"slice\") {\n const rawStart = args[0] ?? 0;\n const rawEnd = args[1] ?? source.length;\n const start = normalizeSliceIndex(rawStart, source.length);\n const end = normalizeSliceIndex(rawEnd, source.length);\n const result = [];\n for (let i = start; i < end; i++) {\n result.push(state.draft_[i]);\n }\n return result;\n }\n return source[method](...args);\n }\n loadPlugin(PluginArrayMethods, {\n createMethodInterceptor,\n isArrayOperationMethod,\n isMutatingArrayMethod\n });\n}\n\n// src/immer.ts\nvar immer = new Immer2();\nvar produce = immer.produce;\nvar produceWithPatches = /* @__PURE__ */ immer.produceWithPatches.bind(immer);\nvar setAutoFreeze = /* @__PURE__ */ immer.setAutoFreeze.bind(immer);\nvar setUseStrictShallowCopy = /* @__PURE__ */ immer.setUseStrictShallowCopy.bind(immer);\nvar setUseStrictIteration = /* @__PURE__ */ immer.setUseStrictIteration.bind(immer);\nvar applyPatches = /* @__PURE__ */ immer.applyPatches.bind(immer);\nvar createDraft = /* @__PURE__ */ immer.createDraft.bind(immer);\nvar finishDraft = /* @__PURE__ */ immer.finishDraft.bind(immer);\nvar castDraft = (value) => value;\nvar castImmutable = (value) => value;\nexport {\n Immer2 as Immer,\n applyPatches,\n castDraft,\n castImmutable,\n createDraft,\n current,\n enableArrayMethods,\n enableMapSet,\n enablePatches,\n finishDraft,\n freeze,\n DRAFTABLE as immerable,\n isDraft,\n isDraftable,\n isModified,\n NOTHING as nothing,\n original,\n produce,\n produceWithPatches,\n setAutoFreeze,\n setUseStrictIteration,\n setUseStrictShallowCopy\n};\n//# sourceMappingURL=immer.mjs.map","import { produce } from 'immer';\n\nconst immerImpl = (initializer) => (set, get, store) => {\n store.setState = (updater, replace, ...args) => {\n const nextState = typeof updater === \"function\" ? produce(updater) : updater;\n return set(nextState, replace, ...args);\n };\n return initializer(store.setState, get, store);\n};\nconst immer = immerImpl;\n\nexport { immer };\n","/**\r\n * UI 状态:面板尺寸、缩放、对话框开关。\r\n *\r\n * 与编辑数据无关的一切交互状态都放这里,避免 editor-store 变成上帝对象。\r\n *\r\n * ⚠️ 使用 vanilla store 工厂而非模块级单例:同一页面挂两个工作台时,\r\n * 面板宽度、缩放、对话框都必须各算各的。React 侧通过 `useUiStore` 订阅。\r\n */\r\n\r\nimport { createStore, type StoreApi } from 'zustand/vanilla'\r\nimport { immer } from 'zustand/middleware/immer'\r\nimport { DEFAULT_PX_PER_SECOND, MAX_PX_PER_SECOND, MIN_PX_PER_SECOND } from '@/lib/constants'\r\n\r\nexport type Route = 'editor' | 'lab'\r\n\r\nexport interface UiState {\r\n route: Route\r\n /** 左侧素材面板宽度(像素) */\r\n leftWidth: number\r\n /** 右侧属性面板宽度(像素) */\r\n rightWidth: number\r\n /** 底部时间轴高度(像素) */\r\n timelineHeight: number\r\n /** 时间轴横向缩放(每秒像素) */\r\n pxPerSecond: number\r\n /** 时间轴横向滚动位置(像素) */\r\n scrollLeftPx: number\r\n /** 当前打开的对话框 */\r\n dialog: 'export' | 'mediaMeta' | 'projectSettings' | null\r\n dialogAssetId: string | null\r\n /** 正在加载的素材数量,用于骨架屏 */\r\n importingCount: number\r\n\r\n setRoute: (route: Route) => void\r\n setLeftWidth: (width: number) => void\r\n setRightWidth: (width: number) => void\r\n setTimelineHeight: (height: number) => void\r\n setPxPerSecond: (value: number) => void\r\n setScrollLeftPx: (value: number) => void\r\n openDialog: (dialog: UiState['dialog'], assetId?: string | null) => void\r\n closeDialog: () => void\r\n beginImport: () => void\r\n endImport: () => void\r\n}\r\n\r\nfunction clampPxPerSecond(value: number): number {\r\n return Math.max(MIN_PX_PER_SECOND, Math.min(MAX_PX_PER_SECOND, value))\r\n}\r\n\r\nexport type UiStore = StoreApi<UiState>\r\n\r\n/** 每个工作台实例一份 UI 状态 */\r\nexport function createUiStore(): UiStore {\r\n return createStore<UiState>()(\r\n immer((set) => ({\r\n route: 'editor',\r\n leftWidth: 320,\r\n rightWidth: 300,\r\n timelineHeight: 280,\r\n pxPerSecond: DEFAULT_PX_PER_SECOND,\r\n scrollLeftPx: 0,\r\n dialog: null,\r\n dialogAssetId: null,\r\n importingCount: 0,\r\n\r\n setRoute: (route) =>\r\n set((draft) => {\r\n draft.route = route\r\n }),\r\n\r\n setLeftWidth: (width) =>\r\n set((draft) => {\r\n draft.leftWidth = Math.max(240, Math.min(520, width))\r\n }),\r\n\r\n setRightWidth: (width) =>\r\n set((draft) => {\r\n draft.rightWidth = Math.max(240, Math.min(480, width))\r\n }),\r\n\r\n setTimelineHeight: (height) =>\r\n set((draft) => {\r\n draft.timelineHeight = Math.max(180, Math.min(560, height))\r\n }),\r\n\r\n setPxPerSecond: (value) =>\r\n set((draft) => {\r\n draft.pxPerSecond = clampPxPerSecond(value)\r\n }),\r\n\r\n setScrollLeftPx: (value) =>\r\n set((draft) => {\r\n draft.scrollLeftPx = Math.max(0, value)\r\n }),\r\n\r\n openDialog: (dialog, assetId = null) =>\r\n set((draft) => {\r\n draft.dialog = dialog\r\n draft.dialogAssetId = assetId\r\n }),\r\n\r\n closeDialog: () =>\r\n set((draft) => {\r\n draft.dialog = null\r\n draft.dialogAssetId = null\r\n }),\r\n\r\n beginImport: () =>\r\n set((draft) => {\r\n draft.importingCount += 1\r\n }),\r\n\r\n endImport: () =>\r\n set((draft) => {\r\n draft.importingCount = Math.max(0, draft.importingCount - 1)\r\n }),\r\n })),\r\n )\r\n}\r\n","/**\n * 主题解析:把 `theme=\"inherit\"` 真正接到宿主的深色约定上。\n *\n * 背景(为什么要这个文件):\n * 库内所有暗色变体(`dark:bg-...`)都由 `data-cw-theme=\"dark\"` 驱动,而 `inherit`\n * 模式下根节点**不写**这个属性 —— 于是 `.cw-root` 的亮色默认值生效,\n * `inherit` 实际等价于「强制浅色」,与文档承诺的「完全跟随宿主」名不副实。\n *\n * 这里读取宿主最通用的约定 `<html class=\"dark\">`,用 MutationObserver 持续跟随,\n * 折算成库自己的 `data-cw-theme`。库仍然只有一套主题机制,宿主不需要改任何代码。\n *\n * 为什么不用 `prefers-color-scheme`:宿主设计系统通常有「跟随系统 / 手动」两种\n * 策略,最终都落在 `<html class=\"dark\">` 上;直接读 class 才与宿主界面一致。\n */\n\nimport { useSyncExternalStore } from 'react'\n\nexport type ResolvedTheme = 'light' | 'dark'\n\n/** 读取宿主 `<html>` 上是否有 `dark` 类;无 DOM 环境(SSR)返回 false */\nfunction getHostDark(): boolean {\n if (typeof document === 'undefined') return false\n return document.documentElement.classList.contains('dark')\n}\n\n/** 订阅宿主根节点 class 变化 */\nfunction subscribeHostDark(onStoreChange: () => void): () => void {\n if (typeof document === 'undefined') return () => undefined\n const observer = new MutationObserver(onStoreChange)\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n return () => observer.disconnect()\n}\n\n/**\n * 持续跟随宿主 `<html class=\"dark\">`。\n *\n * 用 `useSyncExternalStore` 而不是 effect + state:快照是纯布尔值,\n * 不会因为 React 的批处理时序漏掉变化,也不会在 StrictMode 双调用下抖动。\n */\nexport function useHostDarkClass(): boolean {\n return useSyncExternalStore(subscribeHostDark, getHostDark, () => false)\n}\n\n/** 显式主题原样返回;`inherit` 折算为宿主当前的亮/暗 */\nexport function resolveTheme(\n theme: 'light' | 'dark' | 'inherit',\n hostDark: boolean,\n): ResolvedTheme {\n return theme === 'inherit' ? (hostDark ? 'dark' : 'light') : theme\n}\n","/**\n * 主题 token 注入接口。\n *\n * 宿主换肤不该依赖「自己写一份 CSS 并祈祷引入顺序 / 特异性正确」。`tokens` 通过\n * 根节点的 **inline style** 写入 `--cw-*` 变量:\n * - inline 声明的优先级高于任何样式表规则(包括 `[data-cw-theme=\"dark\"]` 的 token 块),\n * 因此宿主不需要处理 CSS 引入顺序;\n * - 变量仍挂在 `.cw-root` 上(不是 `:root`),不会污染宿主页面的同名变量。\n *\n * 用法:\n * ```tsx\n * <CutWorkbenchProvider theme=\"dark\" tokens={{ primary: '#4d6bfe', radius: '8px' }}>\n * ```\n *\n * 需要运行时换肤但不走 React 的宿主,可以用 {@link CUT_THEME_TOKEN_NAMES}\n * 拿到全部变量名,自己写 CSS 或 `style.setProperty()`。\n */\n\nimport type { CSSProperties } from 'react'\n\n/**\n * 所有可覆盖的 token。\n *\n * 值必须是合法的 CSS 颜色 / 长度(例如 `'#4d6bfe'`、`'rgba(0,0,0,.6)'`、`'8px'`、\n * `\"Segoe UI, system-ui, sans-serif\"`)。非法值会被浏览器丢弃,回退到内置的亮/暗默认值。\n */\nexport interface CutThemeTokens {\n // —— 基础色板(与 shadcn 命名一致)——\n background?: string\n foreground?: string\n card?: string\n cardForeground?: string\n popover?: string\n popoverForeground?: string\n primary?: string\n primaryForeground?: string\n secondary?: string\n secondaryForeground?: string\n muted?: string\n mutedForeground?: string\n accent?: string\n accentForeground?: string\n destructive?: string\n destructiveForeground?: string\n /** 警示色(源文件缺失、规格不一致等) */\n warning?: string\n warningForeground?: string\n /** 成功色(能力探测通过等) */\n success?: string\n border?: string\n input?: string\n ring?: string\n // —— 形状与字体 ——\n radius?: string\n fontSans?: string\n fontMono?: string\n // —— 编辑器专属 ——\n timelineBg?: string\n timelineTrack?: string\n timelineGrid?: string\n clipVideo?: string\n clipAudio?: string\n clipImage?: string\n clipText?: string\n clipSubtitle?: string\n playhead?: string\n /** 预览舞台 / Lab 预览区的底色(跟随主题) */\n stageBg?: string\n /** 对话框遮罩(永远是半透明黑,与主题无关) */\n overlay?: string\n /** 压在图片/视频上的角标底色(永远深底,与主题无关) */\n badgeScrim?: string\n /** 角标文字色(永远浅字) */\n badgeScrimForeground?: string\n}\n\n/** token key → `--cw-kebab-case` */\nfunction toCssVarName(key: string): string {\n return `--cw-${key.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`)}`\n}\n\n/**\n * 全部可注入的 token 变量名(含 `--cw-` 前缀)。\n *\n * 供宿主自检(例如校验设计令牌是否齐全)或非 React 场景下手写 CSS。\n */\nexport const CUT_THEME_TOKEN_NAMES: readonly string[] = Object.freeze([\n 'background',\n 'foreground',\n 'card',\n 'cardForeground',\n 'popover',\n 'popoverForeground',\n 'primary',\n 'primaryForeground',\n 'secondary',\n 'secondaryForeground',\n 'muted',\n 'mutedForeground',\n 'accent',\n 'accentForeground',\n 'destructive',\n 'destructiveForeground',\n 'warning',\n 'warningForeground',\n 'success',\n 'border',\n 'input',\n 'ring',\n 'radius',\n 'fontSans',\n 'fontMono',\n 'timelineBg',\n 'timelineTrack',\n 'timelineGrid',\n 'clipVideo',\n 'clipAudio',\n 'clipImage',\n 'clipText',\n 'clipSubtitle',\n 'playhead',\n 'stageBg',\n 'overlay',\n 'badgeScrim',\n 'badgeScrimForeground',\n].map(toCssVarName))\n\n/**\n * 把 token 对象转换成可直接展开到 `style` 的 CSS 变量集合。\n *\n * 空值 / 非字符串会被跳过(例如宿主从 JSON 配置里读到了 `undefined`),\n * 这样未传的 token 保持内置默认值,不会被空字符串覆盖成无效值。\n */\nexport function tokensToCssVars(tokens?: CutThemeTokens): CSSProperties {\n if (tokens == null) return {}\n const style: Record<string, string> = {}\n for (const [key, value] of Object.entries(tokens)) {\n if (typeof value !== 'string' || value.trim().length === 0) continue\n style[toCssVarName(key)] = value\n }\n return style as CSSProperties\n}\n","/**\n * 工作台 Provider:UI 状态 + 主题 + Portal 容器。\n *\n * 与 `CutEditorProvider`(数据与引擎)分开:\n * - `CutEditorProvider` 由 `@cut-workbench/react` 提供,决定「编辑哪个工程」;\n * - `CutWorkbenchProvider` 决定「这一次挂载的布局、主题和宿主桥」。\n *\n * 这样宿主可以在切换主题/布局时重建 UI,而不必重建编辑器(反之亦然)。\n */\n\nimport {\n useCallback,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport { useCutEditor } from '@cut-workbench/react'\nimport type { HostBridge } from '@cut-workbench/core'\nimport { createDefaultBridge } from './default-bridge'\nimport { TooltipProvider } from './components/ui/tooltip'\nimport {\n DEFAULT_CONFIG,\n WorkbenchContext,\n type WorkbenchConfig,\n type WorkbenchContextValue,\n} from './context'\nimport { PortalContainerProvider } from './portal-container'\nimport { createUiStore } from './store/ui-store'\nimport { resolveTheme, useHostDarkClass } from './theme'\nimport { tokensToCssVars, type CutThemeTokens } from './theme-tokens'\n\nexport type { WorkbenchTheme, WorkbenchConfig } from './context'\nexport { useWorkbench, useUi, useUiStoreApi } from './context'\n\nexport interface CutWorkbenchProviderProps {\n children: ReactNode\n /** 宿主桥;不传则使用浏览器内置实现 */\n bridge?: HostBridge\n /**\n * 主题:\n * - `light` / `dark`:固定主题,不理会宿主;\n * - `inherit`:跟随宿主 `<html class=\"dark\">`(MutationObserver 实时同步),\n * 折算成根节点的 `data-cw-theme`,因此库内 `dark:` 变体同样生效。\n */\n theme?: 'light' | 'dark' | 'inherit'\n /**\n * 覆盖任意 `--cw-*` token(key 不带 `--cw-` 前缀)。\n * 通过根节点 inline style 应用,不依赖宿主的 CSS 引入顺序与特异性。\n */\n tokens?: CutThemeTokens\n /** 功能开关(只合并顶层字段) */\n config?: Partial<Omit<WorkbenchConfig, 'features'>> & {\n features?: Partial<WorkbenchConfig['features']>\n }\n className?: string\n}\n\n/**\n * 工作台根 Provider。\n *\n * 同时负责:\n * 1. 创建本次挂载的 UI store(面板尺寸、缩放、对话框);\n * 2. 提供 Radix portal 的挂载点(样式作用域与 z-index 都依赖它);\n * 3. 设置 `data-cw-theme`,让库内 token 生效。\n */\nexport function CutWorkbenchProvider({\n children,\n bridge,\n theme = 'dark',\n tokens,\n config,\n className,\n}: CutWorkbenchProviderProps) {\n // 必须有编辑器才能确定「这个 UI 属于哪个工程」,顺带在缺 Provider 时尽早报错\n useCutEditor()\n\n const [ui] = useState(() => createUiStore())\n const resolvedBridge = useMemo(() => bridge ?? createDefaultBridge(), [bridge])\n\n const mergedConfig = useMemo<WorkbenchConfig>(\n () => ({\n ...DEFAULT_CONFIG,\n ...config,\n features: { ...DEFAULT_CONFIG.features, ...(config?.features ?? {}) },\n }),\n [config],\n )\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)\n const rootRef = useRef<HTMLDivElement | null>(null)\n\n // callback ref:挂载/卸载都走同一条路径,不需要额外 effect\n const attachRoot = useCallback((node: HTMLDivElement | null) => {\n rootRef.current = node\n setPortalContainer(node)\n }, [])\n\n // `inherit` 读宿主 `<html class=\"dark\">`,折算成库自己的 data-cw-theme。\n // 显式主题下订阅依然存在,但快照不变、不会触发渲染(一个根节点一个 observer)。\n const hostDark = useHostDarkClass()\n const resolvedTheme = resolveTheme(theme, hostDark)\n\n // token 注入:inline style 优先级高于任何样式表(含暗色 token 块),\n // 因此宿主不需要关心 CSS 引入顺序,也不需要提高选择器特异性。\n const tokenStyle = useMemo(() => tokensToCssVars(tokens), [tokens])\n\n const value = useMemo<WorkbenchContextValue>(\n () => ({\n ui,\n bridge: resolvedBridge,\n config: mergedConfig,\n theme: { mode: theme, resolved: resolvedTheme },\n portalContainer,\n }),\n [ui, resolvedBridge, mergedConfig, theme, resolvedTheme, portalContainer],\n )\n\n return (\n <WorkbenchContext.Provider value={value}>\n {/* 让 `components/ui/` 里的 Portal 组件在裸用时也能找到挂载点 */}\n <PortalContainerProvider container={portalContainer}>\n {/*\n * 根节点必须是**真实盒子**,不能用 `display: contents`。\n *\n * `contents` 会让元素自己不生成盒子,于是它的 background-color 完全不渲染、\n * 高度也变成 auto —— 工作台撑不满宿主给的高度,内部三栏的间距全塌。\n * 代价是它成为布局中的一个层级,所以这里显式 `height: 100%` + `min-height: 0`,\n * 让它在「父元素有确定高度」和「父元素只有 min-height」两种宿主下都能撑满。\n *\n * 关于 `dark` class 的双写:库内所有暗色样式都走 `data-cw-theme`,不依赖它;\n * 保留是为了兼容「宿主用 `.cw-root.dark` 之类的选择器做钩子」的场景,\n * 且 `inherit` 模式下它也表达了「宿主当前是深色」,顺手给宿主一个统一标记。\n */}\n <div\n ref={attachRoot}\n className={['cw-root', resolvedTheme === 'dark' ? 'dark' : null, className]\n .filter(Boolean)\n .join(' ')}\n data-cw-theme={resolvedTheme}\n data-cw-theme-mode={theme}\n data-cw-root=\"\"\n style={tokenStyle}\n >\n <TooltipProvider delayDuration={300}>{children}</TooltipProvider>\n </div>\n </PortalContainerProvider>\n </WorkbenchContext.Provider>\n )\n}\n","/**\r\n * 统一接入入口:`initCutWorkbench(container, options)`。\r\n *\r\n * 面向三类接入方:\r\n * - **非 React 项目 / 微前端**:只要一个容器和一个函数调用;\r\n * - **Electron 渲染进程**:传入 `bridge` 把「下载」换成「保存到指定路径」;\r\n * - **script 标签直接引入**:UMD 产物暴露同一个函数。\r\n *\r\n * 设计要点:\r\n * - **同步返回句柄**。内部渲染是异步的,但接入方写代码应该是线性的;\r\n * 真正的就绪通过 `on('ready')` 或 `whenReady()` 通知。\r\n * - **dispose 幂等且彻底**:卸载 React 树、释放引擎、关闭 AudioContext。\r\n * - **不缓存全局实例**。同一页面调用两次得到两个互不干扰的工作台。\r\n */\r\n\r\nimport { createRoot, type Root } from 'react-dom/client'\r\nimport { createElement } from 'react'\r\nimport {\r\n createEditor,\r\n type CutEditorHandle,\r\n type CutEventMap,\r\n} from '@cut-workbench/core/engine'\r\nimport {\r\n createIndexedDbStorage,\r\n createMemoryStorage,\r\n deserializeProject,\r\n type HostBridge,\r\n type Project,\r\n type SerializedProject,\r\n type StoragePort,\r\n} from '@cut-workbench/core'\r\nimport { CutEditorProvider } from '@cut-workbench/react'\r\nimport { CutWorkbench } from './CutWorkbench'\r\nimport { createDefaultBridge } from './default-bridge'\r\nimport { CutWorkbenchProvider } from './provider'\r\nimport type { WorkbenchConfig } from './context'\r\nimport type { CutThemeTokens } from './theme-tokens'\r\n\r\nexport interface InitCutWorkbenchOptions {\r\n /** 宿主桥;不传则用浏览器内置实现 */\r\n bridge?: HostBridge\r\n /** 工程持久化;不传用 IndexedDB(命名为 cut-workbench) */\r\n storage?: StoragePort\r\n /** 持久化命名空间(多项目 / 多实例隔离);默认 'default' */\r\n namespace?: string\r\n /** 初始工程 */\r\n project?: Project | SerializedProject\r\n /** 初始素材文件 */\r\n assets?: File[]\r\n /** 主题:`inherit` 跟随宿主 `<html class=\"dark\">` */\r\n theme?: 'light' | 'dark' | 'inherit'\r\n /** 覆盖任意 `--cw-*` token(key 不带 `--cw-` 前缀),透传给 Provider */\r\n tokens?: CutThemeTokens\r\n /** 功能开关 */\r\n config?: Partial<Omit<WorkbenchConfig, 'features'>> & {\r\n features?: Partial<WorkbenchConfig['features']>\r\n }\r\n /** 是否强制能力探测阻断页 */\r\n enforceCapability?: boolean\r\n /** 容器 className(控制高度等) */\r\n className?: string\r\n /** 是否在初始化后自动保存(每次结构性变更后节流保存) */\r\n autoSave?: boolean\r\n autoSaveIntervalMs?: number\r\n /** 就绪回调 */\r\n onReady?: (instance: CutWorkbenchInstance) => void\r\n /** 错误回调 */\r\n onError?: (error: unknown) => void\r\n}\r\n\r\nexport interface CutWorkbenchInstance {\r\n readonly editor: CutEditorHandle\r\n readonly container: HTMLElement\r\n readonly bridge: HostBridge\r\n\r\n /** 等待 React 树挂载完成 */\r\n whenReady(): Promise<void>\r\n /** 卸载 React 树(保留工程数据与编辑器) */\r\n unmount(): void\r\n /** 彻底销毁(引擎资源一并释放);幂等 */\r\n dispose(): Promise<void>\r\n\r\n // —— 工程 ——\r\n getProject(): Project\r\n getAssets(): ReturnType<CutEditorHandle['getAssets']>\r\n serialize(): SerializedProject\r\n loadProject(input: SerializedProject | Project): Promise<void>\r\n saveProject(): Promise<{ ok: boolean; error?: string }>\r\n\r\n // —— 素材 ——\r\n addFiles(files: File[]): Promise<string[]>\r\n\r\n // —— 播放 ——\r\n play(): void\r\n pause(): void\r\n toggle(): void\r\n seek(us: number): void\r\n getPlayhead(): number\r\n\r\n // —— 编辑 ——\r\n undo(): void\r\n redo(): void\r\n\r\n // —— 事件 ——\r\n on<K extends keyof CutEventMap>(type: K, handler: (payload: CutEventMap[K]) => void): () => void\r\n}\r\n\r\nfunction resolveContainer(container: HTMLElement | string): HTMLElement {\r\n if (typeof container !== 'string') return container\r\n const el = document.querySelector(container)\r\n if (el == null) throw new Error(`[cut-workbench] 找不到容器元素:${container}`)\r\n return el as HTMLElement\r\n}\r\n\r\n/**\r\n * 在指定容器里初始化一个工作台。\r\n *\r\n * @example 非 React 项目\r\n * ```js\r\n * const wb = initCutWorkbench('#app', { theme: 'dark' })\r\n * wb.on('export:done', ({ blob }) => saveSomewhere(blob))\r\n * ```\r\n *\r\n * @example Electron 渲染进程\r\n * ```js\r\n * const wb = initCutWorkbench(container, {\r\n * bridge: createElectronBridge(window.cutHost),\r\n * })\r\n * ```\r\n */\r\nexport function initCutWorkbench(\r\n container: HTMLElement | string,\r\n options: InitCutWorkbenchOptions = {},\r\n): CutWorkbenchInstance {\r\n const host = resolveContainer(container)\r\n const bridge = options.bridge ?? createDefaultBridge()\r\n\r\n // 有 bridge 提供的 storage 优先;否则按浏览器能力选 IndexedDB 或内存\r\n const storage =\r\n options.storage ??\r\n (typeof indexedDB !== 'undefined'\r\n ? createIndexedDbStorage({ dbName: 'cut-workbench', version: 1 })\r\n : createMemoryStorage())\r\n const namespace = options.namespace ?? 'default'\r\n\r\n const editor = createEditor({\r\n bridge,\r\n storage,\r\n namespace,\r\n autoLoad: false,\r\n })\r\n\r\n const errors: unknown[] = []\r\n const reportError = (err: unknown) => {\r\n errors.push(err)\r\n if (options.onError != null) options.onError(err)\r\n else console.error('[cut-workbench]', err)\r\n }\r\n\r\n // 引擎里的异步错误统一走这里,避免 unhandled rejection 静默丢失\r\n editor.events.on('error', ({ error }) => reportError(error))\r\n\r\n let root: Root | null = createRoot(host)\r\n // 显式标注类型:直接在 Promise executor 里赋值会被 TS 收窄为 never\r\n let readyResolve: (() => void) | undefined\r\n const readyPromise = new Promise<void>((resolve) => {\r\n readyResolve = resolve\r\n })\r\n let disposed = false\r\n let autoSaveTimer: number | null = null\r\n\r\n function renderTree() {\r\n root!.render(\r\n createElement(\r\n CutEditorProvider,\r\n { editor } as never,\r\n createElement(\r\n CutWorkbenchProvider,\r\n {\r\n bridge,\r\n theme: options.theme ?? 'dark',\r\n tokens: options.tokens,\r\n config: options.config,\r\n } as never,\r\n createElement(CutWorkbench, {\r\n className: options.className ?? 'h-full w-full',\r\n enforceCapability: options.enforceCapability ?? true,\r\n onSaveProject: () => void instance.saveProject(),\r\n onOpenProject: async () => {\r\n const saved = await storage.load(namespace)\r\n if (saved == null) return\r\n const { project, assets, missingAssetIds } = deserializeProject(saved)\r\n editor.setProject(project, assets, missingAssetIds)\r\n },\r\n }),\r\n ),\r\n ),\r\n )\r\n }\r\n\r\n function scheduleAutoSave() {\r\n if (options.autoSave !== true || autoSaveTimer != null) return\r\n autoSaveTimer = window.setTimeout(() => {\r\n autoSaveTimer = null\r\n void saveProject().catch(reportError)\r\n }, options.autoSaveIntervalMs ?? 2000)\r\n }\r\n\r\n async function saveProject() {\r\n return storage.save(namespace, editor.serialize())\r\n }\r\n\r\n const unsubscribeAutoSave =\r\n options.autoSave === true\r\n ? editor.events.on('project:changed', () => scheduleAutoSave())\r\n : () => undefined\r\n\r\n const instance: CutWorkbenchInstance = {\r\n editor,\r\n container: host,\r\n bridge,\r\n\r\n whenReady: () => readyPromise,\r\n\r\n unmount() {\r\n if (root == null) return\r\n root.unmount()\r\n root = null\r\n },\r\n\r\n async dispose() {\r\n if (disposed) return\r\n disposed = true\r\n unsubscribeAutoSave()\r\n if (autoSaveTimer != null) window.clearTimeout(autoSaveTimer)\r\n if (options.autoSave === true) {\r\n // 关闭前落一次盘,避免最后的编辑丢失\r\n try {\r\n await saveProject()\r\n } catch (err) {\r\n reportError(err)\r\n }\r\n }\r\n instance.unmount()\r\n await editor.dispose()\r\n },\r\n\r\n getProject: () => editor.getProject(),\r\n getAssets: () => editor.getAssets(),\r\n serialize: () => editor.serialize(),\r\n\r\n async loadProject(input) {\r\n await editor.load(input)\r\n if (options.autoSave === true) await saveProject()\r\n },\r\n\r\n saveProject,\r\n\r\n async addFiles(files) {\r\n // 引擎是懒加载的:只有真正导入素材时才把 WebAV 拉进来,\r\n // 避免初始化时在非 Chromium 环境触发 OPFS 访问。\r\n const engine = await import('@cut-workbench/core/engine')\r\n const ids: string[] = []\r\n for (const file of files) {\r\n try {\r\n const result = await engine.importFile(editor.runtime.clips, file)\r\n editor.store.getState().addAsset(result.asset)\r\n ids.push(result.asset.id)\r\n } catch (err) {\r\n reportError(err)\r\n }\r\n }\r\n return ids\r\n },\r\n\r\n play: () => editor.play(),\r\n pause: () => editor.pause(),\r\n toggle: () => editor.toggle(),\r\n seek: (us) => editor.seek(us),\r\n getPlayhead: () => editor.getPlayhead(),\r\n\r\n undo: () => editor.undo(),\r\n redo: () => editor.redo(),\r\n\r\n on: (type, handler) => editor.events.on(type, handler),\r\n }\r\n\r\n // 初始工程 / 素材\r\n if (options.project != null) {\r\n void editor.load(options.project)\r\n }\r\n\r\n renderTree()\r\n\r\n // 先执行一次初始导入再报告 ready,避免接入方拿到句柄时素材还没进 store\r\n void (async () => {\r\n try {\r\n if (options.assets != null && options.assets.length > 0) {\r\n await instance.addFiles(options.assets)\r\n }\r\n } catch (err) {\r\n reportError(err)\r\n } finally {\r\n readyResolve?.()\r\n options.onReady?.(instance)\r\n }\r\n })()\r\n\r\n return instance\r\n}\r\n\r\nexport type { CutEventMap }\r\n","/**\n * 原子组件的样式根。\n *\n * 为什么需要:库的 CSS 全部作用域在 `.cw-root` 下(为了不污染宿主)。原子组件\n * 脱离 `<CutWorkbench>` 使用时,如果没有 `.cw-root` 祖先,所有 utility 都会失效 ——\n * 组件能渲染但完全没有样式(片段没有色块、按钮没有圆角)。\n *\n * 用法:\n * ```tsx\n * <AtomsRoot theme=\"dark\" tokens={{ clipVideo: '#4d6bfe' }}>\n * <ClipBlock {...props} />\n * </AtomsRoot>\n * ```\n *\n * `<CutWorkbench>` 内部已经自带 `.cw-root`,不需要再包一层。\n *\n * 它只提供「样式作用域 + 主题变量 + Portal 挂载点 + Tooltip 兜底」,不创建编辑器、\n * 不接管布局,因此可以放心地包在任何位置。\n */\n\nimport { useCallback, useMemo, useState, type CSSProperties, type ReactNode } from 'react'\nimport { TooltipProvider } from './components/ui/tooltip'\nimport { PortalContainerProvider } from './portal-container'\nimport { resolveTheme, useHostDarkClass } from './theme'\nimport { tokensToCssVars, type CutThemeTokens } from './theme-tokens'\n\nexport interface AtomsRootProps {\n children: ReactNode\n /**\n * 主题:\n * - `light` / `dark`:固定主题;\n * - `inherit`:跟随宿主 `<html class=\"dark\">`(MutationObserver 实时同步)。\n */\n theme?: 'light' | 'dark' | 'inherit'\n /** 覆盖任意 `--cw-*` token(key 不带 `--cw-` 前缀) */\n tokens?: CutThemeTokens\n className?: string\n /** 透传给根节点的 style(常用于给个高度);与 `tokens` 同时存在时以 tokens 为准 */\n style?: CSSProperties\n}\n\n/**\n * 原子组件的样式与主题容器。\n *\n * 与 `<CutWorkbenchProvider>` 的区别:后者提供编辑器 UI 状态(面板尺寸、对话框),\n * 原子组件不需要这些。这里只做「让样式生效」这一件事。\n *\n * 但 Portal 挂载点必须同样具备:原子组件里的 Tooltip / Popover / Select\n * 也会往 portal 里渲染内容,挂到 `document.body` 就会丢掉 `.cw-root` 作用域\n * (表现为「下拉菜单没有背景、出现在视口外」)。\n */\nexport function AtomsRoot({\n children,\n theme = 'dark',\n tokens,\n className,\n style,\n}: AtomsRootProps) {\n const hostDark = useHostDarkClass()\n const resolvedTheme = resolveTheme(theme, hostDark)\n const tokenStyle = useMemo(() => tokensToCssVars(tokens), [tokens])\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)\n const attachRoot = useCallback((node: HTMLDivElement | null) => setPortalContainer(node), [])\n\n const classes = ['cw-root', resolvedTheme === 'dark' ? 'dark' : null, className]\n .filter(Boolean)\n .join(' ')\n\n return (\n <PortalContainerProvider container={portalContainer}>\n <div\n ref={attachRoot}\n className={classes}\n data-cw-theme={resolvedTheme}\n data-cw-theme-mode={theme}\n data-cw-atoms-root=\"\"\n // tokens 的优先级高于宿主的 style(同一条属性上后者覆盖前者),\n // 这样「用 style 给高度 + 用 tokens 换肤」可以同时生效\n style={{ ...style, ...tokenStyle }}\n >\n <TooltipProvider delayDuration={300}>{children}</TooltipProvider>\n </div>\n </PortalContainerProvider>\n )\n}\n"],"ignoreList":[97,98],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["Separator","SeparatorPrimitive","TooltipPrimitive","Tooltip","Popover","PopoverPrimitive","Popover","Row","Separator","Tooltip","Toaster","Sonner","Label","LabelPrimitive","ScrollArea","ScrollAreaPrimitive","Dialog","DialogPrimitive","DropdownMenu","DropdownMenuPrimitive","ImageIcon","DropdownMenu","KIND_LABEL","ScrollArea","Dialog","Separator","_Fragment","DropdownMenu","TypeIcon","Label","Tooltip","_Fragment","Tooltip","Slider","SliderPrimitive","Tooltip","_Fragment","Slider","FRAME_US","Tooltip","Separator","Tooltip","_Fragment","ContextMenu","ContextMenuPrimitive","ContextMenu","Tabs","TabsPrimitive","Select","SelectPrimitive","Label","_Fragment","Tooltip","Select","_Fragment","_Fragment","Select","_Fragment","Select","_Fragment","ScrollArea","Tabs","_Fragment","Separator","Switch","SwitchPrimitive","Progress","ProgressPrimitive","import.meta.ROLLDOWN_FILE_URL_axujYtGT_5s$jBogY6bRtw","workerUrl","Dialog","Label","Select","Switch","Progress","_Fragment","Separator","Progress","Dialog","_Fragment","Label","Toaster","immer"],"sources":["../src/components/layout/AppShell.tsx","../src/components/ui/button.tsx","../src/components/ui/separator.tsx","../src/portal-container.tsx","../src/components/ui/tooltip.tsx","../src/components/ui/popover.tsx","../src/components/layout/CapabilityBadge.tsx","../src/components/layout/WorkbenchToolbar.tsx","../src/components/layout/use-panel-resize.ts","../src/components/layout/PanelResizer.tsx","../src/components/common/LoadingOverlay.tsx","../src/components/common/UnsupportedBrowser.tsx","../src/components/ui/sonner.tsx","../src/components/ui/textarea.tsx","../src/components/ui/label.tsx","../src/components/ui/scroll-area.tsx","../src/components/ui/dialog.tsx","../src/components/ui/dropdown-menu.tsx","../src/components/common/EmptyState.tsx","../src/features/media-library/lib/pick-files.ts","../src/features/media-library/hooks/useMediaImport.ts","../src/features/media-library/components/DropZone.tsx","../src/components/ui/skeleton.tsx","../src/features/media-library/components/MediaCard.tsx","../src/features/media-library/components/MediaGrid.tsx","../src/features/media-library/components/MediaLibraryPanel.tsx","../src/hooks/use-toast-safe.ts","../src/features/preview/hooks/useAvCanvas.ts","../src/features/preview/hooks/usePreviewSync.ts","../src/features/preview/hooks/usePlaybackClock.ts","../src/features/preview/hooks/useCanvasInteraction.ts","../src/features/preview/components/CaptureButton.tsx","../src/features/preview/components/SelectionOverlay.tsx","../src/features/preview/components/PreviewStage.tsx","../src/components/ui/slider.tsx","../src/components/common/Timecode.tsx","../src/features/preview/components/PlaybackControls.tsx","../src/features/preview/lib/download.ts","../src/features/preview/components/PreviewPanel.tsx","../src/lib/constants.ts","../src/features/timeline/components/TimelineToolbar.tsx","../src/features/timeline/components/Ruler.tsx","../src/features/timeline/components/TrackHeader.tsx","../src/features/timeline/hooks/useLazyVisibility.ts","../src/features/timeline/components/ClipBlock.tsx","../src/components/ui/context-menu.tsx","../src/features/timeline/components/ClipContextMenu.tsx","../src/features/timeline/components/Playhead.tsx","../src/features/timeline/components/Marquee.tsx","../src/features/timeline/hooks/useTimelineZoom.ts","../src/features/timeline/hooks/useSnap.ts","../src/features/timeline/hooks/useTimelineDrag.ts","../src/features/timeline/components/Timeline.tsx","../src/components/ui/tabs.tsx","../src/components/ui/input.tsx","../src/components/ui/select.tsx","../src/features/inspector/components/Field.tsx","../src/features/inspector/components/VideoProps.tsx","../src/features/inspector/components/AudioProps.tsx","../src/features/inspector/components/TextProps.tsx","../src/features/inspector/components/SubtitleProps.tsx","../src/features/inspector/components/ImageProps.tsx","../src/features/inspector/hooks/useClipInspector.ts","../src/features/inspector/components/InspectorPanel.tsx","../src/components/ui/switch.tsx","../src/components/ui/progress.tsx","../src/features/export/worker-url.ts","../src/features/export/worker-factory.ts","../src/features/export/export-runner.ts","../src/features/export/hooks/useExportState.ts","../src/features/export/components/ExportResult.tsx","../src/features/export/components/ExportDialog.tsx","../src/features/export/components/ExportProgress.tsx","../src/features/canvas-settings/components/CanvasSettingsDialog.tsx","../src/hooks/use-editor-actions.ts","../src/hooks/use-keyboard-shortcuts.ts","../src/hooks/use-capability.ts","../src/components/layout/use-capability-info.ts","../src/app/debug-bridge.ts","../src/CutWorkbench.tsx","../src/default-bridge.ts","../../../node_modules/immer/dist/immer.mjs","../../../node_modules/zustand/esm/middleware/immer.mjs","../src/store/ui-store.ts","../src/theme.ts","../src/theme-tokens.ts","../src/provider.tsx","../src/init.tsx","../src/atoms-root.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { cn } from '@/lib/cn'\n\ninterface AppShellProps {\n topBar: ReactNode\n left: ReactNode\n center: ReactNode\n right: ReactNode\n timeline: ReactNode\n /** 左侧宽度(像素) */\n leftWidth: number\n /** 右侧宽度(像素) */\n rightWidth: number\n /** 时间轴高度(像素) */\n timelineHeight: number\n leftResizer: ReactNode\n rightResizer: ReactNode\n timelineResizer: ReactNode\n className?: string\n}\n\n/**\n * 工作台骨架:顶栏 + 上半区(素材 / 预览 / 属性)+ 底部通栏时间轴。\n *\n * ```\n * ┌───────────────────────────────────────────┐\n * │ 顶栏 │\n * ├──────────┬──────────────┬─────────────────┤\n * │ 素材库 │ 预览 │ 属性 │ 上半区\n * ├──────────┴──────────────┴─────────────────┤\n * │ 时间轴(横跨整行) │ 下栏\n * └───────────────────────────────────────────┘\n * ```\n *\n * 时间轴必须是**通栏**的:它曾被塞进中间列,于是左右面板一拉宽就把时间轴\n * 挤窄,工具条上的按钮直接溢出到属性面板上(实测右栏拉宽时溢出 203px)。\n * 与此同时,几乎所有 NLE 都是「轨道占满底部一条」,通栏也更符合预期。\n *\n * 尺寸一律用 inline style 的像素值(受控),配合 PanelResizer 的拖拽回写;\n * 不用 tailwind 动态 class,避免被 purge 掉导致尺寸失效。\n */\nexport function AppShell({\n topBar,\n left,\n center,\n right,\n timeline,\n leftWidth,\n rightWidth,\n timelineHeight,\n leftResizer,\n rightResizer,\n timelineResizer,\n className,\n}: AppShellProps) {\n return (\n <div className={cn('flex h-full min-h-0 flex-col bg-background', className)}>\n {topBar}\n\n {/* 上半区:太矮时整行可纵向滚动,而不是把内容裁掉 */}\n <div className=\"flex min-h-0 flex-1 overflow-x-hidden overflow-y-auto\">\n <aside\n className=\"flex min-h-[220px] shrink-0 flex-col overflow-hidden border-r bg-card/40\"\n style={{ width: leftWidth }}\n data-panel=\"media\"\n >\n {left}\n </aside>\n {leftResizer}\n <main className=\"flex min-h-[220px] min-w-0 flex-1 flex-col overflow-hidden\" data-panel=\"preview\">\n {center}\n </main>\n {rightResizer}\n <aside\n className=\"flex min-h-[220px] shrink-0 flex-col overflow-hidden border-l bg-card/40\"\n style={{ width: rightWidth }}\n data-panel=\"inspector\"\n >\n {right}\n </aside>\n </div>\n\n {timelineResizer}\n <section\n className=\"flex min-h-0 shrink-0 flex-col overflow-hidden border-t bg-timeline-bg\"\n style={{ height: timelineHeight }}\n data-panel=\"timeline\"\n >\n {timeline}\n </section>\n </div>\n )\n}\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Slot } from \"radix-ui\"\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot.Root : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Separator as SeparatorPrimitive } from \"radix-ui\"\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n decorative = true,\n ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n return (\n <SeparatorPrimitive.Root\n data-slot=\"separator\"\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Separator }\n","/**\n * Portal 挂载点 Context(极轻量,只放一个 DOM 节点)。\n *\n * 为什么单独一个 Context,而不是复用 `WorkbenchContext.portalContainer`:\n * 1. `useWorkbench()` 在缺少 Provider 时会抛错,而 `components/ui/` 里的\n * Portal 组件必须能「裸用」(宿主只拿一个 Popover 去拼自己的页面);\n * 2. `AtomsRoot` 没有 UI store / 宿主桥,只负责「样式作用域 + 主题」,\n * 不该为了一个挂载点被迫造一整套 WorkbenchContext。\n *\n * 语义:`null` 表示「没有作用域根」,此时 Radix 退回默认的 `document.body`,\n * 与改造前的行为一致。\n */\n\nimport { createContext, useContext, type ReactNode } from 'react'\n\nexport const PortalContainerContext = createContext<HTMLElement | null>(null)\n\n/** 读取当前作用域内的 Portal 挂载点;没有则返回 null(Radix 会退回 body) */\nexport function usePortalContainer(): HTMLElement | null {\n return useContext(PortalContainerContext)\n}\n\nexport interface PortalContainerProviderProps {\n container: HTMLElement | null\n children: ReactNode\n}\n\nexport function PortalContainerProvider({ container, children }: PortalContainerProviderProps) {\n return (\n <PortalContainerContext.Provider value={container}>\n {children}\n </PortalContainerContext.Provider>\n )\n}\n","\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport { cn } from \"@/lib/cn\"\r\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\"\r\nimport { usePortalContainer } from \"@/portal-container\"\r\n\r\nconst TooltipProviderPresence = React.createContext<boolean>(false)\r\n\r\nfunction TooltipProvider({\r\n delayDuration = 0,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\r\n return (\r\n <TooltipProviderPresence.Provider value={true}>\r\n <TooltipPrimitive.Provider\r\n data-slot=\"tooltip-provider\"\r\n delayDuration={delayDuration}\r\n {...props}\r\n />\r\n </TooltipProviderPresence.Provider>\r\n )\r\n}\r\n\r\n/**\r\n * Tooltip 在缺少 Provider 时自动兜底。\r\n *\r\n * Radix 的 Tooltip.Root 要求上层有 Tooltip.Provider,否则直接抛错\r\n * 「`Tooltip` must be used within `TooltipProvider`」。\r\n *\r\n * 对「组件库」来说这是致命的:宿主单独拿一个 `TrackHeader` 或 `ClipBlock`\r\n * 嵌到自己的页面里,不应该被迫知道「还得再包一层 Provider」——\r\n * 那是实现细节泄漏。这里检测不到上层 Provider 时就地补一个,\r\n * 已有 Provider 时行为不变(仍用宿主的 delayDuration 等配置)。\r\n */\r\nfunction useHasTooltipProvider(): boolean {\r\n // Radix 的 Provider 没有公开 context 探测手段,因此用一个内部标记 +\r\n // React context 的组合:Provider 组件自己写入标记,Tooltip 读取。\r\n return React.useContext(TooltipProviderPresence) === true\r\n}\r\n\r\n\r\nfunction Tooltip({\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Root>) {\r\n const hasProvider = useHasTooltipProvider()\r\n const root = <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\r\n if (hasProvider) return root\r\n // 兜底:自带一个 Provider,让原子组件可以裸用\r\n return <TooltipPrimitive.Provider delayDuration={300}>{root}</TooltipPrimitive.Provider>\r\n}\r\n\r\nfunction TooltipTrigger({\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\r\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />\r\n}\r\n\r\nfunction TooltipContent({\r\n className,\r\n sideOffset = 0,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\r\n // ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\r\n // utility 不生效(提示框没有背景/内边距)。原因详见 dialog.tsx 的 DialogPortal。\r\n const container = usePortalContainer()\r\n return (\r\n <TooltipPrimitive.Portal container={container ?? undefined}>\r\n <TooltipPrimitive.Content\r\n data-slot=\"tooltip-content\"\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\r\n </TooltipPrimitive.Content>\r\n </TooltipPrimitive.Portal>\r\n )\r\n}\r\n\r\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\r\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction Popover({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\n}\n\nfunction PopoverTrigger({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n // ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n // utility 不生效(浮层没有背景/边框)。原因详见 dialog.tsx 的 DialogPortal。\n const container = usePortalContainer()\n return (\n <PopoverPrimitive.Portal container={container ?? undefined}>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n )\n}\n\nfunction PopoverAnchor({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />\n}\n\nfunction PopoverHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"popover-header\"\n className={cn(\"flex flex-col gap-1 text-sm\", className)}\n {...props}\n />\n )\n}\n\nfunction PopoverTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <div\n data-slot=\"popover-title\"\n className={cn(\"font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction PopoverDescription({\n className,\n ...props\n}: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"popover-description\"\n className={cn(\"text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Popover,\n PopoverTrigger,\n PopoverContent,\n PopoverAnchor,\n PopoverHeader,\n PopoverTitle,\n PopoverDescription,\n}\n","import { CheckCircle2, AlertTriangle, XCircle, Loader2 } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'\nimport type { CapabilityInfo } from './use-capability-info'\n\ninterface CapabilityBadgeProps {\n info: CapabilityInfo\n}\n\n/** 顶栏能力徽标:点击展开逐项结果 */\nexport function CapabilityBadge({ info }: CapabilityBadgeProps) {\n const { status, report, summary } = info\n const icon =\n status === 'checking' ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : status === 'ready' ? (\n <CheckCircle2 className=\"size-3 text-success\" />\n ) : status === 'degraded' ? (\n <AlertTriangle className=\"size-3 text-warning\" />\n ) : (\n <XCircle className=\"size-3 text-destructive\" />\n )\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Badge\n variant=\"outline\"\n className=\"cursor-pointer gap-1 text-[11px] font-normal select-none\"\n >\n {icon}\n {summary}\n </Badge>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 space-y-2 text-xs\">\n <p className=\"font-medium\">浏览器能力</p>\n {report == null ? (\n <p className=\"text-muted-foreground\">{info.loading ? '探测中…' : '探测失败'}</p>\n ) : (\n <ul className=\"space-y-1\">\n <Row label=\"WebCodecs\" ok={report.webCodecs} />\n <Row label=\"H.264 编码\" ok={report.videoEncode} />\n <Row label=\"AAC 编码\" ok={report.audioEncode} />\n <Row label=\"OffscreenCanvas\" ok={report.offscreenCanvas} />\n <Row label=\"OPFS\" ok={report.opfs} />\n <Row label=\"WebGL2(绿幕)\" ok={report.webgl2} optional />\n <Row label=\"Combinator\" ok={report.composerSupported} />\n </ul>\n )}\n <p className=\"border-t pt-2 text-muted-foreground\">\n 输出固定为 MP4(H.264 + AAC 48kHz)。仅支持 Chromium 内核浏览器。\n </p>\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction Row({ label, ok, optional }: { label: string; ok: boolean; optional?: boolean }) {\n return (\n <li className=\"flex items-center justify-between gap-2\">\n <span className={optional ? 'text-muted-foreground' : undefined}>{label}</span>\n <span className={ok ? 'text-success' : optional ? 'text-muted-foreground' : 'text-destructive'}>\n {ok ? '可用' : optional ? '不可用(可选)' : '不可用'}\n </span>\n </li>\n )\n}\n","/**\n * 内置顶栏(WorkbenchToolbar)。\n *\n * 由旧 `TopBar` 演进而来,做了三件事适配「组件库」形态:\n * - 品牌区可替换(`brand`),不强制显示项目名;\n * - 保存/打开是可选回调,宿主不提供就不渲染按钮;\n * - 「视图切换(编辑器/实验室)」只在开启 lab 特性时显示。\n *\n * 可自定义的层次(由浅到深):\n * 1. **文案**:`labels` 覆盖工程名占位、能力徽标等;\n * 2. **局部**:`brand` / `title` / `status` / `actions` / `showCapabilityBadge`\n * 逐块替换;\n * 3. **整条**:`slots.topBar`(传 `null` 表示完全不要顶栏)。\n */\n\nimport { Download, FlaskConical, FolderOpen, Monitor, Redo2, Save, Undo2, Video } from 'lucide-react'\nimport { useState, type ReactNode } from 'react'\nimport { useEditor, useHistory } from '@cut-workbench/react'\nimport { ratioLabel } from '@cut-workbench/core'\nimport { Button } from '@/components/ui/button'\nimport { Separator } from '@/components/ui/separator'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport { cn } from '@/lib/cn'\nimport { CapabilityBadge } from './CapabilityBadge'\nimport { useUi } from '../../context'\nimport type { CapabilityInfo } from './use-capability-info'\n\n/** 可覆盖的文案;缺省用内置中文 */\nexport interface WorkbenchToolbarLabels {\n /** 空工程的占位名(工程名称为空时显示) */\n untitledProject?: string\n /** 「空工程」/「N 个片段」里的占位 */\n emptyProject?: string\n export?: string\n canvasSettings?: string\n showLab?: string\n backToEditor?: string\n save?: string\n open?: string\n undo?: string\n redo?: string\n}\n\nexport interface WorkbenchToolbarProps {\n capability: CapabilityInfo\n onExport: () => void\n /** 不提供则不渲染「保存」按钮 */\n onSaveProject?: () => void\n /** 不提供则不渲染「打开」按钮 */\n onOpenProject?: () => void\n /** 替换左侧品牌区;传 null 表示不要 */\n brand?: ReactNode | null\n /**\n * 替换「工程名」区;传 null 表示不要。\n *\n * 不传时用内置的只读展示(工程名 + 未保存圆点)。\n */\n title?: ReactNode | null\n /** 替换工程名右侧的状态文案区(默认「空工程 · 16:9」);传 null 表示不要 */\n status?: ReactNode | null\n /** 在右侧按钮组**末尾**追加宿主自己的动作 */\n actions?: ReactNode\n /** 是否显示引擎能力徽标,默认 true */\n showCapabilityBadge?: boolean\n /** 是否允许双击工程名重命名,默认 false(宿主可能用自己的工程体系) */\n editableTitle?: boolean\n /** 是否显示「实验室」入口 */\n showLab?: boolean\n /** 覆盖内置文案 */\n labels?: WorkbenchToolbarLabels\n}\n\n/** 顶栏:品牌 / 工程名 / 撤销重做 / 保存 / 导出 / 能力徽标 / 视图切换 */\nexport function WorkbenchToolbar({\n capability,\n onExport,\n onSaveProject,\n onOpenProject,\n brand,\n title,\n status,\n actions,\n showCapabilityBadge = true,\n showLab = false,\n editableTitle = false,\n labels,\n}: WorkbenchToolbarProps) {\n const projectName = useEditor((s) => s.project.name)\n const renameProject = useEditor((s) => s.renameProject)\n const dirty = useEditor((s) => s.dirty)\n const undo = useEditor((s) => s.undo)\n const redo = useEditor((s) => s.redo)\n const canUndo = useHistory((s) => s.past.length > 0)\n const canRedo = useHistory((s) => s.future.length > 0)\n const route = useUi((s) => s.route)\n const setRoute = useUi((s) => s.setRoute)\n const openDialog = useUi((s) => s.openDialog)\n const clipCount = useEditor((s) => s.project.clips.length)\n const projectWidth = useEditor((s) => s.project.width)\n const projectHeight = useEditor((s) => s.project.height)\n\n const text: Required<WorkbenchToolbarLabels> = {\n untitledProject: '未命名工程',\n emptyProject: '空工程',\n export: '导出',\n canvasSettings: '画布设置',\n showLab: '能力实验室',\n backToEditor: '返回编辑器',\n save: '保存工程',\n open: '加载工程',\n undo: '撤销',\n redo: '重做',\n ...labels,\n }\n\n // 品牌区默认不显示 —— 组件库嵌进别人的产品时,顶栏放着「Cut Workbench」\n // 品牌是错的(宿主才是主体)。要显示就显式传 `brand`。\n const branding = brand == null ? null : brand\n\n const projectTitle =\n title === null ? null : (\n title ?? (\n <ProjectTitle\n name={projectName}\n dirty={dirty}\n placeholder={text.untitledProject}\n editable={editableTitle}\n onRename={renameProject}\n />\n )\n )\n\n const statusNode =\n status === null ? null : (\n (status ?? (\n <span className=\"hidden text-xs text-muted-foreground sm:inline\">\n {clipCount > 0 ? `${clipCount} 个片段` : text.emptyProject} ·{' '}\n {ratioLabel(projectWidth, projectHeight)}\n </span>\n ))\n )\n\n return (\n <header\n className=\"flex h-12 shrink-0 items-center gap-2 overflow-hidden border-b bg-card px-3\"\n data-workbench-toolbar\n >\n {branding}\n\n {branding != null && projectTitle != null && (\n <Separator orientation=\"vertical\" className=\"h-5\" />\n )}\n\n {projectTitle}\n\n {statusNode}\n\n <div className=\"ml-auto flex shrink-0 items-center gap-1\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={undo}\n disabled={!canUndo}\n aria-label={text.undo}\n >\n <Undo2 className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{text.undo} (Ctrl+Z)</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={redo}\n disabled={!canRedo}\n aria-label={text.redo}\n >\n <Redo2 className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{text.redo} (Ctrl+Shift+Z)</TooltipContent>\n </Tooltip>\n\n <Separator orientation=\"vertical\" className=\"mx-1 h-5\" />\n\n {onOpenProject != null && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onOpenProject} aria-label={text.open}>\n <FolderOpen className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{text.open}</TooltipContent>\n </Tooltip>\n )}\n\n {onSaveProject != null && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onSaveProject} aria-label={text.save}>\n <Save className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{text.save}</TooltipContent>\n </Tooltip>\n )}\n\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"gap-1.5\"\n aria-label={text.canvasSettings}\n onClick={() => openDialog('projectSettings')}\n >\n <Monitor className=\"size-4\" />\n <span className=\"hidden md:inline\">{text.canvasSettings}</span>\n </Button>\n\n {showLab && (\n <Button\n variant={route === 'lab' ? 'secondary' : 'ghost'}\n size=\"sm\"\n className=\"gap-1.5\"\n onClick={() => setRoute(route === 'lab' ? 'editor' : 'lab')}\n >\n {route === 'lab' ? <Video className=\"size-4\" /> : <FlaskConical className=\"size-4\" />}\n <span className=\"hidden md:inline\">\n {route === 'lab' ? text.backToEditor : text.showLab}\n </span>\n </Button>\n )}\n\n {showCapabilityBadge && <CapabilityBadge info={capability} />}\n\n {actions}\n\n <Button size=\"sm\" className=\"ml-1 gap-1.5\" onClick={onExport}>\n <Download className=\"size-4\" />\n {text.export}\n </Button>\n </div>\n </header>\n )\n}\n\n/**\n * 工程名展示(可选双击重命名)。\n *\n * 重命名走编辑器 store 的 `renameProject`,因此会进撤销栈、并标记 dirty ——\n * 直接改 DOM 或本地 state 会让「保存了什么」和「看到什么」对不上。\n */\nfunction ProjectTitle({\n name,\n dirty,\n placeholder,\n editable,\n onRename,\n}: {\n name: string\n dirty: boolean\n placeholder: string\n editable: boolean\n onRename: (name: string) => void\n}) {\n const [editing, setEditing] = useState(false)\n const [draft, setDraft] = useState(name)\n\n const commit = () => {\n setEditing(false)\n const next = draft.trim()\n if (next.length > 0 && next !== name) onRename(next)\n }\n\n if (editing) {\n return (\n <input\n autoFocus\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onKeyDown={(e) => {\n if (e.key === 'Enter') commit()\n if (e.key === 'Escape') {\n setDraft(name)\n setEditing(false)\n }\n }}\n className=\"h-7 w-48 min-w-0 rounded border bg-background px-1.5 text-sm outline-none focus:ring-1 focus:ring-ring\"\n aria-label=\"工程名称\"\n />\n )\n }\n\n return (\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <span\n className={cn('truncate text-sm font-medium', editable && 'cursor-text')}\n title={editable ? '双击重命名' : name.length > 0 ? name : placeholder}\n onDoubleClick={\n editable\n ? () => {\n setDraft(name.length > 0 ? name : '')\n setEditing(true)\n }\n : undefined\n }\n >\n {name.length > 0 ? name : placeholder}\n </span>\n {dirty && <span className=\"size-1.5 shrink-0 rounded-full bg-primary\" title=\"未保存\" />}\n </div>\n )\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\ninterface ResizerOptions {\n /** 拖拽开始时读取当前尺寸 */\n initial: number\n min: number\n max: number\n /** 方向:横向拖拽调整宽度,纵向调整高度 */\n axis: 'x' | 'y'\n /** 反向:拖拽向右但尺寸变小(右侧面板) */\n invert?: boolean\n onChange: (value: number) => void\n}\n\n/**\n * 面板拖拽调整。内部用 rAF 节流,松手才把最终值写回 store(避免每帧重渲染整棵树)。\n */\nexport function usePanelResize({ initial, min, max, axis, invert, onChange }: ResizerOptions) {\n const [dragging, setDragging] = useState(false)\n const startRef = useRef(0)\n const originRef = useRef(initial)\n const frameRef = useRef<number | null>(null)\n const latestRef = useRef(initial)\n\n const onPointerDown = useCallback(\n (event: React.PointerEvent) => {\n event.preventDefault()\n startRef.current = axis === 'x' ? event.clientX : event.clientY\n originRef.current = initial\n latestRef.current = initial\n setDragging(true)\n },\n [axis, initial],\n )\n\n useEffect(() => {\n if (!dragging) return\n const move = (event: PointerEvent) => {\n const current = axis === 'x' ? event.clientX : event.clientY\n const delta = (current - startRef.current) * (invert ? -1 : 1)\n const next = Math.max(min, Math.min(max, originRef.current + delta))\n latestRef.current = next\n if (frameRef.current != null) return\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null\n onChange(latestRef.current)\n })\n }\n const up = () => {\n if (frameRef.current != null) {\n cancelAnimationFrame(frameRef.current)\n frameRef.current = null\n }\n onChange(latestRef.current)\n setDragging(false)\n }\n window.addEventListener('pointermove', move)\n window.addEventListener('pointerup', up)\n return () => {\n window.removeEventListener('pointermove', move)\n window.removeEventListener('pointerup', up)\n }\n }, [axis, dragging, invert, max, min, onChange])\n\n return { dragging, onPointerDown }\n}\n","import type { ComponentPropsWithoutRef } from 'react'\nimport { cn } from '@/lib/cn'\nimport { usePanelResize } from './use-panel-resize'\n\ninterface PanelResizerProps {\n orientation: 'vertical' | 'horizontal'\n /** 当前尺寸 */\n value: number\n min: number\n max: number\n /** 反向拖拽(右侧 / 底部面板) */\n invert?: boolean\n onChange: (value: number) => void\n className?: string\n label?: string\n}\n\n/**\n * 三栏骨架的分隔条。\n *\n * `orientation=\"vertical\"` 表示「竖线」,拖动改变左右宽度;\n * `orientation=\"horizontal\"` 表示「横线」,拖动改变上下高度。\n */\nexport function PanelResizer({\n orientation,\n value,\n min,\n max,\n invert,\n onChange,\n className,\n label = '拖动调整面板大小',\n}: PanelResizerProps) {\n const isVertical = orientation === 'vertical'\n const { dragging, onPointerDown } = usePanelResize({\n initial: value,\n min,\n max,\n axis: isVertical ? 'x' : 'y',\n invert,\n onChange,\n })\n\n return (\n <div\n role=\"separator\"\n aria-orientation={isVertical ? 'vertical' : 'horizontal'}\n aria-label={label}\n onPointerDown={onPointerDown}\n className={cn(\n 'group relative z-20 shrink-0 touch-none transition-colors',\n isVertical ? 'w-1 cursor-col-resize' : 'h-1 cursor-row-resize',\n dragging ? 'bg-primary/60' : 'bg-border hover:bg-primary/40',\n className,\n )}\n >\n {/* 加大命中区域,避免 1px 宽难以点中 */}\n <div\n className={cn(\n 'absolute',\n isVertical ? '-left-1 -right-1 inset-y-0' : '-top-1 -bottom-1 inset-x-0',\n )}\n />\n </div>\n )\n}\n\nexport type PanelResizerElementProps = ComponentPropsWithoutRef<'div'>\n","import { Loader2 } from 'lucide-react'\nimport { cn } from '@/lib/cn'\n\ninterface LoadingOverlayProps {\n visible: boolean\n title?: string\n description?: string\n /** 0~1;不传则显示不确定进度 */\n progress?: number\n className?: string\n /** 是否覆盖整个视口 */\n fullscreen?: boolean\n children?: React.ReactNode\n}\n\n/** 全屏/局部遮罩加载态,用于导出、批量导入等长任务 */\nexport function LoadingOverlay({\n visible,\n title = '处理中…',\n description,\n progress,\n className,\n fullscreen,\n children,\n}: LoadingOverlayProps) {\n if (!visible) return null\n return (\n <div\n className={cn(\n 'z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm',\n fullscreen ? 'fixed inset-0' : 'absolute inset-0',\n className,\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"w-full max-w-sm space-y-3 rounded-lg border bg-card p-5 shadow-lg\">\n <div className=\"flex items-center gap-2\">\n <Loader2 className=\"size-4 animate-spin text-primary\" />\n <p className=\"text-sm font-medium\">{title}</p>\n </div>\n {description != null && (\n <p className=\"text-xs text-muted-foreground\">{description}</p>\n )}\n {progress != null && (\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-primary transition-all duration-200\"\n style={{ width: `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%` }}\n />\n </div>\n )}\n {children}\n </div>\n </div>\n )\n}\n","import { AlertOctagon, CheckCircle2, XCircle } from 'lucide-react'\nimport type { CapabilityReport } from '@cut-workbench/core/engine'\nimport { ENGINE_DEPENDENCIES_INSTALL_COMMAND } from '@cut-workbench/core/engine'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { LoadingOverlay } from '@/components/common/LoadingOverlay'\n\ninterface UnsupportedBrowserProps {\n report: CapabilityReport | null\n loading: boolean\n error?: string | null\n}\n\nconst CHECKS: Array<{ key: keyof CapabilityReport; label: string; hint: string }> = [\n { key: 'webCodecs', label: 'WebCodecs', hint: '需 Chrome 94+ / Edge 94+' },\n { key: 'videoEncode', label: 'H.264 编码', hint: 'avc1.42E032' },\n { key: 'audioEncode', label: 'AAC 编码', hint: 'mp4a.40.2 / 48kHz' },\n { key: 'offscreenCanvas', label: 'OffscreenCanvas', hint: '离屏渲染' },\n { key: 'opfs', label: 'OPFS', hint: 'WebAV 临时文件存储' },\n { key: 'webgl2', label: 'WebGL2', hint: '绿幕抠像(可选)' },\n]\n\n/**\n * 不满足运行条件时的阻断页:逐项说明缺失能力,不做静默降级。\n *\n * 「依赖没装」与「浏览器不支持」要分开呈现 —— 前者是宿主的构建问题(可执行\n * 安装命令解决),后者只能换浏览器。混在一起会让人对着一条装包报错去升浏览器。\n */\nexport function UnsupportedBrowser({ report, loading, error }: UnsupportedBrowserProps) {\n if (loading) {\n return (\n <div className=\"relative flex min-h-screen items-center justify-center bg-background\">\n <LoadingOverlay visible title=\"正在检测浏览器能力…\" fullscreen={false} />\n </div>\n )\n }\n\n const missing = report?.missingDependencies ?? []\n const dependenciesMissing = missing.length > 0\n\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-background p-6\">\n <Card className=\"w-full max-w-2xl\">\n <CardHeader>\n <div className=\"flex items-center gap-2 text-destructive\">\n <AlertOctagon className=\"size-5\" />\n <CardTitle className=\"text-base\">\n {dependenciesMissing ? '缺少引擎依赖' : '当前浏览器无法运行本应用'}\n </CardTitle>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {dependenciesMissing\n ? '工作台的预览与导出依赖 WebAV(@webav/av-cliper、@webav/av-canvas)。当前项目里没有解析到它们。'\n : 'WebAV 依赖 WebCodecs 与 OPFS,仅支持 Chromium 内核浏览器。请使用最新版 Chrome 或 Edge(非无痕模式)重新打开。'}\n </p>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {/* 依赖缺失:直接给可复制的命令,这是唯一有效的下一步 */}\n {dependenciesMissing && (\n <div className=\"space-y-2 rounded-md border border-destructive/40 bg-destructive/5 p-3\">\n <p className=\"text-xs font-medium text-destructive\">\n 未解析到:{missing.join('、')}\n </p>\n <p className=\"text-xs text-muted-foreground\">在宿主项目里执行:</p>\n <pre className=\"overflow-x-auto rounded bg-muted px-3 py-2 text-xs\">\n <code>{ENGINE_DEPENDENCIES_INSTALL_COMMAND}</code>\n </pre>\n <p className=\"text-xs text-muted-foreground\">\n 安装后需要重新跑一次构建 —— 缺失依赖只在构建期解析,浏览器里刷新页面没用。\n </p>\n </div>\n )}\n\n {report != null && !dependenciesMissing && (\n <div className=\"grid gap-2 sm:grid-cols-2\">\n {CHECKS.map((check) => {\n const value = report[check.key]\n const ok = typeof value === 'boolean' ? value : false\n return (\n <div\n key={check.key}\n className=\"flex items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm\"\n >\n <div className=\"flex items-center gap-2\">\n {ok ? (\n <CheckCircle2 className=\"size-4 text-success\" />\n ) : (\n <XCircle className=\"size-4 text-destructive\" />\n )}\n <span>{check.label}</span>\n </div>\n <span className=\"text-xs text-muted-foreground\">{check.hint}</span>\n </div>\n )\n })}\n </div>\n )}\n\n {report != null && report.issues.length > 0 && (\n <ul className=\"list-disc space-y-1 rounded-md bg-muted p-3 pl-7 text-xs text-muted-foreground\">\n {report.issues.map((issue) => (\n <li key={issue}>{issue}</li>\n ))}\n </ul>\n )}\n\n {error != null && (\n <p className=\"rounded-md bg-destructive/10 p-3 text-xs text-destructive\">{error}</p>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" onClick={() => window.location.reload()}>\n 重新检测\n </Button>\n <span className=\"text-xs text-muted-foreground\">{report?.advice}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n )\n}\n","import {\r\n CircleCheckIcon,\r\n InfoIcon,\r\n Loader2Icon,\r\n OctagonXIcon,\r\n TriangleAlertIcon,\r\n} from \"lucide-react\"\r\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\r\n\r\n/**\r\n * 工作台内的 toast 容器。\r\n *\r\n * 主题跟随 `CutWorkbenchProvider` 的 data-cw-theme,而不是写死 dark ——\r\n * 库可能被放在亮色宿主里。样式 token 全部走 `--cw-*`,因此不受宿主全局变量影响。\r\n */\r\nconst Toaster = (props: ToasterProps) => {\r\n return (\r\n <Sonner\r\n className=\"toaster group\"\r\n icons={{\r\n success: <CircleCheckIcon className=\"size-4\" />,\r\n info: <InfoIcon className=\"size-4\" />,\r\n warning: <TriangleAlertIcon className=\"size-4\" />,\r\n error: <OctagonXIcon className=\"size-4\" />,\r\n loading: <Loader2Icon className=\"size-4 animate-spin\" />,\r\n }}\r\n style={\r\n {\r\n \"--normal-bg\": \"var(--cw-popover)\",\r\n \"--normal-text\": \"var(--cw-popover-foreground)\",\r\n \"--normal-border\": \"var(--cw-border)\",\r\n \"--border-radius\": \"var(--cw-radius)\",\r\n } as React.CSSProperties\r\n }\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport { Toaster }\r\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Textarea }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Label as LabelPrimitive } from \"radix-ui\"\n\nfunction Label({\n className,\n ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Label }\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { ScrollArea as ScrollAreaPrimitive } from \"radix-ui\"\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\n return (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n data-slot=\"scroll-area-scrollbar\"\n orientation={orientation}\n className={cn(\n \"flex touch-none p-px transition-colors select-none\",\n orientation === \"vertical\" &&\n \"h-full w-2.5 border-l border-l-transparent\",\n orientation === \"horizontal\" &&\n \"h-2.5 flex-col border-t border-t-transparent\",\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb\n data-slot=\"scroll-area-thumb\"\n className=\"relative flex-1 rounded-full bg-border\"\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n )\n}\n\nexport { ScrollArea, ScrollBar }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { XIcon } from \"lucide-react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction Dialog({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\n/**\n * ⚠️ 本文件是 shadcn 生成物,按约定「只改样式 token,不改结构」。\n * 这里是一个**必要的结构修正**,请勿在重新生成时覆盖掉:\n *\n * Radix 的 Portal 默认挂到 `document.body`,而库的全部 utility 都被作用域化成\n * `.cw-root .xxx`。挂到 body 的对话框因此在 `.cw-root` 之外,拿不到任何样式\n * (背景透明、圆角为 0、translate 变体失效导致定位到视口外),实测「点导出后\n * 屏幕上看不到任何对话框」。\n *\n * 因此在每个 Portal 上消费 `PortalContainerContext`(由 `<CutWorkbenchProvider>`\n * 或 `<AtomsRoot>` 提供,即 `.cw-root` 根节点)。没有 Provider 时退回 body,\n * 保持组件可裸用。\n */\nfunction DialogPortal({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n const container = usePortalContainer()\n return (\n <DialogPrimitive.Portal\n data-slot=\"dialog-portal\"\n container={container ?? undefined}\n {...props}\n />\n )\n}\n\nfunction DialogClose({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n )\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n}\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\"\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction DropdownMenu({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />\n}\n\n// ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n// utility 不生效(菜单没有背景/边框)。原因详见 dialog.tsx 的 DialogPortal。\nfunction DropdownMenuPortal({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n const container = usePortalContainer()\n return (\n <DropdownMenuPrimitive.Portal\n data-slot=\"dropdown-menu-portal\"\n container={container ?? undefined}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuTrigger({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n return (\n <DropdownMenuPrimitive.Trigger\n data-slot=\"dropdown-menu-trigger\"\n {...props}\n />\n )\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n // Portal 挂载点由 DropdownMenuPortal 统一处理(含 `.cw-root` 作用域)\n return (\n <DropdownMenuPortal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </DropdownMenuPortal>\n )\n}\n\nfunction DropdownMenuGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n return (\n <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />\n )\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean\n variant?: \"default\" | \"destructive\"\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n )\n}\n\nfunction DropdownMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n return (\n <DropdownMenuPrimitive.RadioGroup\n data-slot=\"dropdown-menu-radio-group\"\n {...props}\n />\n )\n}\n\nfunction DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n )\n}\n\nfunction DropdownMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean\n}) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn(\n \"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuShortcut({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuSub({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean\n}) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n )\n}\n\nfunction DropdownMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n DropdownMenu,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuLabel,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n}\n","import type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/cn'\n\ninterface EmptyStateProps {\n icon: LucideIcon\n title: string\n description?: string\n action?: ReactNode\n className?: string\n compact?: boolean\n}\n\n/** 统一空态:图标 + 标题 + 说明 + 可选操作 */\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n action,\n className,\n compact,\n}: EmptyStateProps) {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-2 text-center',\n compact ? 'px-4 py-6' : 'px-6 py-12',\n className,\n )}\n >\n <div className=\"flex size-10 items-center justify-center rounded-full bg-muted text-muted-foreground\">\n <Icon className=\"size-5\" />\n </div>\n <p className=\"text-sm font-medium\">{title}</p>\n {description != null && (\n <p className=\"max-w-xs text-xs leading-relaxed text-muted-foreground\">{description}</p>\n )}\n {action}\n </div>\n )\n}\n","/**\n * 选择素材文件:宿主优先,回退到浏览器的 `<input type=file>`。\n *\n * 为什么不能只用 react-dropzone 的 input:\n * 素材来源不一定在本地文件系统 —— 宿主可能从自己的素材库、云端、录制器或\n * URL 导入。`HostBridge.files.pickFiles` 就是给这类宿主用的口子。\n *\n * 三种结果必须区分清楚:\n * - `File[]`:宿主或浏览器选到了文件;\n * - `[]`:用户主动取消 —— **不要**再回退,否则会连弹两个选择框;\n * - `null`:宿主不接管 —— 回退到内置 input。\n */\n\nimport type { HostBridge, PickFilesResult } from '@cut-workbench/core'\n\nconst INPUT_ID = 'cut-workbench-file-input'\n\n/** 内置回退:隐藏 input,选完即弃 */\nfunction pickWithInput(accept: string[], multiple: boolean): Promise<File[]> {\n return new Promise<File[]>((resolve) => {\n const input = document.createElement('input')\n input.type = 'file'\n input.id = INPUT_ID\n input.multiple = multiple\n if (accept.length > 0) input.accept = accept.join(',')\n input.style.display = 'none'\n document.body.appendChild(input)\n\n const finish = (files: File[]) => {\n input.remove()\n resolve(files)\n }\n\n input.addEventListener('change', () => finish(Array.from(input.files ?? [])), { once: true })\n // 用户取消时 change 不触发;靠 window focus 兜底,避免 input 永久挂载\n window.addEventListener(\n 'focus',\n () => {\n window.setTimeout(() => {\n if (document.body.contains(input)) finish([])\n }, 300)\n },\n { once: true },\n )\n input.click()\n })\n}\n\nexport interface PickMediaOptions {\n /** MIME 或扩展名,形如 `video/mp4`、`.srt` */\n accept: string[]\n multiple?: boolean\n /** 给宿主 UI 用的提示文案 */\n description?: string\n}\n\n/**\n * @returns 选中的文件;用户取消时为空数组\n */\nexport async function pickMediaFiles(\n bridge: HostBridge | null | undefined,\n options: PickMediaOptions,\n): Promise<File[]> {\n const multiple = options.multiple ?? true\n\n if (bridge?.files?.pickFiles != null) {\n const result: PickFilesResult = await bridge.files.pickFiles({\n accept: options.accept,\n multiple,\n description: options.description,\n })\n // null = 宿主不接管,落到下面的内置实现\n if (result != null) return result\n }\n\n return pickWithInput(options.accept, multiple)\n}\n","import { useCallback, useState } from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { useUi, useWorkbench } from '../../../context'\r\nimport { notify } from '@/lib/notify'\r\nimport type { EditorState } from '@cut-workbench/core'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { importFile, UnsupportedFileError } from '@cut-workbench/core/engine'\r\n\r\nexport interface ImportProgressItem {\r\n name: string\r\n progress: number\r\n error?: string\r\n}\r\n\r\nexport interface UseMediaImportResult {\r\n importing: boolean\r\n progress: ImportProgressItem[]\r\n importFiles: (files: File[]) => Promise<MediaAsset[]>\r\n}\r\n\r\nconst ACCEPT = ['video/mp4', 'audio/*', 'image/*', '.srt', 'text/plain']\r\n\r\nexport { ACCEPT }\r\n\r\n/**\r\n * 找出可以「重连」的素材。\r\n *\r\n * 场景:刷新后工程从 IndexedDB 恢复,但素材二进制消失(File 无法持久化)。\r\n * 用户重新导入同一个文件时,复用原 assetId,时间轴上已有片段立即恢复。\r\n * 匹配条件:同名 + 同类型 + 当前确实缺少二进制。\r\n */\r\nfunction findReconnectableAssetId(\r\n state: EditorState,\r\n file: File,\r\n registry: { getFile(id: string): File | null },\r\n): string | null {\r\n if (state.missingAssetIds.length === 0) return null\r\n const candidate = state.assets.find(\r\n (asset) =>\r\n state.missingAssetIds.includes(asset.id) &&\r\n asset.name === file.name &&\r\n registry.getFile(asset.id) == null,\r\n )\r\n return candidate?.id ?? null\r\n}\r\n\r\n/**\r\n * 批量导入素材。\r\n *\r\n * - 串行处理,避免同时开辟多个解码器把内存打爆;\r\n * - 单个文件失败只影响自身,最后汇总 toast;\r\n * - 进度写在本地 state(不放进 store,避免高频更新引起全局重渲染);\r\n * - 若与「缺失源文件」的素材同名同类型,则重连而不是新增。\r\n */\r\nexport function useMediaImport(): UseMediaImportResult {\r\n const editor = useCutEditor()\r\n const clips = editor.runtime.clips\r\n const store = editor.store\r\n const [progress, setProgress] = useState<ImportProgressItem[]>([])\r\n const [importing, setImporting] = useState(false)\r\n const addAsset = useEditor((s) => s.addAsset)\r\n const replaceAsset = useEditor((s) => s.replaceAsset)\r\n const clearMissing = useEditor((s) => s.clearMissing)\r\n const selectAsset = useEditor((s) => s.selectAsset)\r\n const beginImport = useUi((s) => s.beginImport)\r\n const endImport = useUi((s) => s.endImport)\r\n const { bridge } = useWorkbench()\r\n\r\n const importFiles = useCallback(\r\n async (files: File[]): Promise<MediaAsset[]> => {\r\n if (files.length === 0) return []\r\n setImporting(true)\r\n beginImport()\r\n setProgress(files.map((f) => ({ name: f.name, progress: 0 })))\r\n\r\n const imported: MediaAsset[] = []\r\n const failures: string[] = []\r\n let reconnected = 0\r\n\r\n for (let i = 0; i < files.length; i += 1) {\r\n const file = files[i]\r\n if (!file) continue\r\n try {\r\n // 刷新恢复的工程会留下「有元数据、无二进制」的素材。\r\n // 这里按文件名 + 类型尝试重连:复用原 assetId,时间轴上的片段立即恢复可用。\r\n const reconnectId = findReconnectableAssetId(store.getState(), file, clips)\r\n const result = await importFile(clips, file, (p) => {\r\n setProgress((prev) =>\r\n prev.map((item, index) => (index === i ? { ...item, progress: p } : item)),\r\n )\r\n })\r\n\r\n if (reconnectId != null) {\r\n // 先把二进制换回原 id(注册表内部会销毁旧母版与派生实例),\r\n // 再更新 store 里的元数据,并解除缺失标记。\r\n clips.replaceAssetSource(reconnectId, file, result.master ?? null)\r\n replaceAsset(reconnectId, result.asset)\r\n clearMissing(reconnectId)\r\n imported.push({ ...result.asset, id: reconnectId })\r\n reconnected += 1\r\n } else {\r\n addAsset(result.asset)\r\n imported.push(result.asset)\r\n }\r\n } catch (err) {\r\n const message =\r\n err instanceof UnsupportedFileError\r\n ? err.message\r\n : err instanceof Error\r\n ? err.message\r\n : String(err)\r\n failures.push(`${file.name}:${message}`)\r\n setProgress((prev) =>\r\n prev.map((item, index) => (index === i ? { ...item, error: message } : item)),\r\n )\r\n }\r\n }\r\n\r\n setImporting(false)\r\n endImport()\r\n\r\n if (imported.length > 0) {\r\n selectAsset(imported[0]?.id ?? null)\r\n notify(bridge, {\r\n level: 'success',\r\n title:\r\n reconnected > 0\r\n ? `已导入 ${imported.length} 个素材(其中 ${reconnected} 个重连到已有片段)`\r\n : `已导入 ${imported.length} 个素材`,\r\n })\r\n }\r\n if (failures.length > 0) {\r\n notify(bridge, {\r\n level: 'error',\r\n title: `${failures.length} 个文件导入失败`,\r\n description: failures.slice(0, 3).join('\\n'),\r\n duration: 8000,\r\n })\r\n }\r\n\r\n window.setTimeout(() => setProgress([]), 3000)\r\n return imported\r\n },\r\n [addAsset, beginImport, bridge, clearMissing, endImport, replaceAsset, selectAsset],\r\n )\r\n\r\n return { importing, progress, importFiles }\r\n}\r\n","import { useCallback } from 'react'\r\nimport { useDropzone } from 'react-dropzone'\r\nimport { Upload } from 'lucide-react'\r\nimport { cn } from '@/lib/cn'\r\nimport { useWorkbench } from '@/context'\r\nimport { pickMediaFiles } from '../lib/pick-files'\r\nimport { ACCEPT } from '../hooks/useMediaImport'\r\n\r\ninterface DropZoneProps {\r\n onFiles: (files: File[]) => void\r\n disabled?: boolean\r\n className?: string\r\n /** 紧凑模式用于媒体库顶部条 */\r\n compact?: boolean\r\n}\r\n\r\n/** react-dropzone 用的 MIME → 扩展名映射 */\r\nconst DROPZONE_ACCEPT = {\r\n 'video/mp4': ['.mp4', '.m4v'],\r\n 'audio/*': ['.mp3', '.wav', '.m4a', '.aac', '.ogg', '.flac'],\r\n 'image/*': ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.avif'],\r\n 'text/plain': ['.srt'],\r\n}\r\n\r\n/**\r\n * 拖拽 / 点击导入区域。\r\n *\r\n * 两条入口:\r\n * - **拖拽**:走 react-dropzone(宿主无法介入,这是浏览器原生行为);\r\n * - **点击**:优先走 `HostBridge.files.pickFiles` —— 宿主可能不用系统文件\r\n * 对话框,而是从自己的素材库 / 云端 / URL 导入。宿主返回 `null`(不接管)\r\n * 时才回退到内置 `<input type=file>`。\r\n *\r\n * 注意:react-dropzone 的 accept 只做初筛,真正按 MIME + 扩展名校验在\r\n * core/media/file-reader,从而保证「拖入 .webm 得到明确错误」而不是被静默拒绝。\r\n */\r\nexport function DropZone({ onFiles, disabled, className, compact }: DropZoneProps) {\r\n const { bridge } = useWorkbench()\r\n\r\n const { getRootProps, getInputProps, isDragActive } = useDropzone({\r\n onDrop: (accepted) => {\r\n if (accepted.length > 0) onFiles(accepted)\r\n },\r\n // 点击交给下面的 handleClick(要走 bridge),这里只保留拖拽\r\n noClick: true,\r\n noKeyboard: true,\r\n multiple: true,\r\n disabled,\r\n accept: DROPZONE_ACCEPT,\r\n })\r\n\r\n const handleClick = useCallback(() => {\r\n if (disabled) return\r\n void pickMediaFiles(bridge, {\r\n accept: ACCEPT,\r\n multiple: true,\r\n description: '选择要导入的素材',\r\n }).then((files) => {\r\n if (files.length > 0) onFiles(files)\r\n })\r\n }, [bridge, disabled, onFiles])\r\n\r\n return (\r\n <div\r\n {...getRootProps()}\r\n onClick={handleClick}\r\n data-drop-zone=\"\"\r\n className={cn(\r\n 'group relative flex cursor-pointer flex-col items-center justify-center gap-1.5 rounded-lg border border-dashed text-center transition-colors',\r\n compact ? 'px-3 py-2.5' : 'px-4 py-8',\r\n isDragActive\r\n ? 'border-primary bg-primary/10'\r\n : 'border-border hover:border-primary/60 hover:bg-muted/40',\r\n disabled && 'pointer-events-none opacity-50',\r\n className,\r\n )}\r\n >\r\n <input {...getInputProps()} />\r\n <div className=\"flex items-center gap-2 text-sm font-medium\">\r\n <Upload className={cn('text-muted-foreground', compact ? 'size-3.5' : 'size-4')} />\r\n <span>{compact ? '导入素材' : isDragActive ? '松开以导入' : '拖入文件,或点击选择'}</span>\r\n </div>\r\n {!compact && (\r\n <p className=\"text-xs text-muted-foreground\">\r\n 视频仅支持 MP4 · 音频 / 图片 / SRT 字幕 · 支持多选\r\n </p>\r\n )}\r\n {compact && (\r\n <button\r\n type=\"button\"\r\n className=\"sr-only\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n handleClick()\r\n }}\r\n >\r\n 选择文件\r\n </button>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { cn } from \"@/lib/cn\"\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"skeleton\"\n className={cn(\"animate-pulse rounded-md bg-accent\", className)}\n {...props}\n />\n )\n}\n\nexport { Skeleton }\n","import { useMemo } from 'react'\r\nimport { Film, Image as ImageIcon, Music, Plus, Subtitles, Type, Trash2 } from 'lucide-react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { formatBytes, formatDuration, formatResolution } from '@/lib/format'\r\nimport { buildWaveformPath } from '@cut-workbench/core'\r\nimport { cn } from '@/lib/cn'\r\nimport { Button } from '@/components/ui/button'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuSeparator,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu'\r\n\r\ninterface MediaCardProps {\r\n asset: MediaAsset\r\n selected: boolean\r\n /** 源文件缺失(刷新后恢复的工程):卡片显示警示,双击提示重新导入 */\r\n missing?: boolean\r\n onSelect: () => void\r\n onAddToTimeline: () => void\r\n onShowMeta: () => void\r\n onRemove: () => void\r\n}\r\n\r\nconst KIND_META = {\r\n video: { icon: Film, label: '视频', tone: 'bg-clip-video/20 text-clip-video' },\r\n audio: { icon: Music, label: '音频', tone: 'bg-clip-audio/20 text-clip-audio' },\r\n image: { icon: ImageIcon, label: '图片', tone: 'bg-clip-image/20 text-clip-image' },\r\n text: { icon: Type, label: '文字', tone: 'bg-clip-text/20 text-clip-text' },\r\n subtitle: { icon: Subtitles, label: '字幕', tone: 'bg-clip-subtitle/20 text-clip-subtitle' },\r\n} as const\r\n\r\n/** 素材卡片:缩略图 / 波形 / 元数据 / 快捷操作 */\r\nexport function MediaCard({\r\n asset,\r\n selected,\r\n missing,\r\n onSelect,\r\n onAddToTimeline,\r\n onShowMeta,\r\n onRemove,\r\n}: MediaCardProps) {\r\n const kind = KIND_META[asset.kind]\r\n const Icon = kind.icon\r\n const cover = asset.thumbnailUrls?.[0]\r\n\r\n const waveformPath = useMemo(() => {\r\n if (!asset.waveform || asset.waveform.length === 0) return ''\r\n return buildWaveformPath(asset.waveform, 100, 28)\r\n }, [asset.waveform])\r\n\r\n return (\r\n <div\r\n role=\"button\"\r\n tabIndex={0}\r\n onClick={onSelect}\r\n onDoubleClick={onAddToTimeline}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') onAddToTimeline()\r\n }}\r\n className={cn(\r\n 'group relative flex cursor-pointer flex-col gap-1.5 rounded-lg border p-1.5 text-left transition-colors outline-none',\r\n // 缺源文件用 warning token,不再写死 amber:宿主换肤后警示色也要跟着走\r\n missing\r\n ? 'border-warning/50 bg-warning/5'\r\n : selected\r\n ? 'border-primary bg-primary/10 ring-1 ring-primary/40'\r\n : 'border-border bg-card hover:border-primary/40 hover:bg-muted/40 focus-visible:border-primary',\r\n )}\r\n >\r\n <div className=\"relative aspect-video w-full overflow-hidden rounded-md bg-muted\">\r\n {cover != null ? (\r\n <img src={cover} alt=\"\" className=\"size-full object-cover\" draggable={false} />\r\n ) : asset.kind === 'audio' && waveformPath !== '' ? (\r\n <svg viewBox=\"0 0 100 28\" preserveAspectRatio=\"none\" className=\"size-full p-1.5 text-clip-audio\">\r\n <path d={waveformPath} stroke=\"currentColor\" strokeWidth={0.6} opacity={0.9} />\r\n </svg>\r\n ) : (\r\n <div className={cn('flex size-full items-center justify-center', kind.tone)}>\r\n <Icon className=\"size-7\" />\r\n </div>\r\n )}\r\n\r\n {/*\r\n * 角标压在缩略图上,语义固定为「深底浅字」:底下是任意画面内容,\r\n * 浅色主题下改浅底会直接看不清。因此走 --cw-badge-scrim 系列 token\r\n * (亮/暗两套值相同),而不是 bg-black/text-white 写死。\r\n */}\r\n <span className=\"absolute top-1 left-1 flex items-center gap-1 rounded bg-badge-scrim px-1.5 py-0.5 text-[10px] text-badge-scrim-foreground backdrop-blur-sm\">\r\n <Icon className=\"size-2.5\" />\r\n {kind.label}\r\n </span>\r\n {missing && (\r\n <span className=\"absolute inset-x-1 top-7 rounded bg-warning px-1.5 py-0.5 text-center text-[10px] text-warning-foreground\">\r\n 源文件缺失 · 请重新导入\r\n </span>\r\n )}\r\n <span className=\"absolute right-1 bottom-1 rounded bg-badge-scrim px-1 py-0.5 font-mono text-[10px] text-badge-scrim-foreground tabular-nums\">\r\n {formatDuration(asset.durationUs)}\r\n </span>\r\n\r\n <div className=\"absolute inset-0 flex items-center justify-center gap-1 bg-badge-scrim opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100\">\r\n <Button\r\n size=\"icon-sm\"\r\n variant=\"secondary\"\r\n aria-label=\"添加到时间轴\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n onAddToTimeline()\r\n }}\r\n >\r\n <Plus className=\"size-3.5\" />\r\n </Button>\r\n <Button\r\n size=\"icon-sm\"\r\n variant=\"secondary\"\r\n aria-label=\"删除素材\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n onRemove()\r\n }}\r\n >\r\n <Trash2 className=\"size-3.5\" />\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"min-w-0 px-0.5\">\r\n <p className=\"truncate text-xs font-medium\" title={asset.name}>\r\n {asset.name}\r\n </p>\r\n <p className=\"truncate text-[10px] text-muted-foreground\">\r\n {asset.kind === 'audio'\r\n ? `${formatBytes(asset.size)} · 48kHz 双声道`\r\n : asset.kind === 'text' || asset.kind === 'subtitle'\r\n ? `${formatResolution(asset.width, asset.height)}`\r\n : `${formatResolution(asset.width, asset.height)} · ${formatBytes(asset.size)}`}\r\n {asset.hasAudio ? ' · 含音轨' : ''}\r\n </p>\r\n </div>\r\n\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <button\r\n type=\"button\"\r\n className=\"absolute top-1 right-1 rounded bg-badge-scrim px-1 text-[11px] text-badge-scrim-foreground opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100 data-[state=open]:opacity-100\"\r\n onClick={(e) => e.stopPropagation()}\r\n aria-label=\"更多操作\"\r\n >\r\n ⋯\r\n </button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\" onClick={(e) => e.stopPropagation()}>\r\n <DropdownMenuItem onSelect={onAddToTimeline}>添加到时间轴</DropdownMenuItem>\r\n <DropdownMenuItem onSelect={onShowMeta}>查看详细信息</DropdownMenuItem>\r\n <DropdownMenuSeparator />\r\n <DropdownMenuItem variant=\"destructive\" onSelect={onRemove}>\r\n 删除素材\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n )\r\n}\r\n","import { Upload } from 'lucide-react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { cn } from '@/lib/cn'\r\nimport { Skeleton } from '@/components/ui/skeleton'\r\nimport { MediaCard } from './MediaCard'\r\nimport type { ImportProgressItem } from '../hooks/useMediaImport'\r\n\r\ninterface MediaGridProps {\r\n assets: MediaAsset[]\r\n selectedAssetId: string | null\r\n missingAssetIds?: string[]\r\n progress: ImportProgressItem[]\r\n onSelect: (assetId: string) => void\r\n onAddToTimeline: (assetId: string) => void\r\n onShowMeta: (assetId: string) => void\r\n onRemove: (assetId: string) => void\r\n onDropFiles: (files: File[]) => void\r\n}\r\n\r\n/** 素材网格:已导入卡片 + 导入中骨架 */\r\nexport function MediaGrid({\r\n assets,\r\n selectedAssetId,\r\n missingAssetIds = [],\r\n progress,\r\n onSelect,\r\n onAddToTimeline,\r\n onShowMeta,\r\n onRemove,\r\n onDropFiles,\r\n}: MediaGridProps) {\r\n return (\r\n <div className=\"grid grid-cols-2 gap-2.5 p-2.5\">\r\n {progress.map((item, index) => (\r\n <div\r\n key={`${item.name}-${index}`}\r\n className={cn(\r\n 'flex flex-col gap-1.5 rounded-lg border border-dashed p-1.5',\r\n item.error != null && 'border-destructive/50 bg-destructive/5',\r\n )}\r\n >\r\n <Skeleton className=\"aspect-video w-full rounded-md\" />\r\n <p className=\"truncate text-[10px] text-muted-foreground\" title={item.name}>\r\n {item.name}\r\n </p>\r\n {item.error != null ? (\r\n <p className=\"line-clamp-2 text-[10px] text-destructive\">{item.error}</p>\r\n ) : (\r\n <div className=\"h-0.5 w-full overflow-hidden rounded-full bg-muted\">\r\n <div\r\n className=\"h-full rounded-full bg-primary transition-all\"\r\n style={{ width: `${Math.round(item.progress * 100)}%` }}\r\n />\r\n </div>\r\n )}\r\n </div>\r\n ))}\r\n\r\n {assets.map((asset) => (\r\n <MediaCard\r\n key={asset.id}\r\n asset={asset}\r\n selected={selectedAssetId === asset.id}\r\n missing={missingAssetIds.includes(asset.id)}\r\n onSelect={() => onSelect(asset.id)}\r\n onAddToTimeline={() => onAddToTimeline(asset.id)}\r\n onShowMeta={() => onShowMeta(asset.id)}\r\n onRemove={() => onRemove(asset.id)}\r\n />\r\n ))}\r\n\r\n {/* 末尾追加一个快捷导入格,方便连续导入 */}\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.multiple = true\r\n input.accept = '.mp4,.m4v,video/mp4,audio/*,image/*,.srt'\r\n input.onchange = () => {\r\n if (input.files) onDropFiles(Array.from(input.files))\r\n }\r\n input.click()\r\n }}\r\n className=\"flex aspect-video flex-col items-center justify-center gap-1 rounded-lg border border-dashed text-muted-foreground transition-colors hover:border-primary/50 hover:text-foreground\"\r\n aria-label=\"继续导入\"\r\n >\r\n <Upload className=\"size-4\" />\r\n <span className=\"text-[10px]\">导入更多</span>\r\n </button>\r\n </div>\r\n )\r\n}\r\n","import { useState } from 'react'\r\nimport { Film, Plus, Subtitles, Type as TypeIcon } from 'lucide-react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { parseSrtDurationUs } from '@cut-workbench/core'\r\nimport { createSubtitleAsset, createTextAsset } from '@cut-workbench/core/engine'\r\nimport { Button } from '@/components/ui/button'\r\nimport { Textarea } from '@/components/ui/textarea'\r\nimport { Label } from '@/components/ui/label'\r\nimport { Separator } from '@/components/ui/separator'\r\nimport { ScrollArea } from '@/components/ui/scroll-area'\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogDescription,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n} from '@/components/ui/dialog'\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuLabel,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { formatBytes, formatDuration, formatResolution } from '@/lib/format'\r\nimport { DropZone } from './DropZone'\r\nimport { MediaGrid } from './MediaGrid'\r\nimport { useMediaImport } from '../hooks/useMediaImport'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { useWorkbench } from '@/context'\r\nimport { notify } from '@/lib/notify'\r\n\r\ninterface MediaLibraryPanelProps {\r\n /** 双击 / 点击「+」时由外层把素材添加到时间轴 */\r\n onAddToTimeline: (assetId: string) => void\r\n onRemoveAsset: (assetId: string) => void\r\n}\r\n\r\nconst KIND_LABEL: Record<MediaAsset['kind'], string> = {\r\n video: '视频',\r\n audio: '音频',\r\n image: '图片',\r\n text: '文字',\r\n subtitle: '字幕',\r\n}\r\n\r\n/**\r\n * 素材库面板:导入区 + 新建(文字/字幕)+ 网格 + 详情。\r\n */\r\nexport function MediaLibraryPanel({ onAddToTimeline, onRemoveAsset }: MediaLibraryPanelProps) {\r\n const assets = useEditor((s) => s.assets)\r\n const selectedAssetId = useEditor((s) => s.selectedAssetId)\r\n const missingAssetIds = useEditor((s) => s.missingAssetIds)\r\n const selectAsset = useEditor((s) => s.selectAsset)\r\n const { importing, progress, importFiles } = useMediaImport()\r\n const [metaAssetId, setMetaAssetId] = useState<string | null>(null)\r\n\r\n const metaAsset = assets.find((a) => a.id === metaAssetId) ?? null\r\n\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b px-3 py-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <h2 className=\"text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\r\n 素材库\r\n </h2>\r\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground tabular-nums\">\r\n {assets.length}\r\n </span>\r\n </div>\r\n <NewAssetMenu onCreated={selectAsset} />\r\n </div>\r\n\r\n <div className=\"shrink-0 px-2.5 pt-2.5\">\r\n <DropZone onFiles={(files) => void importFiles(files)} disabled={importing} />\r\n </div>\r\n\r\n <ScrollArea className=\"thin-scrollbar min-h-0 flex-1\">\r\n {assets.length === 0 && progress.length === 0 ? (\r\n <EmptyState\r\n icon={Film}\r\n title=\"素材库是空的\"\r\n description=\"拖入 MP4 / 音频 / 图片 / SRT,或用右上角「新建」创建文字与字幕\"\r\n className=\"pt-10\"\r\n />\r\n ) : (\r\n <MediaGrid\r\n assets={assets}\r\n selectedAssetId={selectedAssetId}\r\n missingAssetIds={missingAssetIds}\r\n progress={progress}\r\n onSelect={selectAsset}\r\n onAddToTimeline={onAddToTimeline}\r\n onShowMeta={setMetaAssetId}\r\n onRemove={onRemoveAsset}\r\n onDropFiles={(files) => void importFiles(files)}\r\n />\r\n )}\r\n </ScrollArea>\r\n\r\n <Dialog open={metaAsset != null} onOpenChange={(open) => !open && setMetaAssetId(null)}>\r\n {metaAsset != null && (\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle className=\"truncate\">{metaAsset.name}</DialogTitle>\r\n <DialogDescription>素材详细信息</DialogDescription>\r\n </DialogHeader>\r\n <Separator />\r\n <dl className=\"grid grid-cols-2 gap-y-2 text-sm\">\r\n <Row label=\"类型\" value={KIND_LABEL[metaAsset.kind]} />\r\n <Row label=\"MIME\" value={metaAsset.mimeType} />\r\n <Row label=\"时长\" value={formatDuration(metaAsset.durationUs)} />\r\n <Row label=\"分辨率\" value={formatResolution(metaAsset.width, metaAsset.height)} />\r\n <Row\r\n label=\"大小\"\r\n value={metaAsset.kind === 'text' ? '—' : formatBytes(metaAsset.size)}\r\n />\r\n <Row label=\"音轨\" value={metaAsset.hasAudio ? '有(48kHz 双声道)' : '无'} />\r\n <Row label=\"素材 ID\" value={metaAsset.id} mono />\r\n </dl>\r\n <DialogFooter>\r\n <Button variant=\"outline\" size=\"sm\" onClick={() => setMetaAssetId(null)}>\r\n 关闭\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n )}\r\n </Dialog>\r\n </div>\r\n )\r\n}\r\n\r\nfunction Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) {\r\n return (\r\n <>\r\n <dt className=\"text-muted-foreground\">{label}</dt>\r\n <dd className={mono ? 'truncate font-mono text-xs' : 'truncate'}>{value}</dd>\r\n </>\r\n )\r\n}\r\n\r\nfunction NewAssetMenu({ onCreated }: { onCreated: (assetId: string) => void }) {\r\n const [textOpen, setTextOpen] = useState(false)\r\n const [srtOpen, setSrtOpen] = useState(false)\r\n\r\n return (\r\n <>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"outline\" size=\"xs\">\r\n <Plus className=\"size-3\" />\r\n 新建\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent align=\"end\">\r\n <DropdownMenuLabel>非文件素材</DropdownMenuLabel>\r\n <DropdownMenuItem onSelect={() => setTextOpen(true)}>\r\n <TypeIcon className=\"size-3.5\" />\r\n 文字标题\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onSelect={() => setSrtOpen(true)}>\r\n <Subtitles className=\"size-3.5\" />\r\n 粘贴 SRT 字幕\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n\r\n <CreateTextDialog open={textOpen} onOpenChange={setTextOpen} onCreated={onCreated} />\r\n <CreateSubtitleDialog open={srtOpen} onOpenChange={setSrtOpen} onCreated={onCreated} />\r\n </>\r\n )\r\n}\r\n\r\nfunction CreateTextDialog({\r\n open,\r\n onOpenChange,\r\n onCreated,\r\n}: {\r\n open: boolean\r\n onOpenChange: (open: boolean) => void\r\n onCreated: (assetId: string) => void\r\n}) {\r\n const editor = useCutEditor()\r\n const [text, setText] = useState('双击编辑文字')\r\n const projectWidth = useEditor((s) => s.project.width)\r\n const projectHeight = useEditor((s) => s.project.height)\r\n const addAsset = useEditor((s) => s.addAsset)\r\n const { bridge } = useWorkbench()\r\n\r\n const handleCreate = () => {\r\n const { asset } = createTextAsset({\r\n registry: editor.runtime.clips,\r\n text,\r\n width: projectWidth,\r\n height: projectHeight,\r\n })\r\n addAsset(asset)\r\n onCreated(asset.id)\r\n onOpenChange(false)\r\n notify(bridge, { level: 'success', title: '已创建文字素材' })\r\n }\r\n\r\n return (\r\n <Dialog open={open} onOpenChange={onOpenChange}>\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle>新建文字素材</DialogTitle>\r\n <DialogDescription>\r\n 通过 renderTxt2ImgBitmap 渲染为位图,可叠加在任意视频上。\r\n </DialogDescription>\r\n </DialogHeader>\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"text-content\">文字内容</Label>\r\n <Textarea\r\n id=\"text-content\"\r\n value={text}\r\n onChange={(e) => setText(e.target.value)}\r\n rows={3}\r\n />\r\n </div>\r\n <DialogFooter>\r\n <Button onClick={handleCreate} disabled={text.trim().length === 0} size=\"sm\">\r\n 创建\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n )\r\n}\r\n\r\nfunction CreateSubtitleDialog({\r\n open,\r\n onOpenChange,\r\n onCreated,\r\n}: {\r\n open: boolean\r\n onOpenChange: (open: boolean) => void\r\n onCreated: (assetId: string) => void\r\n}) {\r\n const editor = useCutEditor()\r\n const [srt, setSrt] = useState(\r\n '1\\n00:00:00,000 --> 00:00:02,000\\n示例字幕第一句\\n\\n2\\n00:00:02,000 --> 00:00:04,000\\n示例字幕第二句\\n',\r\n )\r\n const projectWidth = useEditor((s) => s.project.width)\r\n const projectHeight = useEditor((s) => s.project.height)\r\n const addAsset = useEditor((s) => s.addAsset)\r\n const { bridge } = useWorkbench()\r\n\r\n const handleCreate = () => {\r\n const durationUs = parseSrtDurationUs(srt)\r\n if (durationUs <= 0) {\r\n notify(bridge, {\r\n level: 'error',\r\n title: 'SRT 解析失败',\r\n description: '请检查时间轴格式:00:00:00,000 --> 00:00:02,000',\r\n })\r\n return\r\n }\r\n const { asset } = createSubtitleAsset({\r\n registry: editor.runtime.clips,\r\n srt,\r\n width: projectWidth,\r\n height: projectHeight,\r\n durationUs,\r\n })\r\n addAsset(asset)\r\n onCreated(asset.id)\r\n onOpenChange(false)\r\n notify(bridge, { level: 'success', title: '已创建字幕素材' })\r\n }\r\n\r\n return (\r\n <Dialog open={open} onOpenChange={onOpenChange}>\r\n <DialogContent className=\"max-w-xl\">\r\n <DialogHeader>\r\n <DialogTitle>粘贴 SRT 字幕</DialogTitle>\r\n <DialogDescription>\r\n 字幕会作为独立片段烧录进画面(EmbedSubtitlesClip,仅支持 SRT)。\r\n </DialogDescription>\r\n </DialogHeader>\r\n <Textarea\r\n value={srt}\r\n onChange={(e) => setSrt(e.target.value)}\r\n rows={9}\r\n className=\"font-mono text-xs\"\r\n />\r\n <DialogFooter>\r\n <Button onClick={handleCreate} disabled={srt.trim().length === 0} size=\"sm\">\r\n 创建字幕素材\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n )\r\n}\r\n","import { useCallback } from 'react'\nimport { useWorkbench } from '../context'\nimport { notify } from '@/lib/notify'\n\n/**\n * 快捷操作:把编辑相关动作 + 错误提示集中在一处,供工具栏与快捷键共用。\n */\nexport interface EditorActions {\n undo: () => void\n redo: () => void\n split: () => void\n removeSelected: () => void\n duplicateSelected: () => void\n selectAll: () => void\n stepFrame: (direction: -1 | 1) => void\n jumpToStart: () => void\n jumpToEnd: () => void\n}\n\n/**\n * 把异常转成可见提示,禁止静默吞掉。\n *\n * 这是**非 hook** 版本,供事件回调 / 非组件代码调用(例如 `useAvCanvas` 的\n * 初始化失败)。它拿不到 React context,因此走 `notify` 注册的默认桥:\n * `WorkbenchProvider` 挂载时会把 `bridge` 注册进来,宿主实现了 `notify`\n * 就交给宿主,否则退回库内 toast。\n */\nexport function reportError(error: unknown, fallback = '操作失败'): void {\n const message = error instanceof Error ? error.message : String(error)\n console.error(error)\n notify(undefined, { level: 'error', title: fallback, description: message })\n}\n\nexport function useToastSafe(): {\n success: (message: string, description?: string) => void\n error: (message: string, description?: string) => void\n info: (message: string, description?: string) => void\n warning: (message: string, description?: string) => void\n} {\n const { bridge } = useWorkbench()\n\n const success = useCallback(\n (message: string, description?: string) => {\n notify(bridge, { level: 'success', title: message, description })\n },\n [bridge],\n )\n const error = useCallback(\n (message: string, description?: string) => {\n notify(bridge, { level: 'error', title: message, description })\n },\n [bridge],\n )\n const info = useCallback(\n (message: string, description?: string) => {\n notify(bridge, { level: 'info', title: message, description })\n },\n [bridge],\n )\n const warning = useCallback(\n (message: string, description?: string) => {\n notify(bridge, { level: 'warning', title: message, description })\n },\n [bridge],\n )\n\n return { success, error, info, warning }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport type { ClipTransform, MediaAsset, Project } from '@cut-workbench/core'\r\nimport { PreviewCanvas, type SyncReport } from '@cut-workbench/core/engine'\r\nimport { reportError } from '@/hooks/use-toast-safe'\r\n\r\nexport interface UseAvCanvasOptions {\r\n width: number\r\n height: number\r\n bgColor?: string\r\n}\r\n\r\nexport interface UseAvCanvasResult {\r\n /** 用 callback ref 挂载画布容器 */\r\n attachRef: (node: HTMLDivElement | null) => void\r\n containerRef: React.RefObject<HTMLDivElement | null>\r\n ready: boolean\r\n sync: (project: Project, assets: MediaAsset[]) => Promise<SyncReport>\r\n /** 只写 sprite 属性(位置/尺寸/旋转/透明度/翻转),不重建 IClip */\r\n applyProps: (project: Project) => void\r\n /** 拖动中的实时几何:只写 sprite + 重绘,不写 store */\r\n previewTransform: (clipId: string, transform: ClipTransform) => Promise<void>\r\n previewFrame: (us: number) => Promise<void>\r\n refresh: (us: number) => Promise<void>\r\n captureImage: () => string | null\r\n /** 命中测试(工程坐标 + 当前时间,只命中此刻可见的片段) */\r\n hitTest: (point: { x: number; y: number }, project: Project, atUs: number) => string | null\r\n /** sprite 上的实时几何(拖动起点) */\r\n getSpriteTransform: (clipId: string) => ClipTransform | null\r\n captureStream: () => MediaStream | null\r\n getCanvas: () => PreviewCanvas | null\r\n /** 画布 CSS 尺寸 / 工程分辨率 */\r\n getCssScale: () => { x: number; y: number }\r\n}\r\n\r\n/**\r\n * 预览画布的 React 适配层。\r\n *\r\n * 用 callback ref 创建/销毁 `PreviewCanvas`:AVCanvas 的构造函数会直接操作 DOM,\r\n * 放在 effect 里再 setState 会触发「effect 内同步 setState」的反模式,\r\n * callback ref 则天然在提交阶段执行,语义更准确。\r\n *\r\n * ⚠️ 播放不通过 AVCanvas.play(),由 usePlaybackClock + previewFrame 驱动。\r\n * ⚠️ 工程分辨率变化时需 remount(调用方应把 width/height 放进 key)。\r\n * ⚠️ 本层只管画布;选中框与画布内交互在 feature 层(SelectionOverlay /\r\n * useCanvasInteraction),因为那是纯 UI 逻辑,不该下沉到 core。\r\n */\r\nexport function useAvCanvas({\r\n width,\r\n height,\r\n bgColor,\r\n}: UseAvCanvasOptions): UseAvCanvasResult {\r\n const editor = useCutEditor()\r\n const containerRef = useRef<HTMLDivElement | null>(null)\r\n const canvasRef = useRef<PreviewCanvas | null>(null)\r\n const [ready, setReady] = useState(false)\r\n\r\n const attachRef = useCallback(\r\n (node: HTMLDivElement | null) => {\r\n // 先销毁旧实例(node 变化或卸载)\r\n canvasRef.current?.destroy()\r\n canvasRef.current = null\r\n containerRef.current = node\r\n\r\n if (node == null) {\r\n setReady(false)\r\n return\r\n }\r\n\r\n try {\r\n canvasRef.current = new PreviewCanvas(node, { width, height, bgColor, registry: editor.runtime.clips })\r\n setReady(true)\r\n } catch (err) {\r\n setReady(false)\r\n reportError(err, '画布初始化失败')\r\n }\r\n },\r\n [bgColor, height, width],\r\n )\r\n\r\n // 卸载兜底(callback ref 的 cleanup 在 React 19 里不会自动执行,需显式处理)\r\n useEffect(() => {\r\n return () => {\r\n canvasRef.current?.destroy()\r\n canvasRef.current = null\r\n }\r\n }, [])\r\n\r\n const sync = useCallback(async (project: Project, assets: MediaAsset[]) => {\r\n const canvas = canvasRef.current\r\n if (canvas == null) return { created: [], removed: [], errors: [] }\r\n return canvas.sync(project, assets)\r\n }, [])\r\n\r\n const previewFrame = useCallback(async (us: number) => {\r\n await canvasRef.current?.previewFrame(us)\r\n }, [])\r\n\r\n const applyProps = useCallback((project: Project) => {\r\n canvasRef.current?.applyProps(project)\r\n }, [])\r\n\r\n const previewTransform = useCallback((clipId: string, transform: ClipTransform) => {\r\n return canvasRef.current?.previewTransform(clipId, transform) ?? Promise.resolve()\r\n }, [])\r\n\r\n const refresh = useCallback(async (us: number) => {\r\n await canvasRef.current?.refresh(us)\r\n }, [])\r\n\r\n const captureImage = useCallback(() => canvasRef.current?.captureImage() ?? null, [])\r\n\r\n const hitTest = useCallback(\r\n (point: { x: number; y: number }, project: Project, atUs: number) => {\r\n return canvasRef.current?.hitTest(point, project, atUs) ?? null\r\n },\r\n [],\r\n )\r\n\r\n const getSpriteTransform = useCallback((clipId: string) => {\r\n return canvasRef.current?.getSpriteTransform(clipId) ?? null\r\n }, [])\r\n\r\n const captureStream = useCallback(() => canvasRef.current?.captureStream() ?? null, [])\r\n\r\n const getCanvas = useCallback(() => canvasRef.current, [])\r\n\r\n const getCssScale = useCallback(\r\n () => canvasRef.current?.getCssScale() ?? { x: 1, y: 1 },\r\n [],\r\n )\r\n\r\n return {\r\n attachRef,\r\n containerRef,\r\n ready,\r\n sync,\r\n applyProps,\r\n previewTransform,\r\n previewFrame,\r\n refresh,\r\n captureImage,\r\n hitTest,\r\n getSpriteTransform,\r\n captureStream,\r\n getCanvas,\r\n getCssScale,\r\n }\r\n}\r\n","import { useEffect, useRef } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport type { MediaAsset, Project } from '@cut-workbench/core'\r\nimport type { UseAvCanvasResult } from './useAvCanvas'\r\n\r\ninterface UsePreviewSyncOptions {\r\n project: Project\r\n assets: MediaAsset[]\r\n canvas: UseAvCanvasResult\r\n /** 强制重绘当前帧(属性变化后调用) */\r\n refresh: (us: number) => Promise<void>\r\n onSyncErrors: (errors: Array<{ clipId: string; message: string }>) => void\r\n}\r\n\r\n/**\r\n * 结构指纹:只包含「会导致 IClip 重建」的字段。\r\n *\r\n * 位置 / 透明度 / 文字样式等纯属性不在内 —— 它们只需要把值写回 sprite,\r\n * 不需要重建 IClip(重建会重新解码,代价高且会闪)。\r\n */\r\nfunction buildStructureKey(project: Project): string {\r\n const tracks = project.tracks.map((t) => `${t.id}:${t.hidden ? 1 : 0}`).join(',')\r\n const clips = project.clips\r\n .map(\r\n (c) =>\r\n `${c.id}:${c.trackId}:${c.assetId}:${c.inPointUs}:${c.durationUs}:${c.startUs}:${c.playbackRate}`,\r\n )\r\n .join(';')\r\n return `${project.width}x${project.height}#${tracks}#${clips}`\r\n}\r\n\r\n/**\r\n * 属性指纹:不触发重建、但必须同步到 sprite 的字段。\r\n *\r\n * ⚠️ 为什么必须单独有这一层:AVCanvas 的内部 33ms 循环只会「重绘」sprite,\r\n * 不会把 store 的数值写进 sprite.rect / opacity / flip。如果只靠 refresh(),\r\n * 属性面板改 X / 宽 / 旋转后 store 变了但画面纹丝不动 —— 实测确认的 bug。\r\n * 因此属性变化时必须走一遍 `canvas.applyProps()`(只写属性,不重建)。\r\n */\r\nfunction buildPropsKey(project: Project): string {\r\n return project.clips\r\n .map(\r\n (c) =>\r\n `${c.id}:${c.transform.x},${c.transform.y},${c.transform.w},${c.transform.h},${c.transform.angle}:${c.opacity}:${c.flip ?? '-'}`,\r\n )\r\n .join(';')\r\n}\r\n\r\n/**\r\n * 把 store 的时间轴数据同步到 AVCanvas。\r\n *\r\n * 三档处理(按代价从低到高):\r\n * 1. 结构变化 → 全量 `sync`(新建/销毁 IClip 与 sprite);\r\n * 2. 属性变化 → `applyProps`(只写 sprite 属性,然后重绘当前帧);\r\n * 3. 都没变 → 仅重绘当前帧。\r\n *\r\n * ⚠️ sync 必须串行且可取消:快速连续的结构变化(拖拽中持续 split)并发执行会重复 addSprite。\r\n * 播放头从 store 直接读取(getState),避免把它放进依赖导致每帧重跑同步。\r\n */\r\nexport function usePreviewSync({\r\n project,\r\n assets,\r\n canvas,\r\n refresh,\r\n onSyncErrors,\r\n}: UsePreviewSyncOptions): void {\r\n const editor = useCutEditor()\r\n const lastKeyRef = useRef<string>('')\r\n const lastPropsKeyRef = useRef<string>('')\r\n const runIdRef = useRef(0)\r\n const onSyncErrorsRef = useRef(onSyncErrors)\r\n\r\n useEffect(() => {\r\n onSyncErrorsRef.current = onSyncErrors\r\n })\r\n\r\n useEffect(() => {\r\n if (!canvas.ready) {\r\n lastKeyRef.current = ''\r\n lastPropsKeyRef.current = ''\r\n return\r\n }\r\n const playhead = editor.store.getState().playheadUs\r\n const key = buildStructureKey(project)\r\n const propsKey = buildPropsKey(project)\r\n\r\n if (key === lastKeyRef.current) {\r\n if (propsKey !== lastPropsKeyRef.current) {\r\n // 只改了位置/尺寸/旋转/透明度/翻转:写回 sprite 再重绘\r\n canvas.applyProps(project)\r\n lastPropsKeyRef.current = propsKey\r\n }\r\n void refresh(playhead)\r\n return\r\n }\r\n\r\n const runId = ++runIdRef.current\r\n void (async () => {\r\n try {\r\n const report = await canvas.sync(project, assets)\r\n if (runId !== runIdRef.current) return\r\n if (report.errors.length > 0) onSyncErrorsRef.current(report.errors)\r\n lastKeyRef.current = key\r\n lastPropsKeyRef.current = propsKey\r\n await refresh(editor.store.getState().playheadUs)\r\n } catch (err) {\r\n if (runId !== runIdRef.current) return\r\n onSyncErrorsRef.current([\r\n { clipId: '', message: err instanceof Error ? err.message : String(err) },\r\n ])\r\n }\r\n })()\r\n }, [assets, canvas, project, refresh])\r\n}\r\n","import { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { MediaAsset, Project } from '@cut-workbench/core'\r\nimport { loadAudioClipForAsset } from '@cut-workbench/core/engine'\r\nimport { getProjectDuration } from '@cut-workbench/core'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\n\r\nexport interface UsePlaybackClockOptions {\r\n project: Project\r\n assets: MediaAsset[]\r\n /** 每帧渲染回调(微秒) */\r\n onFrame: (us: number) => void\r\n /** 播放到达末尾时回调 */\r\n onEnded?: () => void\r\n masterVolume: number\r\n}\r\n\r\nexport interface UsePlaybackClockResult {\r\n playing: boolean\r\n currentUs: number\r\n play: () => void\r\n pause: () => void\r\n toggle: () => void\r\n seek: (us: number) => void\r\n setPlaybackRate: (rate: number) => void\r\n playbackRate: number\r\n durationUs: number\r\n /** 拖动播放头时暂停音频(不改变播放状态) */\r\n beginScrub: () => void\r\n endScrub: () => void\r\n}\r\n\r\n/**\r\n * 主时钟驱动的播放循环。\r\n *\r\n * 时间来源:`AudioContext.currentTime`(core/clock/playback-clock)。\r\n * rAF 只负责「读取当前时间 → 渲染一帧」,不做任何时间累加,因此长时间播放不会漂移。\r\n *\r\n * 音频:由 `previewAudioEngine` 直接把 PCM 排进同一个 AudioContext,与应用时钟同源。\r\n *\r\n * ⚠️ 拖动播放头时若仍在播放,音频会断续(WebAV tick 的 3s 重置语义),\r\n * 因此 `beginScrub` 会先暂停音频排程,松手后再重新排程。\r\n */\r\nexport function usePlaybackClock({\r\n project,\r\n assets,\r\n onFrame,\r\n onEnded,\r\n masterVolume,\r\n}: UsePlaybackClockOptions): UsePlaybackClockResult {\r\n const editor = useCutEditor()\r\n const [playing, setPlaying] = useState(false)\r\n const [currentUs, setCurrentUs] = useState(0)\r\n const [playbackRate, setPlaybackRateState] = useState(1)\r\n\r\n const rafRef = useRef<number | null>(null)\r\n const playingRef = useRef(false)\r\n const onFrameRef = useRef(onFrame)\r\n const onEndedRef = useRef(onEnded)\r\n const projectRef = useRef(project)\r\n const assetsRef = useRef(assets)\r\n const rateRef = useRef(1)\r\n\r\n // 所有「最新值」的同步统一放在 effect 里,不在渲染期写 ref\r\n useEffect(() => {\r\n onFrameRef.current = onFrame\r\n onEndedRef.current = onEnded\r\n projectRef.current = project\r\n assetsRef.current = assets\r\n })\r\n\r\n const durationUs = getProjectDuration(project)\r\n\r\n useEffect(() => {\r\n editor.runtime.audio.setMasterVolume(masterVolume)\r\n }, [masterVolume])\r\n\r\n // 为含音轨的素材注册 PCM 解码(懒加载,失败静默 —— 只影响预览音质)\r\n useEffect(() => {\r\n const { clock, audio, clips } = editor.runtime\r\n const ctx = clock.context\r\n if (ctx != null) audio.attach(ctx)\r\n for (const asset of assets) {\r\n if (asset.kind !== 'audio' && asset.kind !== 'video') continue\r\n if (!asset.hasAudio) continue\r\n audio.registerAsset(asset.id, async () => {\r\n const clip = await loadAudioClipForAsset(clips, asset.id)\r\n if (clip == null) throw new Error(`无法加载音频:${asset.name}`)\r\n return clip\r\n })\r\n }\r\n }, [assets])\r\n\r\n // 用 ref 持有循环体,避免 useCallback 自引用(React Compiler 不允许变量在声明前被引用)\r\n // 用 ref 持有循环体,避免 useCallback 自引用(React Compiler 不允许变量在声明前被引用)。\r\n // 赋值放在 effect 中,保持渲染纯函数。\r\n const loopRef = useRef<() => void>(() => undefined)\r\n useEffect(() => {\r\n loopRef.current = () => {\r\n if (!playingRef.current) return\r\n const { clock, audio } = editor.runtime\r\n const us = clock.currentUs\r\n const total = getProjectDuration(projectRef.current)\r\n setCurrentUs(us)\r\n onFrameRef.current(us)\r\n if (total > 0 && us >= total) {\r\n playingRef.current = false\r\n clock.pause()\r\n audio.stopAll()\r\n setPlaying(false)\r\n setCurrentUs(total)\r\n onEndedRef.current?.()\r\n return\r\n }\r\n rafRef.current = requestAnimationFrame(() => loopRef.current())\r\n }\r\n })\r\n\r\n const startAudio = useCallback((fromUs: number) => {\r\n const state = projectRef.current\r\n const assetMap = new Map(assetsRef.current.map((a) => [a.id, a]))\r\n const trackMap = new Map(state.tracks.map((t) => [t.id, t]))\r\n const items = state.clips.flatMap((clip) => {\r\n const track = trackMap.get(clip.trackId)\r\n const asset = assetMap.get(clip.assetId)\r\n if (!track || !asset) return []\r\n return [{ clip, track, assetKind: asset.kind }]\r\n })\r\n void editor.runtime.audio.scheduleFrom(fromUs, items, getProjectDuration(state))\r\n }, [])\r\n\r\n const play = useCallback(() => {\r\n if (playingRef.current) return\r\n const { clock, audio } = editor.runtime\r\n const ctx = clock.ensureContext()\r\n audio.attach(ctx)\r\n let from = clock.positionUs\r\n const total = getProjectDuration(projectRef.current)\r\n if (total > 0 && from >= total) from = 0\r\n clock.play(from, rateRef.current)\r\n startAudio(from)\r\n playingRef.current = true\r\n setPlaying(true)\r\n setCurrentUs(from)\r\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\r\n rafRef.current = requestAnimationFrame(() => loopRef.current())\r\n }, [startAudio])\r\n\r\n const pause = useCallback(() => {\r\n if (!playingRef.current) return\r\n playingRef.current = false\r\n if (rafRef.current != null) {\r\n cancelAnimationFrame(rafRef.current)\r\n rafRef.current = null\r\n }\r\n editor.runtime.clock.pause()\r\n editor.runtime.audio.stopAll()\r\n setPlaying(false)\r\n setCurrentUs(editor.runtime.clock.positionUs)\r\n }, [])\r\n\r\n const toggle = useCallback(() => {\r\n if (playingRef.current) pause()\r\n else play()\r\n }, [pause, play])\r\n\r\n const seek = useCallback(\r\n (us: number) => {\r\n const total = getProjectDuration(projectRef.current)\r\n const clamped = Math.max(0, total > 0 ? Math.min(us, total) : us)\r\n editor.runtime.clock.seek(clamped)\r\n setCurrentUs(clamped)\r\n onFrameRef.current(clamped)\r\n if (playingRef.current) {\r\n // 播放中跳转:重排音频,避免 tick 状态错乱\r\n startAudio(clamped)\r\n }\r\n },\r\n [startAudio],\r\n )\r\n\r\n const setPlaybackRate = useCallback((rate: number) => {\r\n const safe = rate > 0 ? rate : 1\r\n rateRef.current = safe\r\n editor.runtime.clock.setRate(safe)\r\n setPlaybackRateState(safe)\r\n }, [])\r\n\r\n const beginScrub = useCallback(() => {\r\n editor.runtime.audio.stopAll()\r\n }, [])\r\n\r\n const endScrub = useCallback(() => {\r\n if (playingRef.current) {\r\n startAudio(editor.runtime.clock.positionUs)\r\n }\r\n }, [startAudio])\r\n\r\n // 卸载时停止播放并释放音频\r\n useEffect(() => {\r\n return () => {\r\n playingRef.current = false\r\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\r\n editor.runtime.audio.stopAll()\r\n editor.runtime.clock.pause()\r\n }\r\n }, [])\r\n\r\n // 首帧渲染在画布就绪后由 PreviewPanel 负责;这里不主动触发\r\n\r\n return {\r\n playing,\r\n currentUs,\r\n play,\r\n pause,\r\n toggle,\r\n seek,\r\n setPlaybackRate,\r\n playbackRate,\r\n durationUs,\r\n beginScrub,\r\n endScrub,\r\n }\r\n}\r\n\r\n","/**\n * 画布内几何交互(选中 / 拖动 / 缩放 / 旋转)。\n *\n * 为什么自绘而不是用 AVCanvas 内建的选中框:\n * - 内建选框每次 `propsChange` 都读 `getBoundingClientRect()` 并写 style,\n * 同时我们的 store 回写又会触发 React 重渲染,拖动时主线程被两套更新挤爆\n * (实测 40 次 pointermove 会产生 6 个 ~100ms 的 long task);\n * - 内建选框的样式只能 `!important` 覆盖私有 DOM,改不动结构(比如想加辅助线、\n * 想按素材类型换颜色都做不到)。\n *\n * 现在的链路:\n * pointermove → 更新本地 ref + 直接写 sprite.rect(不写 store)\n * → DOM 选框只改 transform(浏览器合成层完成,不开重排)\n * pointerup → 一次性写回 store(一步撤销)\n *\n * 因此拖动期间 React 完全不重渲染,选框与画面按同一帧推进。\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { ClipTransform } from '@cut-workbench/core'\nimport type { PreviewCanvas } from '@cut-workbench/core/engine'\nimport {\n normalizeAngle,\n resizeTransform,\n sameTransform,\n type HandleKey,\n} from '@cut-workbench/core'\n\nexport type CanvasDragMode = 'move' | 'resize' | 'rotate'\n\nexport interface CanvasInteractionState {\n clipId: string\n mode: CanvasDragMode\n handle: HandleKey | null\n}\n\ninterface UseCanvasInteractionOptions {\n /** 当前画布实例;未就绪时返回 null */\n getCanvas: () => PreviewCanvas | null\n /** 工程分辨率 */\n projectWidth: number\n projectHeight: number\n /** 画布 DOM 容器(用于换算指针坐标与测量 CSS 尺寸) */\n hostRef: React.RefObject<HTMLDivElement | null>\n /** 当前选中的 clip(单选时才允许画布内变换) */\n selectedClipId: string | null\n /** 该 clip 在 store 里的几何 */\n transform: ClipTransform | null\n /**\n * 是否允许在画布内变换。\n *\n * 为 false 时手柄不响应(例如播放头已经离开该片段区间 —— 此时画面上根本看不到它,\n * 却在原位能被拖走,会让用户误以为「拖了块空白」)。\n */\n interactive: boolean\n /** 该片段是否锁定了宽高比(属性面板的锁) */\n aspectLocked?: boolean\n /** 命中测试所需:给定工程坐标返回 clip id */\n hitTest: (point: { x: number; y: number }) => string | null\n /** 点选画布内素材(不会触发 store 的历史) */\n onSelect: (clipId: string | null) => void\n /** 一次变换结束时提交(drag 期间不写 store) */\n onCommit: (clipId: string, transform: ClipTransform) => void\n}\n\nexport interface UseCanvasInteractionResult {\n /** 拖动中的实时几何(覆盖 store 值用于绘制选框) */\n liveTransform: ClipTransform | null\n interaction: CanvasInteractionState | null\n /** 传给选框容器的 pointerdown */\n onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void\n}\n\nexport function useCanvasInteraction({\n getCanvas,\n projectWidth,\n projectHeight,\n hostRef,\n selectedClipId,\n transform,\n interactive,\n aspectLocked = false,\n hitTest,\n onSelect,\n onCommit,\n}: UseCanvasInteractionOptions): UseCanvasInteractionResult {\n const [liveTransform, setLiveTransform] = useState<ClipTransform | null>(null)\n const [interaction, setInteraction] = useState<CanvasInteractionState | null>(null)\n\n const liveRef = useRef<ClipTransform | null>(null)\n const rafRef = useRef<number | null>(null)\n const envRef = useRef({\n getCanvas,\n projectWidth,\n projectHeight,\n hostRef,\n selectedClipId,\n transform,\n interactive,\n aspectLocked,\n hitTest,\n onSelect,\n onCommit,\n })\n\n useEffect(() => {\n envRef.current = {\n getCanvas,\n projectWidth,\n projectHeight,\n hostRef,\n selectedClipId,\n transform,\n interactive,\n aspectLocked,\n hitTest,\n onSelect,\n onCommit,\n }\n })\n\n /** 指针事件 → 工程坐标 */\n const toProjectPoint = useCallback((clientX: number, clientY: number) => {\n const host = envRef.current.hostRef.current\n if (host == null) return null\n const rect = host.getBoundingClientRect()\n const scaleX = rect.width > 0 ? rect.width / envRef.current.projectWidth : 1\n const scaleY = rect.height > 0 ? rect.height / envRef.current.projectHeight : 1\n return {\n x: (clientX - rect.left) / (scaleX || 1),\n y: (clientY - rect.top) / (scaleY || 1),\n }\n }, [])\n\n const flush = useCallback(() => {\n rafRef.current = null\n const next = liveRef.current\n const canvas = envRef.current.getCanvas()\n const id = envRef.current.selectedClipId\n if (next == null || canvas == null || id == null) return\n // 只写 sprite,不写 store:拖动期间 React 不参与\n void canvas.previewTransform(id, next)\n }, [])\n\n const scheduleFlush = useCallback(() => {\n if (rafRef.current != null) return\n rafRef.current = requestAnimationFrame(flush)\n }, [flush])\n\n const startDrag = useCallback(\n (\n mode: CanvasDragMode,\n handle: HandleKey | null,\n origin: ClipTransform,\n startPoint: { x: number; y: number },\n startAngleDeg: number,\n startPointerAngle: number,\n clipId: string,\n ) => {\n setInteraction({ clipId, mode, handle })\n const onMove = (event: PointerEvent) => {\n const point = toProjectPoint(event.clientX, event.clientY)\n if (point == null) return\n let next: ClipTransform\n if (mode === 'move') {\n next = {\n ...origin,\n x: origin.x + (point.x - startPoint.x),\n y: origin.y + (point.y - startPoint.y),\n }\n } else if (mode === 'rotate') {\n const center = { x: origin.x + origin.w / 2, y: origin.y + origin.h / 2 }\n const angle = (Math.atan2(point.y - center.y, point.x - center.x) * 180) / Math.PI\n next = {\n ...origin,\n angle: normalizeAngle(startAngleDeg + (angle - startPointerAngle)),\n }\n } else if (handle != null) {\n /*\n * Shift 临时等比(所有手柄);片段自身锁定宽高比时只有角手柄等比。\n * 边手柄若也等比,会变成「拖右边、上下也跟着长」,与用户的单维意图相悖。\n */\n const keepAspect = event.shiftKey\n ? true\n : envRef.current.aspectLocked\n ? ('corners' as const)\n : false\n next = resizeTransform(origin, handle, point, keepAspect)\n } else {\n return\n }\n liveRef.current = next\n setLiveTransform(next)\n scheduleFlush()\n }\n\n const onUp = () => {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', onUp)\n window.removeEventListener('pointercancel', onUp)\n if (rafRef.current != null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n const final = liveRef.current\n liveRef.current = null\n setLiveTransform(null)\n setInteraction(null)\n if (final != null && !sameTransform(final, origin)) {\n onCommit(clipId, final)\n }\n }\n\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', onUp)\n window.addEventListener('pointercancel', onUp)\n },\n [onCommit, scheduleFlush, toProjectPoint],\n )\n\n const onPointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n const target = event.target as HTMLElement\n const dataset = target.dataset\n const point = toProjectPoint(event.clientX, event.clientY)\n if (point == null) return\n\n // 手柄优先:先看是否按在已选中素材的手柄上\n const handleKey = dataset.handle as HandleKey | undefined\n const rotate = dataset.rotate != null\n const currentId = envRef.current.selectedClipId\n const currentTransform = envRef.current.transform\n\n if (\n currentId != null &&\n currentTransform != null &&\n envRef.current.interactive &&\n (handleKey != null || rotate)\n ) {\n event.preventDefault()\n event.stopPropagation()\n const center = {\n x: currentTransform.x + currentTransform.w / 2,\n y: currentTransform.y + currentTransform.h / 2,\n }\n const startPointerAngle =\n (Math.atan2(point.y - center.y, point.x - center.x) * 180) / Math.PI\n startDrag(\n rotate ? 'rotate' : 'resize',\n rotate ? null : handleKey!,\n currentTransform,\n point,\n currentTransform.angle,\n startPointerAngle,\n currentId,\n )\n return\n }\n\n // 命中素材:选中并开始拖动\n const hitId = envRef.current.hitTest(point)\n if (hitId == null) {\n envRef.current.onSelect(null)\n return\n }\n envRef.current.onSelect(hitId)\n\n /*\n * 命中后需要先拿到「被选中 clip 的几何」才能开始拖动。\n * 但 onSelect 会写 store,当前这一帧读不到新值 —— 因此用 hit 目标的\n * 实时几何作为起点(由调用方通过 getTransform 提供),失败则本轮不拖动。\n */\n const canvas = envRef.current.getCanvas()\n const targetTransform = canvas?.getSpriteTransform(hitId) ?? null\n if (targetTransform == null) return\n event.preventDefault()\n startDrag('move', null, targetTransform, point, targetTransform.angle, 0, hitId)\n },\n [startDrag, toProjectPoint],\n )\n\n // 卸载时清理未完成的动画帧\n useEffect(() => {\n return () => {\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current)\n }\n }, [])\n\n return { liveTransform, interaction, onPointerDown }\n}\n","import { Camera } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\n\ninterface CaptureButtonProps {\n onCapture: () => void\n disabled?: boolean\n}\n\n/**\n * 截图当前帧。\n *\n * 内部走 `AVCanvas.captureImage()`(返回 base64 data URL),由调用方负责下载。\n * 单独抽成组件是为了让预览工具条可以复用它(当前工具条直接内联在 PreviewStage,\n * 保留此导出供扩展)。\n */\nexport function CaptureButton({ onCapture, disabled }: CaptureButtonProps) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"secondary\"\n size=\"icon-sm\"\n onClick={onCapture}\n disabled={disabled}\n aria-label=\"截图当前帧\"\n >\n <Camera className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>截图当前帧</TooltipContent>\n </Tooltip>\n )\n}\n","import type { ClipTransform } from '@cut-workbench/core'\nimport { handleCursor, handleLocalOffset, RESIZE_HANDLES } from '@cut-workbench/core'\n\ninterface SelectionOverlayProps {\n /** 工程坐标系下的几何(拖动中为实时值) */\n transform: ClipTransform\n /** 工程分辨率 → CSS 像素的缩放 */\n scaleX: number\n scaleY: number\n /** 只读(音频等不可变换的 clip)时不画手柄 */\n interactive?: boolean\n}\n\n/**\n * 画布自绘选框。\n *\n * 布局:外层 div 用 `left/top/width/height + rotate` 定位,与素材完全重合;\n * 手柄与旋转柄用 `translate(-50%,-50%)` 贴在外层的边上。\n *\n * 性能:拖动时只更新这些元素的 style(transform/left/top),不做布局以外的\n * React 计算 —— 但因为我们在拖动期间不写 store,React 实际上也不重渲染。\n */\nexport function SelectionOverlay({\n transform,\n scaleX,\n scaleY,\n interactive = true,\n}: SelectionOverlayProps) {\n const { x, y, w, h, angle } = transform\n const left = x * scaleX\n const top = y * scaleY\n const width = Math.max(1, w * scaleX)\n const height = Math.max(1, h * scaleY)\n\n return (\n <div\n className=\"pointer-events-none absolute origin-center\"\n style={{\n left,\n top,\n width,\n height,\n transform: `rotate(${angle}deg)`,\n zIndex: 20,\n }}\n data-selection-box\n >\n <div className=\"absolute inset-0 border-2 border-primary shadow-[0_0_0_1px_rgba(0,0,0,0.55)]\" />\n\n {interactive && (\n <>\n {RESIZE_HANDLES.map((key) => {\n const offset = handleLocalOffset(key, width, height)\n return (\n <div\n key={key}\n data-handle={key}\n className=\"pointer-events-auto absolute size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background\"\n style={{\n left: width / 2 + offset.x,\n top: height / 2 + offset.y,\n cursor: handleCursor(key, angle),\n }}\n />\n )\n })}\n <div\n data-rotate=\"true\"\n className=\"pointer-events-auto absolute size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background\"\n style={{\n // 旋转柄放在上边中点的外侧(按缩放后固定 20px,避免素材很小时压住角柄)\n left: width / 2,\n top: -20,\n cursor: handleCursor('rotate', angle),\n }}\n />\n <div\n className=\"pointer-events-none absolute bg-primary\"\n style={{ left: width / 2 - 0.5, top: -20, width: 1, height: 20 }}\n />\n </>\n )}\n </div>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { Maximize2 } from 'lucide-react'\nimport type { ClipTransform } from '@cut-workbench/core'\nimport { Button } from '@/components/ui/button'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport { cn } from '@/lib/cn'\nimport { CaptureButton } from './CaptureButton'\nimport { SelectionOverlay } from './SelectionOverlay'\nimport type { CanvasInteractionState } from '../hooks/useCanvasInteraction'\n\ninterface PreviewStageProps {\n /** callback ref:由 useAvCanvas 负责挂载/销毁 AVCanvas 实例 */\n attachRef: (node: HTMLDivElement | null) => void\n width: number\n height: number\n emptyHint?: React.ReactNode\n onCapture: () => void\n onFullscreen: () => void\n /** 选中素材的几何(拖动中为实时值);null 表示不画选框 */\n selection: ClipTransform | null\n /** 是否显示可拖拽手柄(音频片段等不显示) */\n selectionInteractive: boolean\n interaction: CanvasInteractionState | null\n onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void\n className?: string\n}\n\n/**\n * 预览舞台。\n *\n * AVCanvas 依赖容器的 clientWidth/clientHeight 换算指针坐标,并给内部 canvas 设\n * `width:100%;height:100%`。因此这里用 ResizeObserver 测量可用空间后,\n * 计算一个精确的 CSS 像素尺寸(等比 fit),而不是依赖百分比。\n *\n * 选框叠加层与画布共用同一个缩放系数(`box.w / width`),保证「画出来的框」\n * 与「画面里的素材」严格重合。\n */\nexport function PreviewStage({\n attachRef,\n width,\n height,\n emptyHint,\n onCapture,\n onFullscreen,\n selection,\n selectionInteractive,\n interaction,\n onPointerDown,\n className,\n}: PreviewStageProps) {\n const outerRef = useRef<HTMLDivElement | null>(null)\n const [box, setBox] = useState({ w: 0, h: 0 })\n\n useEffect(() => {\n const el = outerRef.current\n if (el == null) return\n const padding = 32\n const observer = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect\n if (!rect) return\n const availableW = Math.max(0, rect.width - padding)\n const availableH = Math.max(0, rect.height - padding)\n if (availableW <= 0 || availableH <= 0) return\n const scale = Math.min(availableW / width, availableH / height)\n setBox({\n w: Math.max(1, Math.floor(width * scale)),\n h: Math.max(1, Math.floor(height * scale)),\n })\n })\n observer.observe(el)\n return () => observer.disconnect()\n }, [width, height])\n\n const scaleX = width > 0 ? box.w / width : 1\n const scaleY = height > 0 ? box.h / height : 1\n\n return (\n <div\n ref={outerRef}\n className={cn(\n 'relative flex min-h-0 flex-1 items-center justify-center overflow-hidden bg-stage-bg',\n className,\n )}\n >\n {/*\n * 内层是「画面本身」的容器:视频在导出时就是黑底合成,\n * 这里的 bg-black 属于内容底色而不是 UI 主题色,故意保留固定黑。\n * 外层的舞台底则走 --cw-stage-bg,跟随主题。\n */}\n <div\n className=\"relative shrink-0 overflow-hidden rounded-md bg-black shadow-2xl ring-1 ring-border\"\n style={{ width: box.w, height: box.h }}\n >\n <div\n ref={attachRef}\n className=\"av-canvas-host size-full\"\n style={{ width: '100%', height: '100%' }}\n />\n {/* 交互层:整块覆盖在画布上,负责点选与拖动;选框本身在它内部 */}\n <div\n className=\"absolute inset-0\"\n onPointerDown={onPointerDown}\n style={{ cursor: interaction?.mode === 'move' ? 'grabbing' : 'default' }}\n >\n {selection != null && (\n <SelectionOverlay\n transform={selection}\n scaleX={scaleX}\n scaleY={scaleY}\n interactive={selectionInteractive}\n />\n )}\n </div>\n {emptyHint != null && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center px-6\">\n {emptyHint}\n </div>\n )}\n </div>\n\n <div className=\"absolute right-3 bottom-3 flex items-center gap-1\">\n <CaptureButton onCapture={onCapture} />\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"secondary\" size=\"icon-sm\" onClick={onFullscreen} aria-label=\"全屏预览\">\n <Maximize2 className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>全屏预览</TooltipContent>\n </Tooltip>\n </div>\n </div>\n )\n}\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Slider as SliderPrimitive } from \"radix-ui\"\n\nfunction Slider({\n className,\n defaultValue,\n value,\n min = 0,\n max = 100,\n ...props\n}: React.ComponentProps<typeof SliderPrimitive.Root>) {\n const _values = React.useMemo(\n () =>\n Array.isArray(value)\n ? value\n : Array.isArray(defaultValue)\n ? defaultValue\n : [min, max],\n [value, defaultValue, min, max]\n )\n\n return (\n <SliderPrimitive.Root\n data-slot=\"slider\"\n defaultValue={defaultValue}\n value={value}\n min={min}\n max={max}\n className={cn(\n \"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col\",\n className\n )}\n {...props}\n >\n <SliderPrimitive.Track\n data-slot=\"slider-track\"\n className={cn(\n \"relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5\"\n )}\n >\n <SliderPrimitive.Range\n data-slot=\"slider-range\"\n className={cn(\n \"absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\"\n )}\n />\n </SliderPrimitive.Track>\n {Array.from({ length: _values.length }, (_, index) => (\n <SliderPrimitive.Thumb\n data-slot=\"slider-thumb\"\n key={index}\n // thumb 跟随主题:亮色下白芯深边、暗色下深芯浅边(shadcn 原版写死 bg-white,\n // 暗色主题里会出现一个刺眼的白点)\n className=\"block size-4 shrink-0 rounded-full border border-primary bg-background shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50\"\n />\n ))}\n </SliderPrimitive.Root>\n )\n}\n\nexport { Slider }\n","import { formatTimecode } from '@cut-workbench/core'\nimport { cn } from '@/lib/cn'\n\ninterface TimecodeProps {\n /** 微秒 */\n us: number\n /** 是否显示毫秒段 */\n showMs?: boolean\n className?: string\n /** 显示为弱化色(例如总时长) */\n muted?: boolean\n title?: string\n}\n\n/**\n * µs ↔ `HH:MM:SS.mmm` 的唯一展示入口。\n * 等宽字体 + `tabular-nums`,避免播放时数字宽度跳动。\n */\nexport function Timecode({ us, showMs = true, className, muted, title }: TimecodeProps) {\n return (\n <span\n className={cn(\n 'font-mono text-xs tabular-nums',\n muted && 'text-muted-foreground',\n className,\n )}\n title={title ?? `${us} µs`}\n >\n {formatTimecode(us, showMs)}\n </span>\n )\n}\n","import {\n Pause,\n Play,\n SkipBack,\n SkipForward,\n StepBack,\n StepForward,\n Volume2,\n VolumeX,\n} from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\nimport { Button } from '@/components/ui/button'\nimport { Slider } from '@/components/ui/slider'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport { Timecode } from '@/components/common/Timecode'\nimport { cn } from '@/lib/cn'\n\ninterface PlaybackControlsProps {\n playing: boolean\n currentUs: number\n durationUs: number\n masterVolume: number\n playbackRate: number\n disabled?: boolean\n onToggle: () => void\n onJumpStart: () => void\n onJumpEnd: () => void\n onStepFrame: (direction: -1 | 1) => void\n onSeek: (us: number) => void\n onVolumeChange: (volume: number) => void\n onRateChange: (rate: number) => void\n className?: string\n}\n\nconst RATES = [0.5, 1, 1.5, 2]\n\n/**\n * 紧凑档位(按容器宽度,不是视口 —— 预览列宽由拖拽决定)。\n *\n * 阈值来自实测的控件总宽:完整形态约 680px,`< 700` 先去音量滑杆与速率、\n * `< 560` 再去时间码的「总时长」与进度条、`< 420` 再去跳首尾。\n * 只靠 `shrink-0` + `overflow:hidden` 会在 650px 左右把时间码裁掉,\n * 且**没有任何报错**,只有截图能看出来 —— 所以这里显式分级。\n */\nconst FULL_PX = 700\nconst COMPACT_PX = 620\nconst TIGHT_PX = 520\nconst ULTRA_COMPACT_PX = 420\n/** 低于此宽度连时间码都放不下:只留播放/逐帧,横向可滚 */\nconst MICRO_PX = 200\n\n/** 播放控制条:逐帧 / 跳首尾 / 播放 / 时间码 / 速率 / 音量 */\nexport function PlaybackControls({\n playing,\n currentUs,\n durationUs,\n masterVolume,\n playbackRate,\n disabled,\n onToggle,\n onJumpStart,\n onJumpEnd,\n onStepFrame,\n onSeek,\n onVolumeChange,\n onRateChange,\n className,\n}: PlaybackControlsProps) {\n const rootRef = useRef<HTMLDivElement | null>(null)\n const [width, setWidth] = useState(0)\n\n /**\n * 用容器宽度而不是 CSS 媒体查询。\n *\n * 预览面板宽度由左侧拖拽决定,和视口宽度无关 —— 用户把两侧拉宽时,\n * 视口一点没变,`max-[560px]:hidden` 这类媒体查询不会触发,时间码照样被压扁。\n */\n useEffect(() => {\n const el = rootRef.current\n if (el == null) return\n const ro = new ResizeObserver(() => setWidth(el.clientWidth))\n ro.observe(el)\n setWidth(el.clientWidth)\n return () => ro.disconnect()\n }, [])\n\n const compact = width > 0 && width < COMPACT_PX\n const tight = width > 0 && width < TIGHT_PX\n const ultraCompact = width > 0 && width < ULTRA_COMPACT_PX\n const micro = width > 0 && width < MICRO_PX\n const full = width === 0 || width >= FULL_PX\n\n return (\n <div\n ref={rootRef}\n className={cn(\n 'flex h-11 shrink-0 items-center gap-1.5 overflow-hidden border-t bg-card/60',\n ultraCompact ? 'px-1.5' : 'px-3',\n className,\n )}\n >\n <div className=\"flex shrink-0 items-center gap-0.5\">\n {/* 跳首尾在窄列下隐藏:它们能用 Home / End 或拖时间轴完成,\n 而播放与逐帧是高频操作,必须留住 */}\n {!ultraCompact && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onJumpStart} disabled={disabled} aria-label=\"跳到开头\">\n <SkipBack className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>跳到开头 (Home)</TooltipContent>\n </Tooltip>\n )}\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => onStepFrame(-1)}\n disabled={disabled}\n aria-label=\"上一帧\"\n >\n <StepBack className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>上一帧 (←)</TooltipContent>\n </Tooltip>\n\n <Button\n variant=\"secondary\"\n size=\"icon\"\n onClick={onToggle}\n disabled={disabled}\n aria-label={playing ? '暂停' : '播放'}\n className=\"mx-0.5\"\n >\n {playing ? <Pause className=\"size-4\" /> : <Play className=\"size-4\" />}\n </Button>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => onStepFrame(1)}\n disabled={disabled}\n aria-label=\"下一帧\"\n >\n <StepForward className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>下一帧 (→)</TooltipContent>\n </Tooltip>\n {!ultraCompact && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onJumpEnd} disabled={disabled} aria-label=\"跳到结尾\">\n <SkipForward className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>跳到结尾 (End)</TooltipContent>\n </Tooltip>\n )}\n </div>\n\n <div\n className={cn(\n 'mx-2 flex shrink-0 items-baseline gap-1 whitespace-nowrap',\n ultraCompact && 'mx-1',\n micro && 'hidden',\n )}\n >\n <Timecode us={currentUs} className=\"text-[13px] font-medium\" />\n {/* 紧凑档只留当前时间:总时长在时间轴刻度尺上也能看到 */}\n {!tight && (\n <>\n <span className=\"text-xs text-muted-foreground\">/</span>\n <Timecode us={durationUs} muted />\n </>\n )}\n </div>\n\n {!ultraCompact && (\n <input\n type=\"range\"\n className=\"mx-1 h-1 min-w-16 flex-1 cursor-pointer appearance-none rounded-full bg-muted accent-primary\"\n min={0}\n max={Math.max(1, durationUs)}\n step={1000}\n value={Math.min(currentUs, durationUs)}\n onChange={(e) => onSeek(Number(e.target.value))}\n disabled={disabled}\n aria-label=\"播放进度\"\n />\n )}\n\n {/* 右侧附加控件:窄列下逐级隐藏,而不是把时间码挤到只剩一半 */}\n <div className={cn('flex shrink-0 items-center gap-1', compact && 'ml-auto', micro && 'hidden')}>\n {!compact && (\n <div className=\"flex items-center rounded-md border p-0.5\">\n {RATES.map((rate) => (\n <button\n key={rate}\n type=\"button\"\n onClick={() => onRateChange(rate)}\n className={cn(\n 'rounded px-1.5 py-0.5 text-[11px] tabular-nums transition-colors',\n playbackRate === rate\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:text-foreground',\n )}\n title={rate !== 1 ? '变速预览(不会改变导出结果)' : '正常速度'}\n >\n {rate}×\n </button>\n ))}\n </div>\n )}\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => onVolumeChange(masterVolume > 0 ? 0 : 1)}\n aria-label=\"静音\"\n >\n {masterVolume > 0 ? <Volume2 className=\"size-3.5\" /> : <VolumeX className=\"size-3.5\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>{masterVolume > 0 ? '静音预览' : '取消静音'}</TooltipContent>\n </Tooltip>\n {/* 音量滑杆只在最宽档出现:它最占地方,且静音按钮已能表达「有没有声」 */}\n {full && (\n <Slider\n className=\"w-20\"\n min={0}\n max={1}\n step={0.01}\n value={[masterVolume]}\n onValueChange={([v]) => onVolumeChange(v ?? 0)}\n aria-label=\"预览音量\"\n />\n )}\n </div>\n </div>\n )\n}\n","/** 下载 data URL(截图) */\nexport function downloadDataUrl(dataUrl: string, filename: string): void {\n const link = document.createElement('a')\n link.href = dataUrl\n link.download = filename\n document.body.appendChild(link)\n link.click()\n link.remove()\n}\n","import { useCallback, useEffect, useRef, useMemo } from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { Film, Loader2 } from 'lucide-react'\r\nimport { notify } from '@/lib/notify'\r\nimport { useWorkbench } from '@/context'\r\nimport type { ClipTransform } from '@cut-workbench/core'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { reportError } from '@/hooks/use-toast-safe'\r\nimport { useAvCanvas } from '../hooks/useAvCanvas'\r\nimport { usePreviewSync } from '../hooks/usePreviewSync'\r\nimport { usePlaybackClock } from '../hooks/usePlaybackClock'\r\nimport { useCanvasInteraction } from '../hooks/useCanvasInteraction'\r\nimport { PreviewStage } from './PreviewStage'\r\nimport { PlaybackControls } from './PlaybackControls'\r\nimport { downloadDataUrl } from '../lib/download'\r\n\r\nconst FRAME_US = 1_000_000 / 30\r\n/** 播放头写 store 的最小间隔(微秒):避免 60fps 全量重渲染 */\r\nconst PLAYHEAD_WRITE_STEP_US = 10_000\r\n\r\n/**\r\n * 预览面板:画布 + 播放时钟 + sprite 同步。\r\n *\r\n * 播放路径:PlaybackClock(AudioContext) → rAF → previewFrame;音频独立排程。\r\n * 拖拽播放头:暂停音频 → 松手后重排 + previewFrame。\r\n *\r\n * 播放控制通过全局自定义事件与快捷键 / 时间轴解耦:\r\n * - `cut-test:toggle-play`\r\n * - `cut-test:step-frame` {direction}\r\n * - `cut-test:seek` {us}\r\n * - `cut-test:scrub-start` / `cut-test:scrub-end`\r\n * - `cut-test:add-asset-to-timeline` {assetId} → 转发 `cut-test:add-asset-request`\r\n */\r\nexport function PreviewPanel() {\r\n const editor = useCutEditor()\r\n const project = useEditor((s) => s.project)\r\n const assets = useEditor((s) => s.assets)\r\n const masterVolume = useEditor((s) => s.masterVolume)\r\n const setMasterVolume = useEditor((s) => s.setMasterVolume)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n const selectedClipIds = useEditor((s) => s.selectedClipIds)\r\n const { bridge } = useWorkbench()\r\n\r\n const canvas = useAvCanvas({ width: project.width, height: project.height })\r\n const previewFrame = canvas.previewFrame\r\n const lastWriteRef = useRef(0)\r\n\r\n const handleSyncErrors = useCallback((errors: Array<{ clipId: string; message: string }>) => {\r\n if (errors.length === 0) return\r\n // 同一批错误合并成一条提示:刷新恢复的工程可能有几十个 clip 都缺文件\r\n const missing = errors.filter((e) => e.message.startsWith('素材文件未载入'))\r\n const other = errors.filter((e) => !e.message.startsWith('素材文件未载入'))\r\n if (missing.length > 0) {\r\n notify(bridge, {\r\n level: 'warning',\r\n title: `${missing.length} 个片段缺少源文件,已跳过预览`,\r\n description: '刷新后素材二进制不会保留,请重新导入相同文件',\r\n duration: 8000,\r\n })\r\n }\r\n if (other.length > 0) {\r\n notify(bridge, {\r\n level: 'error',\r\n title: '部分片段无法载入预览',\r\n description: Array.from(new Set(other.map((e) => e.message))).slice(0, 3).join('\\n'),\r\n })\r\n }\r\n }, [bridge])\r\n\r\n const onFrame = useCallback(\r\n (us: number) => {\r\n void previewFrame(us)\r\n if (Math.abs(us - lastWriteRef.current) >= PLAYHEAD_WRITE_STEP_US) {\r\n lastWriteRef.current = us\r\n editor.store.getState().setPlayhead(us)\r\n }\r\n },\r\n [previewFrame],\r\n )\r\n\r\n const handleEnded = useCallback(() => {\r\n const total = editor.store.getState().project.clips.reduce(\r\n (max, c) => Math.max(max, c.startUs + c.durationUs),\r\n 0,\r\n )\r\n editor.store.getState().setPlayhead(total)\r\n }, [])\r\n\r\n const clock = usePlaybackClock({\r\n project,\r\n assets,\r\n masterVolume,\r\n onFrame,\r\n onEnded: handleEnded,\r\n })\r\n\r\n const clockRef = useRef(clock)\r\n useEffect(() => {\r\n clockRef.current = clock\r\n })\r\n\r\n const { refresh } = canvas\r\n usePreviewSync({ project, assets, canvas, refresh, onSyncErrors: handleSyncErrors })\r\n\r\n // 时间轴 / 属性面板的选中 → 画布选框\r\n const onlySelected = selectedClipIds.length === 1 ? (selectedClipIds[0] ?? null) : null\r\n const selectedClip = useMemo(\r\n () => (onlySelected == null ? null : (project.clips.find((c) => c.id === onlySelected) ?? null)),\r\n [onlySelected, project.clips],\r\n )\r\n const selectedAsset = useMemo(\r\n () => (selectedClip == null ? undefined : assets.find((a) => a.id === selectedClip.assetId)),\r\n [selectedClip, assets],\r\n )\r\n const selectionTransform: ClipTransform | null = selectedClip?.transform ?? null\r\n /**\r\n * 该片段此刻是否真的在画面上。\r\n *\r\n * 播放头离开片段区间后,AVCanvas 不再绘制它(时间可见性),但 sprite 本身仍\r\n * 存在且 `visible = true`(那个字段只跟随「轨道隐藏」)。因此这里必须自己判时间,\r\n * 否则用户能在空白处拖到「已经不在画面上」的素材。\r\n */\r\n const selectionInRange =\r\n selectedClip != null &&\r\n playheadUs >= selectedClip.startUs &&\r\n playheadUs <= selectedClip.startUs + selectedClip.durationUs\r\n /**\r\n * 纯音频片段没有画面(sprite 尺寸为 0、transform 只是「铺满画布」的占位值),\r\n * 若照常画选框会出现一个覆盖整个画布、却拖不动的框,反而误导用户。\r\n */\r\n const selectionVisible = selectedAsset != null && selectedAsset.kind !== 'audio'\r\n /** 是否允许在画布内拖动 / 缩放:必须同时「有画面」且「此刻在画面上」 */\r\n const selectionInteractive = selectionVisible && selectionInRange\r\n\r\n const hitTest = useCallback(\r\n (point: { x: number; y: number }) => {\r\n // 时间取播放头:不在当前画面上的片段不能被点中(否则会拖到看不见的素材)\r\n const state = editor.store.getState()\r\n return canvas.hitTest(point, state.project, state.playheadUs)\r\n },\r\n [canvas],\r\n )\r\n\r\n const handleCanvasSelect = useCallback((clipId: string | null) => {\r\n const state = editor.store.getState()\r\n if (clipId == null) {\r\n if (state.selectedClipIds.length > 0) state.selectClips([])\r\n return\r\n }\r\n if (!state.selectedClipIds.includes(clipId)) state.selectClips([clipId])\r\n }, [])\r\n\r\n /** 画布内一次变换结束:只在这里写 store(拖动期间不写) */\r\n const handleCanvasCommit = useCallback((clipId: string, transform: ClipTransform) => {\r\n editor.store\r\n .getState()\r\n .updateClip(clipId, { transform }, true, `canvas:${clipId}`)\r\n }, [])\r\n\r\n const interaction = useCanvasInteraction({\r\n getCanvas: canvas.getCanvas,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n hostRef: canvas.containerRef,\r\n selectedClipId: selectedClip?.id ?? null,\r\n transform: selectionTransform,\r\n /** 播放头不在片段区间内时,不允许在画布内拖动 / 缩放 / 旋转 */\r\n interactive: selectionInteractive,\r\n aspectLocked: selectedClip?.aspectLocked === true,\r\n hitTest,\r\n onSelect: handleCanvasSelect,\r\n onCommit: handleCanvasCommit,\r\n })\r\n\r\n // 拖动中优先用实时几何绘制选框,松手后回落到 store 值\r\n // 播放头离开片段后仍显示选框(提示「它被选中了」),只是不给手柄、不可拖\r\n const overlayTransform =\r\n selectionVisible ? (interaction.liveTransform ?? selectionTransform) : null\r\n\r\n // 首帧:画布就绪后渲染当前播放头\r\n useEffect(() => {\r\n if (!canvas.ready) return\r\n const timer = window.setTimeout(() => {\r\n void refresh(editor.store.getState().playheadUs)\r\n }, 80)\r\n return () => window.clearTimeout(timer)\r\n }, [canvas.ready, refresh])\r\n\r\n const handleSeek = useCallback((us: number) => {\r\n clockRef.current.seek(us)\r\n editor.store.getState().setPlayhead(us)\r\n }, [])\r\n\r\n const handleStepFrame = useCallback((direction: -1 | 1) => {\r\n const current = clockRef.current\r\n current.pause()\r\n const from = editor.store.getState().playheadUs\r\n const next = Math.max(0, Math.min(current.durationUs, from + direction * FRAME_US))\r\n current.seek(next)\r\n editor.store.getState().setPlayhead(next)\r\n }, [])\r\n\r\n // 全局事件桥接\r\n useEffect(() => {\r\n const onToggle = () => clockRef.current.toggle()\r\n const onStep = (event: Event) => {\r\n const detail = (event as CustomEvent<{ direction: -1 | 1 }>).detail\r\n handleStepFrame(detail?.direction ?? 1)\r\n }\r\n const onSeek = (event: Event) => {\r\n const detail = (event as CustomEvent<{ us: number }>).detail\r\n if (detail?.us != null) handleSeek(detail.us)\r\n }\r\n const onScrubStart = () => clockRef.current.beginScrub()\r\n const onScrubEnd = () => {\r\n const us = editor.store.getState().playheadUs\r\n clockRef.current.endScrub()\r\n clockRef.current.seek(us)\r\n }\r\n const onAddAsset = (event: Event) => {\r\n const detail = (event as CustomEvent<{ assetId: string }>).detail\r\n if (detail?.assetId) {\r\n window.dispatchEvent(\r\n new CustomEvent('cut-test:add-asset-request', { detail: { assetId: detail.assetId } }),\r\n )\r\n }\r\n }\r\n\r\n window.addEventListener('cut-test:toggle-play', onToggle)\r\n window.addEventListener('cut-test:step-frame', onStep)\r\n window.addEventListener('cut-test:seek', onSeek)\r\n window.addEventListener('cut-test:scrub-start', onScrubStart)\r\n window.addEventListener('cut-test:scrub-end', onScrubEnd)\r\n window.addEventListener('cut-test:add-asset-to-timeline', onAddAsset)\r\n return () => {\r\n window.removeEventListener('cut-test:toggle-play', onToggle)\r\n window.removeEventListener('cut-test:step-frame', onStep)\r\n window.removeEventListener('cut-test:seek', onSeek)\r\n window.removeEventListener('cut-test:scrub-start', onScrubStart)\r\n window.removeEventListener('cut-test:scrub-end', onScrubEnd)\r\n window.removeEventListener('cut-test:add-asset-to-timeline', onAddAsset)\r\n }\r\n }, [handleSeek, handleStepFrame])\r\n\r\n const handleCapture = useCallback(() => {\r\n const dataUrl = canvas.captureImage()\r\n if (dataUrl == null) {\r\n notify(bridge, { level: 'error', title: '截图失败:画布尚未就绪' })\r\n return\r\n }\r\n downloadDataUrl(dataUrl, `frame-${Date.now()}.png`)\r\n notify(bridge, { level: 'success', title: '已导出当前帧' })\r\n }, [bridge, canvas])\r\n\r\n // 全屏直接读 DOM,无需 memo(React Compiler 无法保存含 ref 读取的 useCallback)\r\n const handleFullscreen = () => {\r\n const el = canvas.containerRef.current?.parentElement\r\n if (el == null) return\r\n if (document.fullscreenElement != null) {\r\n void document.exitFullscreen()\r\n } else {\r\n void el.requestFullscreen().catch((err: unknown) => reportError(err, '全屏失败'))\r\n }\r\n }\r\n\r\n const emptyHint = useMemo(() => {\r\n if (assets.length === 0) {\r\n return (\r\n <EmptyState\r\n icon={Film}\r\n title=\"还没有素材\"\r\n description=\"把 MP4、音频、图片或 SRT 拖到左侧素材库\"\r\n // 提示文字叠在深色舞台底上,用的是舞台内的前景色而非主题前景色\r\n className=\"text-muted-foreground\"\r\n />\r\n )\r\n }\r\n if (project.clips.length === 0) {\r\n return (\r\n <EmptyState\r\n icon={Film}\r\n title=\"时间轴为空\"\r\n description=\"双击左侧素材卡片,把它加入时间轴\"\r\n className=\"text-muted-foreground\"\r\n />\r\n )\r\n }\r\n return null\r\n }, [assets.length, project.clips.length])\r\n\r\n return (\r\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\r\n <PreviewStage\r\n attachRef={canvas.attachRef}\r\n width={project.width}\r\n height={project.height}\r\n emptyHint={emptyHint}\r\n onCapture={handleCapture}\r\n onFullscreen={handleFullscreen}\r\n selection={overlayTransform}\r\n selectionInteractive={selectionInteractive}\r\n interaction={interaction.interaction}\r\n onPointerDown={interaction.onPointerDown}\r\n />\r\n <PlaybackControls\r\n playing={clock.playing}\r\n currentUs={playheadUs}\r\n durationUs={clock.durationUs}\r\n masterVolume={masterVolume}\r\n playbackRate={clock.playbackRate}\r\n onToggle={clock.toggle}\r\n onJumpStart={() => handleSeek(0)}\r\n onJumpEnd={() => handleSeek(clock.durationUs)}\r\n onStepFrame={handleStepFrame}\r\n onSeek={handleSeek}\r\n onVolumeChange={setMasterVolume}\r\n onRateChange={clock.setPlaybackRate}\r\n />\r\n {!canvas.ready && (\r\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\r\n <span className=\"flex items-center gap-2 rounded-md bg-background/80 px-3 py-1.5 text-xs text-muted-foreground\">\r\n <Loader2 className=\"size-3 animate-spin\" />\r\n 画布初始化…\r\n </span>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\n * 应用侧量化常量。集中管理,避免散落魔法数字。\n *\n * 与核心逻辑无关的常量(缩放档位、面板尺寸、吸附像素阈值)留在这里;\n * 逻辑层必须知道的值(默认时长、默认音量、schema 版本、撤销栈深度)\n * 由 `@cut-workbench/core` 提供,这里只做转发,避免两处定义漂移。\n */\n\nexport {\n DEFAULT_STILL_DURATION_US,\n DEFAULT_CLIP_VOLUME,\n PROJECT_SCHEMA_VERSION,\n MAX_HISTORY_STEPS,\n HISTORY_MERGE_WINDOW_MS,\n} from '@cut-workbench/core'\n\n/** 时间轴横向缩放档位(每秒像素) */\nexport const ZOOM_LEVELS = [10, 20, 40, 80, 120, 200, 320, 500] as const\n\nexport const DEFAULT_PX_PER_SECOND = 80\nexport const MIN_PX_PER_SECOND = 10\nexport const MAX_PX_PER_SECOND = 500\n\n/** 吸附阈值(像素) */\nexport const SNAP_THRESHOLD_PX = 15\n/**\n * 已吸附后脱离的滞后阈值(像素)。\n *\n * 必须 **大于** SNAP_THRESHOLD_PX,否则「先吸附、再多走一点才脱离」在数学上不成立:\n * 旧值 8px 小于吸附阈值 15px,导致滞后分支永远走不到,吸附在窗口边缘反复跳动。\n */\nexport const SNAP_RELEASE_PX = 26\n\n/** 轨道行高(像素) */\nexport const TRACK_HEIGHT_PX = 56\nexport const RULER_HEIGHT_PX = 28\nexport const TRACK_HEADER_WIDTH_PX = 150\n\n/** 播放器每帧步进(微秒),仅用于 UI 刷新节流 */\nexport const FRAME_INTERVAL_US = 16_666\n\n/** 导出默认码率 */\nexport const DEFAULT_BITRATE = 8_000_000\n\n/** IndexedDB 数据库(应用默认,库默认值为 `cut-workbench`) */\nexport const DB_NAME = 'cut-workbench'\nexport const DB_VERSION = 1\n","import {\n Copy,\n Info,\n Magnet,\n Plus,\n Scissors,\n Trash2,\n ZoomIn,\n ZoomOut,\n} from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport { Separator } from '@/components/ui/separator'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport { Timecode } from '@/components/common/Timecode'\n\ninterface TimelineToolbarProps {\n playheadUs: number\n durationUs: number\n pxPerSecond: number\n selectionCount: number\n magnetEnabled: boolean\n onToggleMagnet: () => void\n onSplit: () => void\n onDuplicate: () => void\n onRemove: () => void\n onZoomIn: () => void\n onZoomOut: () => void\n onZoomToFit: () => void\n onAddTrack: (kind: 'visual' | 'audio') => void\n canZoomIn: boolean\n canZoomOut: boolean\n}\n\n/** 时间轴顶部工具条:分割 / 复制 / 删除 / 吸附开关 / 缩放 / 加轨道 */\nexport function TimelineToolbar({\n playheadUs,\n durationUs,\n pxPerSecond,\n selectionCount,\n magnetEnabled,\n onToggleMagnet,\n onSplit,\n onDuplicate,\n onRemove,\n onZoomIn,\n onZoomOut,\n onZoomToFit,\n onAddTrack,\n canZoomIn,\n canZoomOut,\n}: TimelineToolbarProps) {\n return (\n <div\n className=\"thin-scrollbar @container flex h-9 shrink-0 items-center gap-1 overflow-x-auto overflow-y-hidden border-b bg-card/60 px-2\"\n role=\"toolbar\"\n aria-label=\"时间轴工具\"\n data-timeline-toolbar\n >\n {/* 操作组:不允许压缩,但整条工具条可横向滚动 */}\n <div className=\"flex shrink-0 items-center gap-1\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onSplit} aria-label=\"分割\">\n <Scissors className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>在播放头分割 (S)</TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onDuplicate}\n disabled={selectionCount === 0}\n aria-label=\"复制片段\"\n >\n <Copy className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>复制片段 (Ctrl+D)</TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onRemove}\n disabled={selectionCount === 0}\n aria-label=\"删除片段\"\n >\n <Trash2 className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>删除片段 (Delete)</TooltipContent>\n </Tooltip>\n\n <Separator orientation=\"vertical\" className=\"mx-1 h-4\" />\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant={magnetEnabled ? 'secondary' : 'ghost'}\n size=\"icon-sm\"\n onClick={onToggleMagnet}\n aria-label=\"吸附\"\n aria-pressed={magnetEnabled}\n >\n <Magnet className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>吸附对齐(阈值 15px)</TooltipContent>\n </Tooltip>\n\n <Separator orientation=\"vertical\" className=\"mx-1 h-4\" />\n\n <div className=\"flex items-center gap-0.5\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onZoomOut}\n disabled={!canZoomOut}\n aria-label=\"缩小\"\n >\n <ZoomOut className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>缩小 (-)</TooltipContent>\n </Tooltip>\n <span className=\"w-11 text-center font-mono text-[10px] text-muted-foreground tabular-nums\">\n {Math.round(pxPerSecond)}\n </span>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onZoomIn}\n disabled={!canZoomIn}\n aria-label=\"放大\"\n >\n <ZoomIn className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>放大 (+)</TooltipContent>\n </Tooltip>\n <Button variant=\"ghost\" size=\"xs\" onClick={onZoomToFit} className=\"text-[10px]\">\n 适应\n </Button>\n </div>\n\n <Separator orientation=\"vertical\" className=\"mx-1 h-4\" />\n\n <div className=\"flex items-center gap-0.5\">\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onAddTrack('visual')}>\n <Plus className=\"size-3\" />\n 视频轨\n </Button>\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onAddTrack('audio')}>\n <Plus className=\"size-3\" />\n 音频轨\n </Button>\n </div>\n </div>\n\n {/* 状态组:容器变窄时隐藏,而不是被挤出容器压到别的面板上。\n 用容器查询(工具条自身是 @container)而不是媒体查询:\n 时间轴宽度取决于左右面板的拖拽,与视口宽度无关。 */}\n <div className=\"ml-auto hidden shrink-0 items-center gap-2 pl-2 text-[11px] text-muted-foreground @[720px]:flex\">\n {selectionCount > 0 && (\n <span className=\"rounded bg-primary/15 px-1.5 py-0.5 text-primary\">\n 已选 {selectionCount}\n </span>\n )}\n <Info className=\"size-3\" />\n <span className=\"tabular-nums whitespace-nowrap\">\n <Timecode us={playheadUs} className=\"text-foreground\" />\n <span className=\"mx-1\">/</span>\n <Timecode us={durationUs} muted />\n </span>\n </div>\n </div>\n )\n}\n","import { useMemo } from 'react'\nimport { buildTicks, formatTimecode } from '@cut-workbench/core'\nimport { RULER_HEIGHT_PX } from '@/lib/constants'\nimport { cn } from '@/lib/cn'\n\ninterface RulerProps {\n pxPerSecond: number\n /** 内容总宽(像素) */\n widthPx: number\n /** 播放头像素位置(仅用于视觉标记,不做拖拽) */\n playheadPx: number\n /** 当前横向滚动(用于只渲染可见刻度) */\n scrollLeftPx: number\n className?: string\n}\n\n/**\n * 刻度尺。自适应刻度密度(1s / 5s / 10s / 1min…),由 buildTicks 决定。\n *\n * 指针交互由父容器(Timeline 的 sticky 头部)统一处理,因此这里只负责绘制,\n * 避免嵌套的 pointer 事件处理。\n */\nexport function Ruler({\n pxPerSecond,\n widthPx,\n playheadPx,\n scrollLeftPx,\n className,\n}: RulerProps) {\n const ticks = useMemo(\n () => buildTicks(pxPerSecond, Math.max(widthPx, 1), scrollLeftPx),\n [pxPerSecond, scrollLeftPx, widthPx],\n )\n\n return (\n <div\n className={cn('relative shrink-0 cursor-ew-resize border-b bg-timeline-bg select-none', className)}\n style={{ height: RULER_HEIGHT_PX, width: widthPx }}\n >\n {ticks.map((tick) => (\n <div\n key={`${tick.us}-${tick.major ? 'M' : 'm'}`}\n className={cn(\n 'absolute bottom-0 w-px',\n tick.major ? 'h-2 bg-muted-foreground/50' : 'h-1 bg-muted-foreground/25',\n )}\n style={{ left: tick.px }}\n />\n ))}\n {ticks\n .filter((t) => t.major)\n .map((tick) => (\n <span\n key={`label-${tick.us}`}\n className=\"absolute top-1 font-mono text-[10px] text-muted-foreground tabular-nums\"\n style={{ left: tick.px + 3 }}\n >\n {formatTimecode(tick.us, false)}\n </span>\n ))}\n\n <div\n className=\"pointer-events-none absolute top-0 z-10 -translate-x-1/2\"\n style={{ left: playheadPx }}\n >\n <div className=\"h-2 w-3 rounded-b-sm bg-playhead\" />\n </div>\n </div>\n )\n}\n","import { memo } from 'react'\r\nimport { Eye, EyeOff, Lock, LockOpen, Volume2, VolumeX, X } from 'lucide-react'\r\nimport type { Track } from '@cut-workbench/core'\r\nimport { cn } from '@/lib/cn'\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\r\nimport { TRACK_HEIGHT_PX } from '@/lib/constants'\r\n\r\nexport interface TrackHeaderProps {\r\n track: Track\r\n onToggleMute: () => void\r\n onToggleLock: () => void\r\n onToggleHidden: () => void\r\n onRemove: () => void\r\n}\r\n\r\n/**\r\n * 轨道头:名称 + 静音 / 锁定 / 隐藏 / 删除。\r\n *\r\n * 使用绝对定位叠在轨道内容之上(时间轴左列),因此宽度固定、不随横向滚动。\r\n *\r\n * 宽度预算(TRACK_HEADER_WIDTH_PX = 132):左右内边距 8+8、gap 4、图标区 4×18。\r\n * 之前图标按钮是 size-5(20px),文字区只剩 24px,「视频 1」被截成「视…」。\r\n * 缩到 18px 后文字区约 40px,中文轨名可以完整显示。\r\n */\r\nexport const TrackHeader = memo(function TrackHeader({\r\n track,\r\n onToggleMute,\r\n onToggleLock,\r\n onToggleHidden,\r\n onRemove,\r\n}: TrackHeaderProps) {\r\n return (\r\n <div\r\n className={cn(\r\n 'flex items-center gap-1 border-r border-b border-timeline-grid px-2',\r\n track.kind === 'audio' ? 'bg-clip-audio/10' : 'bg-clip-video/10',\r\n track.hidden && 'opacity-60',\r\n )}\r\n style={{ height: TRACK_HEIGHT_PX }}\r\n data-track-header\r\n >\r\n <div className=\"min-w-0 flex-1\">\r\n <p className=\"truncate text-[11px] font-medium\">{track.name}</p>\r\n <p className=\"truncate text-[10px] text-muted-foreground\">\r\n {track.kind === 'audio' ? '音频轨' : '视觉轨'}\r\n </p>\r\n </div>\r\n <div className=\"flex shrink-0 items-center gap-px\">\r\n <IconToggle\r\n active={track.muted}\r\n onClick={onToggleMute}\r\n label={track.muted ? '取消静音' : '静音轨道'}\r\n icon={track.muted ? VolumeX : Volume2}\r\n />\r\n <IconToggle\r\n active={track.locked}\r\n onClick={onToggleLock}\r\n label={track.locked ? '解锁轨道' : '锁定轨道'}\r\n icon={track.locked ? Lock : LockOpen}\r\n />\r\n <IconToggle\r\n active={track.hidden}\r\n onClick={onToggleHidden}\r\n label={track.hidden ? '显示轨道' : '隐藏轨道'}\r\n icon={track.hidden ? EyeOff : Eye}\r\n />\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <button\r\n type=\"button\"\r\n onClick={onRemove}\r\n aria-label=\"删除轨道\"\r\n className=\"flex size-[18px] items-center justify-center rounded text-muted-foreground transition-colors hover:text-destructive\"\r\n >\r\n <X className=\"size-3\" />\r\n </button>\r\n </TooltipTrigger>\r\n <TooltipContent>删除轨道</TooltipContent>\r\n </Tooltip>\r\n </div>\r\n </div>\r\n )\r\n})\r\n\r\nfunction IconToggle({\r\n active,\r\n onClick,\r\n label,\r\n icon: Icon,\r\n}: {\r\n active: boolean\r\n onClick: () => void\r\n label: string\r\n icon: typeof Eye\r\n}) {\r\n return (\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <button\r\n type=\"button\"\r\n onClick={onClick}\r\n aria-label={label}\r\n aria-pressed={active}\r\n className={cn(\r\n 'flex size-[18px] items-center justify-center rounded transition-colors',\r\n active ? 'bg-primary/20 text-primary' : 'text-muted-foreground hover:text-foreground',\r\n )}\r\n >\r\n <Icon className=\"size-3\" />\r\n </button>\r\n </TooltipTrigger>\r\n <TooltipContent>{label}</TooltipContent>\r\n </Tooltip>\r\n )\r\n}\r\n","import { useEffect, useRef, useState } from 'react'\n\n/**\n * 懒加载可见性:元素进入视口才置 true(只切一次,不来回抖动)。\n *\n * 用于时间轴上大量 clip 的缩略图/波形渲染:200 个 clip 同时解码缩略图会很卡。\n *\n * IntersectionObserver 不可用时用「首帧后置可见」的异步方式降级(不在 effect 里同步 setState)。\n */\nexport function useLazyVisibility<T extends HTMLElement>(): {\n ref: React.RefObject<T | null>\n visible: boolean\n} {\n const ref = useRef<T | null>(null)\n const [visible, setVisible] = useState(false)\n\n useEffect(() => {\n if (visible) return\n const el = ref.current\n if (el == null) return\n\n if (typeof IntersectionObserver === 'undefined') {\n const timer = window.setTimeout(() => setVisible(true), 0)\n return () => window.clearTimeout(timer)\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((e) => e.isIntersecting)) {\n setVisible(true)\n observer.disconnect()\n }\n },\n { rootMargin: '200px' },\n )\n observer.observe(el)\n return () => observer.disconnect()\n }, [visible])\n\n return { ref, visible }\n}\n","import { memo, useMemo } from \"react\";\nimport type { MediaAsset, TimelineClip, TrackKind } from \"@cut-workbench/core\";\nimport { formatDuration } from \"@/lib/format\";\nimport { usToPx } from \"@cut-workbench/core\";\nimport { buildWaveformPath } from \"@cut-workbench/core\";\nimport { cn } from \"@/lib/cn\";\nimport { MIN_CLIP_WIDTH_PX, TRIM_HANDLE_PX } from \"../constants\";\nimport { useLazyVisibility } from \"../hooks/useLazyVisibility\";\n\n/**\n * 一组稳定的 clip 交互回调。\n *\n * 为什么单独抽出来:Timeline 每次拖拽预览都会重新渲染,如果给每个 ClipBlock 传内联箭头函数,\n * `memo` 会全部失效(200 个 clip 一起重渲染,拖拽直接卡死)。\n * 把回调做成「接收 clip 参数」的稳定对象后,其余 clip 的 props 不变,memo 生效。\n */\nexport interface ClipHandlers {\n beginMove: (event: React.PointerEvent, clip: TimelineClip) => void;\n beginTrimStart: (event: React.PointerEvent, clip: TimelineClip) => void;\n beginTrimEnd: (event: React.PointerEvent, clip: TimelineClip) => void;\n select: (clip: TimelineClip, additive: boolean) => void;\n split: (clip: TimelineClip) => void;\n duplicate: (clip: TimelineClip) => void;\n remove: (clip: TimelineClip) => void;\n showInfo: (clip: TimelineClip) => void;\n seekTo: (clip: TimelineClip) => void;\n selectOnly: (clip: TimelineClip) => void;\n}\n\nexport interface ClipBlockProps {\n clip: TimelineClip;\n asset: MediaAsset | undefined;\n pxPerSecond: number;\n selected: boolean;\n locked: boolean;\n trackKind: TrackKind;\n /** 拖拽中的临时位移(像素),不写入 store */\n previewOffsetPx?: number;\n previewWidthPx?: number;\n handlers: ClipHandlers;\n}\n\nconst KIND_BG: Record<MediaAsset[\"kind\"], string> = {\n video: \"bg-clip-video/35 border-clip-video/60\",\n audio: \"bg-clip-audio/35 border-clip-audio/60\",\n image: \"bg-clip-image/35 border-clip-image/60\",\n text: \"bg-clip-text/35 border-clip-text/60\",\n subtitle: \"bg-clip-subtitle/35 border-clip-subtitle/60\",\n};\n\n/**\n * 单个 clip 块。\n *\n * 渲染策略:\n * - 视频背景用多张缩略图按百分比平铺成一条缩略图带(CSS background 多值),不建 DOM 图;\n * - 音频用 SVG 波形,且只在元素进入视口后才生成路径(IntersectionObserver 懒加载);\n * - 拖拽位移用 transform,不写 store;\n * - 全 memo 化 + 稳定回调(见 ClipHandlers),200 clip 拖拽仍流畅。\n */\nexport const ClipBlock = memo(function ClipBlock({\n clip,\n asset,\n pxPerSecond,\n selected,\n locked,\n trackKind,\n previewOffsetPx = 0,\n previewWidthPx,\n handlers,\n}: ClipBlockProps) {\n const { ref, visible } = useLazyVisibility<HTMLDivElement>();\n const leftPx = usToPx(clip.startUs, pxPerSecond);\n const widthPx = Math.max(\n MIN_CLIP_WIDTH_PX,\n previewWidthPx ?? usToPx(clip.durationUs, pxPerSecond),\n );\n const kind = asset?.kind ?? (trackKind === \"audio\" ? \"audio\" : \"video\");\n const tooNarrow = widthPx < 56;\n const urls = asset?.thumbnailUrls;\n const thumbnailKey = urls?.join(\"|\") ?? \"\";\n\n const background = useMemo(() => {\n if (\n asset?.kind !== \"video\" ||\n urls == null ||\n urls.length === 0 ||\n !visible\n )\n return undefined;\n const share = 100 / urls.length;\n return urls\n .map(\n (url, index) =>\n `url(\"${url}\") ${index * share}% 50% / ${share}% 100% no-repeat`,\n )\n .join(\", \");\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [asset?.kind, thumbnailKey, visible]);\n\n const waveformPath = useMemo(() => {\n if (asset?.kind !== \"audio\" || asset.waveform == null || !visible)\n return \"\";\n return buildWaveformPath(asset.waveform, 200, 32);\n }, [asset?.kind, asset?.waveform, visible]);\n\n return (\n <div\n ref={ref}\n role=\"button\"\n tabIndex={0}\n aria-selected={selected}\n data-clip-block={clip.id}\n onPointerDown={\n locked\n ? undefined\n : (event) => {\n handlers.select(clip, event.shiftKey);\n handlers.beginMove(event, clip);\n }\n }\n onDoubleClick={() => handlers.seekTo(clip)}\n className={cn(\n // select-none:素材名与时长只是标签,拖拽时不该被浏览器选中(会留下蓝色选区)\n 'group absolute top-1 bottom-1 overflow-hidden rounded-md border text-left select-none transition-shadow',\n KIND_BG[kind],\n selected\n ? \"ring-2 ring-primary ring-offset-1 ring-offset-timeline-bg\"\n : \"hover:brightness-110\",\n locked\n ? \"cursor-not-allowed opacity-70\"\n : \"cursor-grab active:cursor-grabbing\",\n )}\n style={{\n left: leftPx,\n width: widthPx,\n transform:\n previewOffsetPx !== 0\n ? `translateX(${previewOffsetPx}px)`\n : undefined,\n zIndex: selected ? 5 : 1,\n background,\n }}\n title={`${asset?.name ?? \"缺失素材\"}\\n${formatDuration(clip.durationUs)}`}\n >\n {/*\n * 片段上的文字/波形都压在不固定的画面(缩略图)上,语义固定为「深底浅字」,\n * 因此用 --cw-badge-scrim* 系列 token 而不是 text-white / bg-black。\n * 透明度用 token + `/xx` 表达,宿主改 token 后整套遮罩一起变。\n */}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-b from-badge-scrim/15 via-transparent to-badge-scrim/75\" />\n\n {waveformPath !== \"\" && (\n <svg\n viewBox=\"0 0 200 32\"\n preserveAspectRatio=\"none\"\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-2/3 w-full text-badge-scrim-foreground/60\"\n >\n <path d={waveformPath} stroke=\"currentColor\" strokeWidth={0.8} />\n </svg>\n )}\n\n <div className=\"pointer-events-none relative flex h-full flex-col justify-between p-1\">\n {!tooNarrow && (\n <p className=\"truncate text-[10px] leading-tight font-medium text-badge-scrim-foreground/95 drop-shadow\">\n {asset?.name ?? \"缺失素材\"}\n </p>\n )}\n {!tooNarrow && (\n <p className=\"truncate font-mono text-[9px] text-badge-scrim-foreground/75 tabular-nums\">\n {formatDuration(clip.durationUs)}\n {clip.playbackRate !== 1 && ` · ${clip.playbackRate}×`}\n {clip.muted && \" · 静音\"}\n </p>\n )}\n </div>\n\n {!locked && widthPx > 28 && (\n <>\n <div\n onPointerDown={(event) => handlers.beginTrimStart(event, clip)}\n className=\"absolute inset-y-0 left-0 z-10 flex cursor-col-resize items-center justify-center transition-colors hover:bg-primary/40\"\n style={{ width: TRIM_HANDLE_PX }}\n aria-label=\"裁剪起点\"\n role=\"slider\"\n aria-valuenow={0}\n >\n <span className=\"h-4 w-0.5 rounded bg-badge-scrim-foreground/70 opacity-0 group-hover:opacity-100\" />\n </div>\n <div\n onPointerDown={(event) => handlers.beginTrimEnd(event, clip)}\n className=\"absolute inset-y-0 right-0 z-10 flex cursor-col-resize items-center justify-center transition-colors hover:bg-primary/40\"\n style={{ width: TRIM_HANDLE_PX }}\n aria-label=\"裁剪终点\"\n role=\"slider\"\n aria-valuenow={0}\n >\n <span className=\"h-4 w-0.5 rounded bg-badge-scrim-foreground/70 opacity-0 group-hover:opacity-100\" />\n </div>\n </>\n )}\n </div>\n );\n});\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\"\nimport { ContextMenu as ContextMenuPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction ContextMenu({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {\n return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />\n}\n\nfunction ContextMenuTrigger({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {\n return (\n <ContextMenuPrimitive.Trigger data-slot=\"context-menu-trigger\" {...props} />\n )\n}\n\nfunction ContextMenuGroup({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {\n return (\n <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />\n )\n}\n\n// ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n// utility 不生效(右键菜单没有背景/边框)。原因详见 dialog.tsx 的 DialogPortal。\nfunction ContextMenuPortal({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {\n const container = usePortalContainer()\n return (\n <ContextMenuPrimitive.Portal\n data-slot=\"context-menu-portal\"\n container={container ?? undefined}\n {...props}\n />\n )\n}\n\nfunction ContextMenuSub({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {\n return <ContextMenuPrimitive.Sub data-slot=\"context-menu-sub\" {...props} />\n}\n\nfunction ContextMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {\n return (\n <ContextMenuPrimitive.RadioGroup\n data-slot=\"context-menu-radio-group\"\n {...props}\n />\n )\n}\n\nfunction ContextMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {\n inset?: boolean\n}) {\n return (\n <ContextMenuPrimitive.SubTrigger\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto\" />\n </ContextMenuPrimitive.SubTrigger>\n )\n}\n\nfunction ContextMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {\n return (\n <ContextMenuPrimitive.SubContent\n data-slot=\"context-menu-sub-content\"\n className={cn(\n \"z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ContextMenuContent({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {\n // Portal 挂载点由 ContextMenuPortal 统一处理(含 `.cw-root` 作用域)\n return (\n <ContextMenuPortal>\n <ContextMenuPrimitive.Content\n data-slot=\"context-menu-content\"\n className={cn(\n \"z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </ContextMenuPortal>\n )\n}\n\nfunction ContextMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {\n inset?: boolean\n variant?: \"default\" | \"destructive\"\n}) {\n return (\n <ContextMenuPrimitive.Item\n data-slot=\"context-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ContextMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {\n return (\n <ContextMenuPrimitive.CheckboxItem\n data-slot=\"context-menu-checkbox-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.CheckboxItem>\n )\n}\n\nfunction ContextMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {\n return (\n <ContextMenuPrimitive.RadioItem\n data-slot=\"context-menu-radio-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.RadioItem>\n )\n}\n\nfunction ContextMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {\n inset?: boolean\n}) {\n return (\n <ContextMenuPrimitive.Label\n data-slot=\"context-menu-label\"\n data-inset={inset}\n className={cn(\n \"px-2 py-1.5 text-sm font-medium text-foreground data-[inset]:pl-8\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ContextMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {\n return (\n <ContextMenuPrimitive.Separator\n data-slot=\"context-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction ContextMenuShortcut({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n ContextMenu,\n ContextMenuTrigger,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuCheckboxItem,\n ContextMenuRadioItem,\n ContextMenuLabel,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuGroup,\n ContextMenuPortal,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuRadioGroup,\n}\n","import { Copy, Info, Scissors, Trash2 } from 'lucide-react'\nimport {\n ContextMenu,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuTrigger,\n} from '@/components/ui/context-menu'\n\ninterface ClipContextMenuProps {\n children: React.ReactNode\n onSplit: () => void\n onDuplicate: () => void\n onRemove: () => void\n onShowInfo: () => void\n disabled?: boolean\n}\n\n/**\n * 时间轴右键菜单的「单例」外壳。\n *\n * 只挂一层 ContextMenu 包住整个轨道区 —— 若给每个 ClipBlock 各挂一个 Radix 菜单,\n * 200 个 clip 时右键与选中的重渲染成本会从 ~30ms 升到数百毫秒。\n * 具体作用于哪个 clip 由右键时的选中状态决定(见 Timeline 的 onContextMenu 处理)。\n */\nexport function ClipContextMenu({\n children,\n onSplit,\n onDuplicate,\n onRemove,\n onShowInfo,\n disabled,\n}: ClipContextMenuProps) {\n return (\n <ContextMenu>\n <ContextMenuTrigger asChild disabled={disabled}>\n {children}\n </ContextMenuTrigger>\n <ContextMenuContent className=\"w-44\">\n <ContextMenuItem onSelect={onSplit}>\n <Scissors className=\"size-3.5\" />\n 在播放头分割\n <ContextMenuShortcut>S</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem onSelect={onDuplicate}>\n <Copy className=\"size-3.5\" />\n 复制\n <ContextMenuShortcut>Ctrl+D</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem onSelect={onShowInfo}>\n <Info className=\"size-3.5\" />\n 属性\n </ContextMenuItem>\n <ContextMenuSeparator />\n <ContextMenuItem variant=\"destructive\" onSelect={onRemove}>\n <Trash2 className=\"size-3.5\" />\n 删除\n <ContextMenuShortcut>Del</ContextMenuShortcut>\n </ContextMenuItem>\n </ContextMenuContent>\n </ContextMenu>\n )\n}\n","import { cn } from '@/lib/cn'\nimport { RULER_HEIGHT_PX } from '@/lib/constants'\n\ninterface PlayheadProps {\n px: number\n playing: boolean\n className?: string\n}\n\n/** 播放头:一条竖线 + 顶部把手,跨过所有轨道 */\nexport function Playhead({ px, playing, className }: PlayheadProps) {\n return (\n <div\n className={cn('pointer-events-none absolute top-0 bottom-0 z-20 w-px', className)}\n style={{ left: px }}\n aria-hidden\n >\n <div\n className={cn(\n 'absolute inset-y-0 -left-px w-0.5 bg-playhead',\n playing && 'shadow-[0_0_6px_var(--cw-playhead)]',\n )}\n style={{ top: RULER_HEIGHT_PX - 4 }}\n />\n <div\n className={cn(\n 'absolute -translate-x-1/2 rounded-sm bg-playhead',\n playing ? 'size-3 rotate-45' : 'h-2.5 w-2.5 rotate-45',\n )}\n style={{ top: RULER_HEIGHT_PX - 14 }}\n />\n </div>\n )\n}\n","import { cn } from '@/lib/cn'\r\n\r\nexport interface MarqueeProps {\r\n x1: number\r\n y1: number\r\n x2: number\r\n y2: number\r\n className?: string\r\n}\r\n\r\n/** 框选矩形 */\r\nexport function Marquee({ x1, y1, x2, y2, className }: MarqueeProps) {\r\n return (\r\n <div\r\n className={cn(\r\n 'pointer-events-none absolute z-40 rounded-sm border border-primary/70 bg-primary/15',\r\n className,\r\n )}\r\n style={{\r\n left: Math.min(x1, x2),\r\n top: Math.min(y1, y2),\r\n width: Math.abs(x2 - x1),\r\n height: Math.abs(y2 - y1),\r\n }}\r\n aria-hidden\r\n />\r\n )\r\n}\r\n","import { useCallback, useEffect, useRef } from 'react'\r\nimport { useUi, useUiStoreApi } from '../../../context'\r\nimport { MAX_PX_PER_SECOND, MIN_PX_PER_SECOND, ZOOM_LEVELS } from '@/lib/constants'\r\nimport { clamp } from '@cut-workbench/core'\r\n\r\nexport interface UseTimelineZoomResult {\r\n pxPerSecond: number\r\n zoomIn: () => void\r\n zoomOut: () => void\r\n setZoom: (value: number) => void\r\n /** 按倍数缩放(Ctrl+滚轮用),以指针位置为锚点 */\r\n zoomBy: (factor: number, anchorClientX?: number, container?: HTMLElement | null) => void\r\n canZoomIn: boolean\r\n canZoomOut: boolean\r\n}\r\n\r\n/**\r\n * 时间轴横向缩放。\r\n *\r\n * 缩放以「指针位置对应的时刻」为锚点:先记录指针下的时间,缩放后把 scrollLeft\r\n * 调整回去,视觉上内容不会乱跳。\r\n */\r\nexport function useTimelineZoom(containerRef: React.RefObject<HTMLElement | null>): UseTimelineZoomResult {\r\n const uiApi = useUiStoreApi()\r\n const pxPerSecond = useUi((s) => s.pxPerSecond)\r\n const setPxPerSecond = useUi((s) => s.setPxPerSecond)\r\n const setScrollLeftPx = useUi((s) => s.setScrollLeftPx)\r\n\r\n const applyZoom = useCallback(\r\n (nextPps: number, anchorClientX?: number) => {\r\n const container = containerRef.current\r\n const prevPps = uiApi.getState().pxPerSecond\r\n const next = clamp(nextPps, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND)\r\n if (next === prevPps) return\r\n\r\n if (container != null && anchorClientX != null) {\r\n const rect = container.getBoundingClientRect()\r\n const offsetPx = anchorClientX - rect.left\r\n const anchorTimeSec = (container.scrollLeft + offsetPx) / prevPps\r\n setPxPerSecond(next)\r\n // 下一帧再回调 scroll,等 DOM 用新宽度重排\r\n requestAnimationFrame(() => {\r\n const targetScroll = anchorTimeSec * next - offsetPx\r\n container.scrollLeft = Math.max(0, targetScroll)\r\n setScrollLeftPx(container.scrollLeft)\r\n })\r\n } else {\r\n setPxPerSecond(next)\r\n }\r\n },\r\n [containerRef, setPxPerSecond, setScrollLeftPx],\r\n )\r\n\r\n const zoomIn = useCallback(() => {\r\n const next = ZOOM_LEVELS.find((z) => z > pxPerSecond) ?? MAX_PX_PER_SECOND\r\n applyZoom(next)\r\n }, [applyZoom, pxPerSecond])\r\n\r\n const zoomOut = useCallback(() => {\r\n const next = [...ZOOM_LEVELS].reverse().find((z) => z < pxPerSecond) ?? MIN_PX_PER_SECOND\r\n applyZoom(next)\r\n }, [applyZoom, pxPerSecond])\r\n\r\n const zoomBy = useCallback(\r\n (factor: number, anchorClientX?: number, container?: HTMLElement | null) => {\r\n const target = container === undefined ? containerRef.current : container\r\n void target\r\n applyZoom(pxPerSecond * factor, anchorClientX)\r\n },\r\n [applyZoom, containerRef, pxPerSecond],\r\n )\r\n\r\n const setZoom = useCallback((value: number) => applyZoom(value), [applyZoom])\r\n\r\n // Ctrl+滚轮缩放(非 passive,才能 preventDefault 阻止页面缩放)\r\n useEffect(() => {\r\n const container = containerRef.current\r\n if (container == null) return\r\n const onWheel = (event: WheelEvent) => {\r\n if (!event.ctrlKey && !event.metaKey) return\r\n event.preventDefault()\r\n const factor = event.deltaY < 0 ? 1.15 : 1 / 1.15\r\n applyZoom(uiApi.getState().pxPerSecond * factor, event.clientX)\r\n }\r\n container.addEventListener('wheel', onWheel, { passive: false })\r\n return () => container.removeEventListener('wheel', onWheel)\r\n }, [applyZoom, containerRef])\r\n\r\n return {\r\n pxPerSecond,\r\n zoomIn,\r\n zoomOut,\r\n setZoom,\r\n zoomBy,\r\n canZoomIn: pxPerSecond < MAX_PX_PER_SECOND,\r\n canZoomOut: pxPerSecond > MIN_PX_PER_SECOND,\r\n }\r\n}\r\n\r\n/** 内部使用:ref 仅为兼容 React 19 类型 */\r\nexport type TimelineZoomRef = ReturnType<typeof useRef<number>>\r\n","import { useCallback, useMemo, useRef } from 'react'\nimport type { Project, TimelineClip } from '@cut-workbench/core'\nimport {\n computeSnapWithHysteresis,\n type SnapResult,\n type SnapTarget,\n} from '@cut-workbench/core'\nimport { getClipEnd } from '@cut-workbench/core'\nimport { SNAP_RELEASE_PX, SNAP_THRESHOLD_PX } from '@/lib/constants'\nimport { SNAP_GUIDE_TTL_MS } from '../constants'\n\nexport interface SnapContext {\n project: Project\n /** 当前拖动/裁剪的 clip id(不参与吸附目标) */\n excludeIds: string[]\n playheadUs: number\n}\n\nexport interface UseSnapResult {\n /** 计算吸附;同时返回需要绘制的辅助线 */\n snap: (\n startUs: number,\n durationUs: number,\n pxPerSecond: number,\n context: SnapContext,\n /** 参与吸附的边,默认两端;裁剪终点时应只传 ['end'] */\n edges?: Array<'start' | 'end'>,\n ) => SnapResult\n /** 清空滞后状态 */\n reset: () => void\n}\n\n/**\n * 吸附 hook:把 store 数据转成候选目标,调用 core 的纯函数。\n *\n * 候选目标:\n * - 其它 clip 的头 / 尾\n * - 播放头\n * - 0 点\n * - 每条轨道的末尾(同轨最后一个 clip 的结束)\n *\n * 滞后状态保存在 ref 里(每次拖动会话重置),避免 React 重渲染丢失。\n */\nexport function useSnap(): UseSnapResult {\n const previousRef = useRef<SnapResult | null>(null)\n const lastTouchRef = useRef(0)\n\n const buildTargets = useCallback((context: SnapContext): SnapTarget[] => {\n const exclude = new Set(context.excludeIds)\n const targets: SnapTarget[] = [{ us: 0, kind: 'zero' }]\n\n const perTrackEnd = new Map<string, number>()\n for (const clip of context.project.clips) {\n if (exclude.has(clip.id)) continue\n targets.push({ us: clip.startUs, kind: 'clip-start', sourceId: clip.id })\n targets.push({ us: getClipEnd(clip), kind: 'clip-end', sourceId: clip.id })\n perTrackEnd.set(clip.trackId, Math.max(perTrackEnd.get(clip.trackId) ?? 0, getClipEnd(clip)))\n }\n\n targets.push({ us: context.playheadUs, kind: 'playhead' })\n for (const [trackId, end] of perTrackEnd) {\n targets.push({ us: end, kind: 'track-end', sourceId: trackId })\n }\n return targets\n }, [])\n\n const snap = useCallback(\n (\n startUs: number,\n durationUs: number,\n pxPerSecond: number,\n context: SnapContext,\n edges?: Array<'start' | 'end'>,\n ): SnapResult => {\n const now = performance.now()\n // 超过 TTL 的旧吸附状态不再参与滞后判断(例如两次独立拖拽)\n const previous = now - lastTouchRef.current < SNAP_GUIDE_TTL_MS ? previousRef.current : null\n const result = computeSnapWithHysteresis(\n {\n startUs,\n durationUs,\n targets: buildTargets(context),\n pxPerSecond,\n thresholdPx: SNAP_THRESHOLD_PX,\n previous,\n edges,\n },\n SNAP_RELEASE_PX,\n )\n previousRef.current = result.edge != null ? result : null\n lastTouchRef.current = now\n return result\n },\n [buildTargets],\n )\n\n const reset = useCallback(() => {\n previousRef.current = null\n lastTouchRef.current = 0\n }, [])\n\n return useMemo(() => ({ snap, reset }), [snap, reset])\n}\n\nexport type { SnapResult, SnapTarget, TimelineClip }\n","import { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { TimelineClip } from '@cut-workbench/core'\r\nimport { pxToUs, usToPx } from '@cut-workbench/core'\r\nimport { EMPTY_SNAP } from '@cut-workbench/core'\r\nimport { useSnap } from './useSnap'\r\nimport { useEditor } from '@cut-workbench/react'\r\n\r\nexport type DragMode = 'move' | 'trim-start' | 'trim-end'\r\n\r\nexport interface DragState {\r\n mode: DragMode\r\n clipId: string\r\n trackId: string\r\n originStartUs: number\r\n originDurationUs: number\r\n originInPointUs: number\r\n playbackRate: number\r\n /** 抓取点到 clip 左边缘的距离(像素),保证抓取点不跳 */\r\n grabOffsetPx: number\r\n}\r\n\r\nexport interface DragPreview {\r\n clipId: string\r\n trackId: string\r\n startUs: number\r\n durationUs: number\r\n guidePx: number[]\r\n}\r\n\r\nexport interface DragCommit extends DragPreview {\r\n inPointUs: number\r\n}\r\n\r\ninterface UseTimelineDragOptions {\r\n pxPerSecond: number\r\n /** 时间轴内容区左边界(clientX 坐标) */\r\n contentLeftPx: () => number\r\n /** 根据指针纵坐标定位目标轨道 id */\r\n resolveTrackAt: (clientY: number) => string | null\r\n /** 是否启用吸附(工具栏磁吸开关);关闭时拖拽完全自由 */\r\n snapEnabled: boolean\r\n onPreview: (preview: DragPreview | null) => void\r\n onCommit: (commit: DragCommit) => void\r\n}\r\n\r\nconst MIN_DURATION_US = 100_000\r\n\r\n/**\r\n * 时间轴拖拽交互(移动 / 左侧裁剪 / 右侧裁剪)。\r\n *\r\n * 性能设计:拖拽过程只更新「临时预览」(Timeline 用 transform 位移渲染),\r\n * 松手时才一次性写 store。因此 200 个 clip 拖动也不触发全量重渲染。\r\n *\r\n * 吸附:移动与右侧裁剪会参考吸附结果;左侧裁剪不吸附(避免与入点计算打架,\r\n * 这一点在 README 的已知限制里说明)。\r\n */\r\nexport function useTimelineDrag({\r\n pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled,\r\n onPreview,\r\n onCommit,\r\n}: UseTimelineDragOptions) {\r\n const [drag, setDrag] = useState<DragState | null>(null)\r\n const dragRef = useRef<DragState | null>(null)\r\n const snapApi = useSnap()\r\n const project = useEditor((s) => s.project)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n\r\n const envRef = useRef({\r\n pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled,\r\n project,\r\n playheadUs,\r\n })\r\n\r\n // 最新值在 effect 中同步(不在渲染期写 ref,保持 React Compiler 的纯渲染假设)\r\n useEffect(() => {\r\n envRef.current = {\r\n pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled,\r\n project,\r\n playheadUs,\r\n }\r\n })\r\n\r\n const compute = useCallback(\r\n (clientX: number, clientY: number, current: DragState) => {\r\n const env = envRef.current\r\n const xInContent = clientX - env.contentLeftPx()\r\n const pointerUs = pxToUs(xInContent, env.pxPerSecond)\r\n const rate = current.playbackRate > 0 ? current.playbackRate : 1\r\n\r\n let startUs = current.originStartUs\r\n let durationUs = current.originDurationUs\r\n let inPointUs = current.originInPointUs\r\n\r\n if (current.mode === 'move') {\r\n const grabUs = pxToUs(current.grabOffsetPx, env.pxPerSecond)\r\n startUs = Math.max(0, pointerUs - grabUs)\r\n } else if (current.mode === 'trim-start') {\r\n const endUs = current.originStartUs + current.originDurationUs\r\n // 起点最多回到「源素材入点归零」的位置,且至少保留 MIN_DURATION_US\r\n const minStartUs = current.originStartUs - current.originInPointUs / rate\r\n startUs = Math.max(\r\n Math.max(0, minStartUs),\r\n Math.min(pointerUs, endUs - MIN_DURATION_US),\r\n )\r\n } else {\r\n // 右侧裁剪:起点固定,终点跟随指针,下限由最短时长保证\r\n durationUs = Math.max(MIN_DURATION_US, pointerUs - current.originStartUs)\r\n }\r\n\r\n const targetTrack = env.resolveTrackAt(clientY) ?? current.trackId\r\n\r\n /**\r\n * 吸附。\r\n *\r\n * 关键:把「哪条边被吸住」交给 core 判定,这里只把位移量施加到对应的量上。\r\n * 旧实现无论吸住哪条边都拿 `snappedStartUs - originStartUs` 当作新时长,\r\n * 右侧裁剪时起点固定,于是时长被算成「目标点与当前终点的差值」,\r\n * 片段瞬间缩成 0.1s(用户看到的「闪到左边」)。这就是那次 bug 的根因。\r\n *\r\n * 三条拖动路径各自只让「真正在动的那条边」参与吸附:\r\n * - move:两端都在动,都参与;\r\n * - trim-end:只有终点在动 → ['end'];\r\n * - trim-start:只有起点在动 → ['start'],结束时再按位移回算入点。\r\n */\r\n const edges: Array<'start' | 'end'> =\r\n current.mode === 'trim-end'\r\n ? ['end']\r\n : current.mode === 'trim-start'\r\n ? ['start']\r\n : ['start', 'end']\r\n const result = env.snapEnabled\r\n ? snapApi.snap(\r\n startUs,\r\n durationUs,\r\n env.pxPerSecond,\r\n {\r\n project: env.project,\r\n excludeIds: [current.clipId],\r\n playheadUs: env.playheadUs,\r\n },\r\n edges,\r\n )\r\n : EMPTY_SNAP\r\n\r\n if (result.edge != null && result.deltaUs !== 0) {\r\n if (current.mode === 'trim-end') {\r\n // 终点在动:位移直接落到时长上,起点不动\r\n durationUs = durationUs + result.deltaUs\r\n } else {\r\n // move / trim-start:起点在动,位移落到起点;trim-start 的时长随之收缩\r\n const nextStart = startUs + result.deltaUs\r\n if (current.mode === 'move') {\r\n startUs = nextStart\r\n } else {\r\n startUs = nextStart\r\n durationUs = current.originStartUs + current.originDurationUs - nextStart\r\n }\r\n }\r\n }\r\n\r\n if (current.mode === 'trim-start') {\r\n const endUs = current.originStartUs + current.originDurationUs\r\n const deltaTimeline = startUs - current.originStartUs\r\n durationUs = endUs - startUs\r\n inPointUs = Math.max(0, current.originInPointUs + Math.round(deltaTimeline * rate))\r\n }\r\n\r\n return {\r\n preview: {\r\n clipId: current.clipId,\r\n trackId: targetTrack,\r\n startUs: Math.max(0, Math.round(startUs)),\r\n durationUs: Math.max(MIN_DURATION_US, Math.round(durationUs)),\r\n guidePx: result.guidePx,\r\n },\r\n inPointUs: Math.round(inPointUs),\r\n }\r\n },\r\n [snapApi],\r\n )\r\n\r\n const begin = useCallback(\r\n (event: React.PointerEvent, clip: TimelineClip, mode: DragMode) => {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n const env = envRef.current\r\n const pointerX = event.clientX - env.contentLeftPx()\r\n const clipLeftPx = usToPx(clip.startUs, env.pxPerSecond)\r\n const state: DragState = {\r\n mode,\r\n clipId: clip.id,\r\n trackId: clip.trackId,\r\n originStartUs: clip.startUs,\r\n originDurationUs: clip.durationUs,\r\n originInPointUs: clip.inPointUs,\r\n playbackRate: clip.playbackRate,\r\n grabOffsetPx: mode === 'move' ? Math.max(0, pointerX - clipLeftPx) : 0,\r\n }\r\n dragRef.current = state\r\n setDrag(state)\r\n snapApi.reset()\r\n },\r\n [snapApi],\r\n )\r\n\r\n useEffect(() => {\r\n if (drag == null) return\r\n let frame: number | null = null\r\n let lastPointer: { x: number; y: number } | null = null\r\n\r\n const flush = () => {\r\n frame = null\r\n const current = dragRef.current\r\n if (lastPointer == null || current == null) return\r\n const { preview } = compute(lastPointer.x, lastPointer.y, current)\r\n onPreview(preview)\r\n }\r\n\r\n const move = (event: PointerEvent) => {\r\n lastPointer = { x: event.clientX, y: event.clientY }\r\n if (frame != null) return\r\n frame = requestAnimationFrame(flush)\r\n }\r\n\r\n const finish = () => {\r\n if (frame != null) cancelAnimationFrame(frame)\r\n const current = dragRef.current\r\n if (current != null && lastPointer != null) {\r\n const { preview, inPointUs } = compute(lastPointer.x, lastPointer.y, current)\r\n onCommit({ ...preview, inPointUs })\r\n }\r\n dragRef.current = null\r\n lastPointer = null\r\n setDrag(null)\r\n onPreview(null)\r\n snapApi.reset()\r\n }\r\n\r\n window.addEventListener('pointermove', move)\r\n window.addEventListener('pointerup', finish)\r\n window.addEventListener('pointercancel', finish)\r\n return () => {\r\n if (frame != null) cancelAnimationFrame(frame)\r\n window.removeEventListener('pointermove', move)\r\n window.removeEventListener('pointerup', finish)\r\n window.removeEventListener('pointercancel', finish)\r\n }\r\n }, [compute, drag, onCommit, onPreview, snapApi])\r\n\r\n return {\r\n drag,\r\n beginMove: (event: React.PointerEvent, clip: TimelineClip) => begin(event, clip, 'move'),\r\n beginTrimStart: (event: React.PointerEvent, clip: TimelineClip) =>\r\n begin(event, clip, 'trim-start'),\r\n beginTrimEnd: (event: React.PointerEvent, clip: TimelineClip) => begin(event, clip, 'trim-end'),\r\n }\r\n}\r\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { useUiStoreApi } from '../../../context'\r\nimport { Layers } from 'lucide-react'\r\nimport type { MediaAsset, TimelineClip, Track } from '@cut-workbench/core'\r\nimport { getProjectDuration } from '@cut-workbench/core'\r\nimport { pxToUs, usToPx } from '@cut-workbench/core'\r\nimport { RULER_HEIGHT_PX, TRACK_HEADER_WIDTH_PX, TRACK_HEIGHT_PX } from '@/lib/constants'\r\nimport { cn } from '@/lib/cn'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { TimelineToolbar } from './TimelineToolbar'\r\nimport { Ruler } from './Ruler'\r\nimport { TrackHeader } from './TrackHeader'\r\nimport { ClipBlock, type ClipHandlers } from './ClipBlock'\r\nimport { ClipContextMenu } from './ClipContextMenu'\r\nimport { Playhead } from './Playhead'\r\nimport { Marquee } from './Marquee'\r\nimport { useTimelineZoom } from '../hooks/useTimelineZoom'\r\nimport { useTimelineDrag, type DragPreview } from '../hooks/useTimelineDrag'\r\nimport { MARQUEE_THRESHOLD_PX } from '../constants'\r\n\r\n/**\r\n * 时间轴主视图。\r\n *\r\n * 布局:\r\n * ┌ 工具条 ─────────────────────────────────────┐\r\n * │ [轨道头列 固定宽] │ [刻度尺 + 轨道内容 横向滚动] │\r\n * └─────────────────────────────────────────────┘\r\n *\r\n * 性能:\r\n * - ClipBlock 全 memo,拖拽只更新 preview state(不写 store);\r\n * - 刻度尺 / 播放头分别订阅,互不牵连;\r\n * - 吸附是 core 里的纯函数,候选点一次构建。\r\n */\r\nexport function Timeline() {\r\n const editor = useCutEditor()\r\n const uiApi = useUiStoreApi()\r\n const project = useEditor((s) => s.project)\r\n const assets = useEditor((s) => s.assets)\r\n const selectedClipIds = useEditor((s) => s.selectedClipIds)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n const setPlayhead = useEditor((s) => s.setPlayhead)\r\n const selectClips = useEditor((s) => s.selectClips)\r\n const splitClipsAt = useEditor((s) => s.splitClipsAt)\r\n const duplicateSelection = useEditor((s) => s.duplicateSelection)\r\n const removeClips = useEditor((s) => s.removeClips)\r\n const updateTrack = useEditor((s) => s.updateTrack)\r\n const removeTrack = useEditor((s) => s.removeTrack)\r\n const addTrack = useEditor((s) => s.addTrack)\r\n const compactTrack = useEditor((s) => s.compactTrack)\r\n const playing = useEditor((s) => s.playing)\r\n\r\n const [magnet, setMagnet] = useState(true)\r\n const [preview, setPreview] = useState<DragPreview | null>(null)\r\n const [scrollLeft, setScrollLeft] = useState(0)\r\n const [viewportWidth, setViewportWidth] = useState(800)\r\n\r\n const scrollRef = useRef<HTMLDivElement | null>(null)\r\n const tracksRef = useRef<HTMLDivElement | null>(null)\r\n\r\n const zoom = useTimelineZoom(scrollRef)\r\n const durationUs = getProjectDuration(project)\r\n\r\n const contentWidthPx = useMemo(\r\n () =>\r\n Math.max(\r\n viewportWidth + 240,\r\n Math.ceil((durationUs / 1_000_000) * zoom.pxPerSecond) + 320,\r\n ),\r\n [durationUs, viewportWidth, zoom.pxPerSecond],\r\n )\r\n\r\n const assetMap = useMemo(\r\n () => new Map<string, MediaAsset>(assets.map((a) => [a.id, a])),\r\n [assets],\r\n )\r\n const trackMap = useMemo(\r\n () => new Map<string, Track>(project.tracks.map((t) => [t.id, t])),\r\n [project.tracks],\r\n )\r\n const clipsByTrack = useMemo(() => {\r\n const map = new Map<string, TimelineClip[]>()\r\n for (const clip of project.clips) {\r\n const list = map.get(clip.trackId)\r\n if (list) list.push(clip)\r\n else map.set(clip.trackId, [clip])\r\n }\r\n return map\r\n }, [project.clips])\r\n\r\n useEffect(() => {\r\n const el = scrollRef.current\r\n if (el == null) return\r\n const ro = new ResizeObserver(() => setViewportWidth(el.clientWidth))\r\n ro.observe(el)\r\n setViewportWidth(el.clientWidth)\r\n return () => ro.disconnect()\r\n }, [])\r\n\r\n useEffect(() => {\r\n const el = scrollRef.current\r\n if (el == null) return\r\n const onScroll = () => {\r\n setScrollLeft(el.scrollLeft)\r\n uiApi.getState().setScrollLeftPx(el.scrollLeft)\r\n }\r\n el.addEventListener('scroll', onScroll, { passive: true })\r\n return () => el.removeEventListener('scroll', onScroll)\r\n }, [])\r\n\r\n const contentLeftPx = useCallback(\r\n () => tracksRef.current?.getBoundingClientRect().left ?? 0,\r\n [],\r\n )\r\n\r\n const resolveTrackAt = useCallback(\r\n (clientY: number): string | null => {\r\n const el = tracksRef.current\r\n if (el == null) return null\r\n const y = clientY - el.getBoundingClientRect().top\r\n const index = Math.floor(y / TRACK_HEIGHT_PX)\r\n return project.tracks[index]?.id ?? null\r\n },\r\n [project.tracks],\r\n )\r\n\r\n const handlePreview = useCallback((next: DragPreview | null) => {\r\n setPreview(next)\r\n }, [])\r\n\r\n const handleCommit = useCallback(\r\n (commit: {\r\n clipId: string\r\n trackId: string\r\n startUs: number\r\n durationUs: number\r\n inPointUs: number\r\n }) => {\r\n const clip = project.clips.find((c) => c.id === commit.clipId)\r\n if (!clip) return\r\n const patch: Partial<typeof clip> = {}\r\n if (commit.trackId !== clip.trackId) patch.trackId = commit.trackId\r\n if (commit.startUs !== clip.startUs) patch.startUs = commit.startUs as typeof clip.startUs\r\n if (commit.durationUs !== clip.durationUs) {\r\n patch.durationUs = commit.durationUs as typeof clip.durationUs\r\n }\r\n if (commit.inPointUs !== clip.inPointUs) {\r\n patch.inPointUs = commit.inPointUs as typeof clip.inPointUs\r\n }\r\n if (Object.keys(patch).length === 0) return\r\n editor.store.getState().updateClip(commit.clipId, patch, true)\r\n },\r\n [project.clips],\r\n )\r\n\r\n const drag = useTimelineDrag({\r\n pxPerSecond: zoom.pxPerSecond,\r\n contentLeftPx,\r\n resolveTrackAt,\r\n snapEnabled: magnet,\r\n onPreview: handlePreview,\r\n onCommit: handleCommit,\r\n })\r\n\r\n const playheadPx = usToPx(playheadUs, zoom.pxPerSecond)\r\n\r\n const seekToClientX = useCallback(\r\n (clientX: number) => {\r\n const left = tracksRef.current?.getBoundingClientRect().left ?? 0\r\n const scrolled = scrollRef.current?.scrollLeft ?? 0\r\n const us = pxToUs(clientX - left + scrolled, zoom.pxPerSecond)\r\n setPlayhead(Math.max(0, us))\r\n },\r\n [setPlayhead, zoom.pxPerSecond],\r\n )\r\n\r\n // 刻度尺拖拽:音频由 PreviewPanel 侧暂停(scrub 事件)\r\n const scrubCleanupRef = useRef<(() => void) | null>(null)\r\n const beginScrub = useCallback(\r\n (event: React.PointerEvent) => {\r\n window.dispatchEvent(new CustomEvent('cut-test:scrub-start'))\r\n seekToClientX(event.clientX)\r\n const move = (e: PointerEvent) => seekToClientX(e.clientX)\r\n const up = () => {\r\n window.removeEventListener('pointermove', move)\r\n window.removeEventListener('pointerup', up)\r\n window.dispatchEvent(new CustomEvent('cut-test:scrub-end'))\r\n scrubCleanupRef.current = null\r\n }\r\n scrubCleanupRef.current = up\r\n window.addEventListener('pointermove', move)\r\n window.addEventListener('pointerup', up)\r\n },\r\n [seekToClientX],\r\n )\r\n\r\n useEffect(() => {\r\n return () => {\r\n scrubCleanupRef.current?.()\r\n }\r\n }, [])\r\n\r\n // 框选\r\n const [marquee, setMarquee] = useState<{\r\n x1: number\r\n y1: number\r\n x2: number\r\n y2: number\r\n } | null>(null)\r\n\r\n const handleBackgroundPointerDown = useCallback(\r\n (event: React.PointerEvent) => {\r\n if (event.button !== 0) return\r\n const target = event.target as HTMLElement\r\n if (target.closest('[data-clip-block]')) return\r\n if (target.closest('[data-track-header]')) return\r\n const area = tracksRef.current\r\n if (area == null) return\r\n const rect = area.getBoundingClientRect()\r\n const startX = event.clientX\r\n const startY = event.clientY\r\n let moved = false\r\n const baseSelection = event.shiftKey ? selectedClipIds : []\r\n\r\n const move = (e: PointerEvent) => {\r\n if (!moved && Math.hypot(e.clientX - startX, e.clientY - startY) < MARQUEE_THRESHOLD_PX) {\r\n return\r\n }\r\n moved = true\r\n setMarquee({\r\n x1: startX - rect.left,\r\n y1: startY - rect.top,\r\n x2: e.clientX - rect.left,\r\n y2: e.clientY - rect.top,\r\n })\r\n }\r\n const up = (e: PointerEvent) => {\r\n window.removeEventListener('pointermove', move)\r\n window.removeEventListener('pointerup', up)\r\n setMarquee(null)\r\n if (!moved) {\r\n selectClips([])\r\n return\r\n }\r\n const minX = Math.min(startX, e.clientX) - rect.left\r\n const maxX = Math.max(startX, e.clientX) - rect.left\r\n const minY = Math.min(startY, e.clientY) - rect.top\r\n const maxY = Math.max(startY, e.clientY) - rect.top\r\n const hits: string[] = []\r\n project.tracks.forEach((track, index) => {\r\n const rowTop = index * TRACK_HEIGHT_PX\r\n const rowBottom = rowTop + TRACK_HEIGHT_PX\r\n if (rowBottom < minY || rowTop > maxY) return\r\n for (const clip of project.clips) {\r\n if (clip.trackId !== track.id) continue\r\n const leftPx = usToPx(clip.startUs, zoom.pxPerSecond)\r\n const rightPx = usToPx(clip.startUs + clip.durationUs, zoom.pxPerSecond)\r\n if (rightPx < minX || leftPx > maxX) continue\r\n hits.push(clip.id)\r\n }\r\n })\r\n selectClips(Array.from(new Set([...baseSelection, ...hits])))\r\n }\r\n window.addEventListener('pointermove', move)\r\n window.addEventListener('pointerup', up)\r\n },\r\n [project.clips, project.tracks, selectClips, selectedClipIds, zoom.pxPerSecond],\r\n )\r\n\r\n const zoomToFit = useCallback(() => {\r\n if (durationUs <= 0 || viewportWidth <= 0) return\r\n zoom.setZoom(Math.max(10, (viewportWidth - 60) / (durationUs / 1_000_000)))\r\n }, [durationUs, viewportWidth, zoom])\r\n\r\n const splitSelection = useCallback(() => {\r\n splitClipsAt(playheadUs, selectedClipIds.length > 0 ? selectedClipIds : undefined)\r\n }, [playheadUs, selectedClipIds, splitClipsAt])\r\n\r\n const splitClip = useCallback(\r\n (clip: TimelineClip) => {\r\n splitClipsAt(editor.store.getState().playheadUs, [clip.id])\r\n },\r\n [splitClipsAt],\r\n )\r\n\r\n const selectClip = useCallback(\r\n (clip: TimelineClip, additive: boolean) => {\r\n const state = editor.store.getState()\r\n if (additive) state.toggleSelectClip(clip.id)\r\n else if (!state.selectedClipIds.includes(clip.id)) state.selectClips([clip.id])\r\n },\r\n [],\r\n )\r\n\r\n const duplicateClip = useCallback(\r\n (clip: TimelineClip) => {\r\n const state = editor.store.getState()\r\n state.selectClips([clip.id])\r\n state.duplicateSelection(500_000)\r\n },\r\n [],\r\n )\r\n\r\n const removeClip = useCallback((clip: TimelineClip) => {\r\n editor.store.getState().removeClips([clip.id])\r\n }, [])\r\n\r\n const seekToClip = useCallback((clip: TimelineClip) => {\r\n editor.store.getState().setPlayhead(clip.startUs)\r\n }, [])\r\n\r\n // 稳定的 handler 集合:避免每次渲染生成新函数导致 ClipBlock 的 memo 失效\r\n // (200 clip 场景下这是拖拽是否流畅的关键)\r\n /** 右键作用于「当前选中的第一个 clip」(右键时会先选中它) */\r\n const firstSelectedClip = useCallback((): TimelineClip => {\r\n const state = editor.store.getState()\r\n const id = state.selectedClipIds[0]\r\n return state.project.clips.find((c) => c.id === id) ?? state.project.clips[0]!\r\n }, [])\r\n\r\n /** 右键:若命中 clip 则选中它;右键空白处不弹菜单 */\r\n const handleTrackContextMenu = useCallback((event: React.MouseEvent) => {\r\n const target = event.target as HTMLElement\r\n const el = target.closest('[data-clip-block]') as HTMLElement | null\r\n if (el == null) {\r\n event.preventDefault()\r\n return\r\n }\r\n const clipId = el.dataset.clipBlock\r\n if (clipId == null) return\r\n const state = editor.store.getState()\r\n if (!state.selectedClipIds.includes(clipId)) state.selectClips([clipId])\r\n }, [])\r\n\r\n const handlers = useMemo<ClipHandlers>(\r\n () => ({\r\n beginMove: drag.beginMove,\r\n beginTrimStart: drag.beginTrimStart,\r\n beginTrimEnd: drag.beginTrimEnd,\r\n select: selectClip,\r\n split: splitClip,\r\n duplicate: duplicateClip,\r\n remove: removeClip,\r\n showInfo: (clip) => selectClip(clip, false),\r\n seekTo: seekToClip,\r\n selectOnly: (clip) => selectClip(clip, false),\r\n }),\r\n [\r\n drag.beginMove,\r\n drag.beginTrimEnd,\r\n drag.beginTrimStart,\r\n duplicateClip,\r\n removeClip,\r\n selectClip,\r\n seekToClip,\r\n splitClip,\r\n ],\r\n )\r\n\r\n return (\r\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\r\n <TimelineToolbar\r\n playheadUs={playheadUs}\r\n durationUs={durationUs}\r\n pxPerSecond={zoom.pxPerSecond}\r\n selectionCount={selectedClipIds.length}\r\n magnetEnabled={magnet}\r\n onToggleMagnet={() => setMagnet((m) => !m)}\r\n onSplit={splitSelection}\r\n onDuplicate={() => duplicateSelection(500_000)}\r\n onRemove={() => removeClips(selectedClipIds)}\r\n onZoomIn={zoom.zoomIn}\r\n onZoomOut={zoom.zoomOut}\r\n onZoomToFit={zoomToFit}\r\n onAddTrack={(kind) => addTrack(kind)}\r\n canZoomIn={zoom.canZoomIn}\r\n canZoomOut={zoom.canZoomOut}\r\n />\r\n\r\n <div className=\"relative flex min-h-0 min-w-0 flex-1 overflow-hidden\">\r\n {/* 左列:轨道头(固定,不随横向滚动) */}\r\n <div\r\n className=\"thin-scrollbar flex shrink-0 flex-col overflow-y-auto border-r\"\r\n style={{ width: TRACK_HEADER_WIDTH_PX }}\r\n data-track-header-column\r\n >\r\n <div\r\n className=\"flex shrink-0 items-center border-b px-2 text-[10px] text-muted-foreground\"\r\n style={{ height: RULER_HEIGHT_PX }}\r\n >\r\n <Layers className=\"mr-1 size-3\" />\r\n 轨道\r\n </div>\r\n {project.tracks.map((track) => (\r\n <TrackHeader\r\n key={track.id}\r\n track={track}\r\n onToggleMute={() => updateTrack(track.id, { muted: !track.muted })}\r\n onToggleLock={() => updateTrack(track.id, { locked: !track.locked })}\r\n onToggleHidden={() => updateTrack(track.id, { hidden: !track.hidden })}\r\n onRemove={() => removeTrack(track.id)}\r\n />\r\n ))}\r\n <div className=\"flex gap-1 p-1.5\">\r\n <button\r\n type=\"button\"\r\n className=\"flex-1 rounded border border-dashed py-1 text-[10px] text-muted-foreground hover:border-primary/50 hover:text-foreground\"\r\n onClick={() => addTrack('visual')}\r\n >\r\n + 视频轨\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"flex-1 rounded border border-dashed py-1 text-[10px] text-muted-foreground hover:border-primary/50 hover:text-foreground\"\r\n onClick={() => addTrack('audio')}\r\n >\r\n + 音频轨\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* 右列:刻度尺 + 轨道内容(唯一横向滚动容器) */}\r\n <div\r\n ref={scrollRef}\r\n className=\"thin-scrollbar min-w-0 flex-1 overflow-x-auto overflow-y-auto\"\r\n >\r\n <div style={{ width: contentWidthPx }} className=\"relative\">\r\n <div\r\n className=\"sticky top-0 z-30 bg-timeline-bg\"\r\n style={{ width: contentWidthPx }}\r\n onPointerDown={beginScrub}\r\n >\r\n <Ruler\r\n pxPerSecond={zoom.pxPerSecond}\r\n widthPx={contentWidthPx}\r\n playheadPx={playheadPx}\r\n scrollLeftPx={scrollLeft}\r\n />\r\n </div>\r\n\r\n <ClipContextMenu\r\n onSplit={() => handlers.split(firstSelectedClip())}\r\n onDuplicate={() => handlers.duplicate(firstSelectedClip())}\r\n onRemove={() => handlers.remove(firstSelectedClip())}\r\n onShowInfo={() => undefined}\r\n >\r\n <div\r\n ref={tracksRef}\r\n className=\"relative select-none\"\r\n onPointerDown={handleBackgroundPointerDown}\r\n onContextMenu={handleTrackContextMenu}\r\n data-timeline-tracks\r\n >\r\n {project.tracks.map((track, index) => (\r\n <div\r\n key={track.id}\r\n className={cn(\r\n 'relative border-b border-timeline-grid',\r\n track.kind === 'audio' ? 'bg-timeline-track/30' : 'bg-timeline-track/50',\r\n track.hidden && 'opacity-40',\r\n )}\r\n style={{ height: TRACK_HEIGHT_PX }}\r\n data-track-row={track.id}\r\n >\r\n <div\r\n className=\"pointer-events-none absolute inset-0 opacity-40\"\r\n style={{\r\n backgroundImage:\r\n 'repeating-linear-gradient(to right, var(--timeline-grid) 0 1px, transparent 1px 80px)',\r\n }}\r\n />\r\n {(clipsByTrack.get(track.id) ?? []).map((clip) => {\r\n const isPreview = preview?.clipId === clip.id\r\n const isSelected = selectedClipIds.includes(clip.id)\r\n return (\r\n <ClipBlock\r\n key={clip.id}\r\n clip={clip}\r\n asset={assetMap.get(clip.assetId)}\r\n pxPerSecond={zoom.pxPerSecond}\r\n selected={isSelected}\r\n locked={track.locked}\r\n trackKind={track.kind}\r\n previewOffsetPx={\r\n isPreview && preview != null\r\n ? usToPx(preview.startUs - clip.startUs, zoom.pxPerSecond)\r\n : 0\r\n }\r\n previewWidthPx={\r\n isPreview && preview != null\r\n ? usToPx(preview.durationUs, zoom.pxPerSecond)\r\n : undefined\r\n }\r\n handlers={handlers}\r\n />\r\n )\r\n })}\r\n\r\n {index === project.tracks.length - 1 && preview != null && (\r\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-px bg-primary/40\" />\r\n )}\r\n </div>\r\n ))}\r\n\r\n {preview?.guidePx.map((px) => (\r\n <div\r\n key={`guide-${px}`}\r\n className=\"pointer-events-none absolute top-0 bottom-0 z-30 w-px bg-warning\"\r\n style={{ left: px }}\r\n />\r\n ))}\r\n\r\n {marquee != null && <Marquee {...marquee} />}\r\n </div>\r\n </ClipContextMenu>\r\n\r\n <Playhead px={playheadPx} playing={playing} />\r\n\r\n {project.clips.length === 0 && (\r\n <div className=\"pointer-events-none absolute inset-x-0 top-16 z-10\">\r\n <EmptyState\r\n icon={Layers}\r\n title=\"时间轴为空\"\r\n description=\"从左侧素材库双击素材,或点击卡片上的「+」加入时间轴\"\r\n compact\r\n />\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* 轨道顺序提示:轨道数组顺序即 zIndex(越靠下越在上层) */}\r\n <div className=\"flex h-5 shrink-0 items-center justify-end gap-2 border-t px-2 text-[10px] text-muted-foreground\">\r\n <span>Shift+点击多选 · 框选 · Ctrl+滚轮缩放</span>\r\n <button\r\n type=\"button\"\r\n className=\"hover:text-foreground\"\r\n onClick={() => {\r\n for (const track of trackMap.values()) compactTrack(track.id)\r\n }}\r\n >\r\n 轨道磁吸排序\r\n </button>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/cn\"\nimport { Tabs as TabsPrimitive } from \"radix-ui\"\n\nfunction Tabs({\n className,\n orientation = \"horizontal\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>) {\n return (\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n data-orientation={orientation}\n orientation={orientation}\n className={cn(\n \"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col\",\n className\n )}\n {...props}\n />\n )\n}\n\nconst tabsListVariants = cva(\n \"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none\",\n {\n variants: {\n variant: {\n default: \"bg-muted\",\n line: \"gap-1 bg-transparent\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction TabsList({\n className,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.List> &\n VariantProps<typeof tabsListVariants>) {\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n data-variant={variant}\n className={cn(tabsListVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nfunction TabsTrigger({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n return (\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n \"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n \"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent\",\n \"data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground\",\n \"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction TabsContent({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return (\n <TabsPrimitive.Content\n data-slot=\"tabs-content\"\n className={cn(\"flex-1 outline-none\", className)}\n {...props}\n />\n )\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Input }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon } from \"lucide-react\"\nimport { Select as SelectPrimitive } from \"radix-ui\"\nimport { usePortalContainer } from \"@/portal-container\"\n\nfunction Select({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Root>) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />\n}\n\nfunction SelectGroup({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nfunction SelectTrigger({\n className,\n size = \"default\",\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"default\"\n}) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon className=\"size-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n}\n\nfunction SelectContent({\n className,\n children,\n position = \"item-aligned\",\n align = \"center\",\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Content>) {\n // ⚠️ shadcn 生成物的必要结构修正:Portal 必须挂进 `.cw-root`,否则作用域化\n // utility 不生效(下拉菜单没有背景、出现在视口外)。原因详见 dialog.tsx 的 DialogPortal。\n const container = usePortalContainer()\n return (\n <SelectPrimitive.Portal container={container ?? undefined}>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n align={align}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Label>) {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(\"px-2 py-1.5 text-xs text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n \"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2\",\n className\n )}\n {...props}\n >\n <span\n data-slot=\"select-item-indicator\"\n className=\"absolute right-2 flex size-3.5 items-center justify-center\"\n >\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\nfunction SelectSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Separator>) {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(\"pointer-events-none -mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n {...props}\n >\n <ChevronUpIcon className=\"size-4\" />\n </SelectPrimitive.ScrollUpButton>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n {...props}\n >\n <ChevronDownIcon className=\"size-4\" />\n </SelectPrimitive.ScrollDownButton>\n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n","import type { ReactNode } from 'react'\nimport { cn } from '@/lib/cn'\nimport { Label } from '@/components/ui/label'\n\ninterface FieldProps {\n label: string\n hint?: string\n children: ReactNode\n className?: string\n htmlFor?: string\n}\n\n/** 属性面板的一行:标签 + 控件(标签在左,控件占满剩余宽度) */\nexport function Field({ label, hint, children, className, htmlFor }: FieldProps) {\n return (\n <div className={cn('flex items-center gap-2', className)}>\n <Label htmlFor={htmlFor} className=\"w-16 shrink-0 text-[11px] text-muted-foreground\">\n {label}\n </Label>\n <div className=\"min-w-0 flex-1\">{children}</div>\n {hint != null && <span className=\"text-[10px] text-muted-foreground\">{hint}</span>}\n </div>\n )\n}\n\ninterface SectionProps {\n title: string\n children: ReactNode\n action?: ReactNode\n className?: string\n}\n\n/** 属性分组 */\nexport function Section({ title, children, action, className }: SectionProps) {\n return (\n <section className={cn('space-y-2', className)}>\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">\n {title}\n </h3>\n {action}\n </div>\n <div className=\"space-y-2\">{children}</div>\n </section>\n )\n}\n\ninterface NumberFieldProps {\n label: string\n value: number\n min?: number\n max?: number\n step?: number\n suffix?: string\n onChange: (value: number) => void\n disabled?: boolean\n}\n\n/** 数字输入:onChange 立即生效,但通过 number 输入避免字符串中间态 */\nexport function NumberField({\n label,\n value,\n min,\n max,\n step = 1,\n suffix,\n onChange,\n disabled,\n}: NumberFieldProps) {\n return (\n <Field label={label} hint={suffix}>\n <input\n type=\"number\"\n className=\"h-7 w-full rounded-md border bg-transparent px-2 text-xs tabular-nums outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 disabled:opacity-50\"\n value={Number.isFinite(value) ? Math.round(value * 100) / 100 : 0}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n onChange={(e) => {\n const next = Number(e.target.value)\n if (Number.isFinite(next)) onChange(next)\n }}\n />\n </Field>\n )\n}\n\ninterface SliderFieldProps {\n label: string\n value: number\n min: number\n max: number\n step: number\n /** 显示值格式化 */\n format?: (value: number) => string\n onChange: (value: number) => void\n disabled?: boolean\n}\n\n/** 滑杆 + 数值展示 */\nexport function SliderField({\n label,\n value,\n min,\n max,\n step,\n format,\n onChange,\n disabled,\n}: SliderFieldProps) {\n return (\n <div className=\"space-y-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[11px] text-muted-foreground\">{label}</span>\n <span className=\"font-mono text-[10px] tabular-nums\">\n {format ? format(value) : value}\n </span>\n </div>\n <input\n type=\"range\"\n className=\"h-1 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary disabled:opacity-50\"\n min={min}\n max={max}\n step={step}\n value={value}\n disabled={disabled}\n onChange={(e) => onChange(Number(e.target.value))}\n aria-label={label}\n />\n </div>\n )\n}\n","import { FlipHorizontal2, FlipVertical2, Link2, Link2Off, RotateCcw } from 'lucide-react'\r\nimport type { TimelineClip } from '@cut-workbench/core'\r\nimport { Button } from '@/components/ui/button'\r\nimport { Input } from '@/components/ui/input'\r\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\r\nimport { formatSeconds } from '@/lib/format'\r\nimport { usToSec } from '@cut-workbench/core'\r\nimport { ratioLabel } from '@cut-workbench/core'\r\nimport { Field, NumberField, Section, SliderField } from './Field'\r\n\r\ninterface VideoPropsProps {\r\n clip: TimelineClip\r\n onPatch: (patch: Partial<TimelineClip>) => void\r\n onPatchTransform: (patch: Partial<TimelineClip['transform']>) => void\r\n /** 修改尺寸(自动处理宽高比锁定);未传时退化为 onPatchTransform */\r\n onPatchSize?: (axis: 'w' | 'h', value: number) => void\r\n onRebuild?: () => void\r\n}\r\n\r\n/**\r\n * 视频 / 图片共用的几何与外观属性。\r\n *\r\n * 变速会同时改变「时间轴可见时长」:为了让画面内容完整,变速时同步缩放 durationUs,\r\n * 保持源素材消费长度不变(duration = sourceSpan / rate)。\r\n */\r\nexport function VideoProps({ clip, onPatch, onPatchTransform, onPatchSize }: VideoPropsProps) {\r\n const handleRateChange = (rate: number) => {\r\n const safe = Math.max(0.1, Math.min(5, rate))\r\n const sourceSpan = Math.round(clip.durationUs * clip.playbackRate)\r\n onPatch({\r\n playbackRate: safe,\r\n durationUs: Math.max(100_000, Math.round(sourceSpan / safe)) as TimelineClip['durationUs'],\r\n })\r\n }\r\n\r\n const setSize = (axis: 'w' | 'h', value: number) => {\r\n if (onPatchSize) onPatchSize(axis, value)\r\n else onPatchTransform({ [axis]: Math.max(1, value) } as Partial<TimelineClip['transform']>)\r\n }\r\n const locked = clip.aspectLocked === true\r\n // 用整数比展示(640×360 → 16:9),比 1.778:1 直观\r\n const ratioText = ratioLabel(Math.round(clip.transform.w), Math.round(clip.transform.h))\r\n\r\n return (\r\n <>\r\n <Section title=\"位置与尺寸\">\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n <NumberField label=\"X\" value={clip.transform.x} onChange={(x) => onPatchTransform({ x })} />\r\n <NumberField label=\"Y\" value={clip.transform.y} onChange={(y) => onPatchTransform({ y })} />\r\n <NumberField\r\n label=\"宽\"\r\n value={clip.transform.w}\r\n min={1}\r\n onChange={(w) => setSize('w', w)}\r\n />\r\n <NumberField\r\n label=\"高\"\r\n value={clip.transform.h}\r\n min={1}\r\n onChange={(h) => setSize('h', h)}\r\n />\r\n </div>\r\n <div className=\"flex items-center justify-between\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={locked ? 'secondary' : 'ghost'}\r\n size=\"icon-xs\"\r\n onClick={() => onPatch({ aspectLocked: !locked })}\r\n aria-label={locked ? '解锁宽高比' : '锁定宽高比'}\r\n aria-pressed={locked}\r\n >\r\n {locked ? <Link2 className=\"size-3\" /> : <Link2Off className=\"size-3\" />}\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>\r\n {locked ? '已锁定宽高比:改宽会自动改高' : '锁定宽高比'}\r\n </TooltipContent>\r\n </Tooltip>\r\n <span className=\"font-mono text-[10px] text-muted-foreground tabular-nums\">\r\n {locked ? `已锁定 ${ratioText}` : '未锁定'}\r\n </span>\r\n </div>\r\n <SliderField\r\n label=\"旋转\"\r\n value={clip.transform.angle}\r\n min={-180}\r\n max={180}\r\n step={1}\r\n format={(v) => `${v}°`}\r\n onChange={(angle) => onPatchTransform({ angle })}\r\n />\r\n </Section>\r\n\r\n <Section\r\n title=\"外观\"\r\n action={\r\n <div className=\"flex gap-0.5\">\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={clip.flip === 'horizontal' ? 'secondary' : 'ghost'}\r\n size=\"icon-xs\"\r\n onClick={() =>\r\n onPatch({ flip: clip.flip === 'horizontal' ? null : 'horizontal' })\r\n }\r\n aria-label=\"水平翻转\"\r\n >\r\n <FlipHorizontal2 className=\"size-3\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>水平翻转</TooltipContent>\r\n </Tooltip>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant={clip.flip === 'vertical' ? 'secondary' : 'ghost'}\r\n size=\"icon-xs\"\r\n onClick={() => onPatch({ flip: clip.flip === 'vertical' ? null : 'vertical' })}\r\n aria-label=\"垂直翻转\"\r\n >\r\n <FlipVertical2 className=\"size-3\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>垂直翻转</TooltipContent>\r\n </Tooltip>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon-xs\"\r\n onClick={() => onPatchTransform({ angle: 0 })}\r\n aria-label=\"重置旋转\"\r\n >\r\n <RotateCcw className=\"size-3\" />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent>重置旋转</TooltipContent>\r\n </Tooltip>\r\n </div>\r\n }\r\n >\r\n <SliderField\r\n label=\"不透明度\"\r\n value={clip.opacity}\r\n min={0}\r\n max={1}\r\n step={0.01}\r\n format={(v) => `${Math.round(v * 100)}%`}\r\n onChange={(opacity) => onPatch({ opacity })}\r\n />\r\n </Section>\r\n\r\n <Section title=\"时间\">\r\n <Field label=\"开始\">\r\n <Input\r\n type=\"number\"\r\n className=\"h-7 text-xs tabular-nums\"\r\n value={formatSeconds(clip.startUs)}\r\n step={0.1}\r\n min={0}\r\n onChange={(e) => {\r\n const sec = Number(e.target.value)\r\n if (Number.isFinite(sec)) {\r\n onPatch({ startUs: Math.max(0, Math.round(sec * 1_000_000)) as TimelineClip['startUs'] })\r\n }\r\n }}\r\n />\r\n </Field>\r\n <Field label=\"时长\">\r\n <Input\r\n type=\"number\"\r\n className=\"h-7 text-xs tabular-nums\"\r\n value={formatSeconds(clip.durationUs)}\r\n step={0.1}\r\n min={0.1}\r\n onChange={(e) => {\r\n const sec = Number(e.target.value)\r\n if (Number.isFinite(sec) && sec > 0) {\r\n onPatch({ durationUs: Math.round(sec * 1_000_000) as TimelineClip['durationUs'] })\r\n }\r\n }}\r\n />\r\n </Field>\r\n <Field label=\"变速\" hint={clip.playbackRate !== 1 ? '会变调' : undefined}>\r\n <Select\r\n value={String(clip.playbackRate)}\r\n onValueChange={(value) => handleRateChange(Number(value))}\r\n >\r\n <SelectTrigger className=\"h-7 text-xs\">\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {[0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4].map((rate) => (\r\n <SelectItem key={rate} value={String(rate)} className=\"text-xs\">\r\n {rate}×\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </Field>\r\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\r\n 变速使用线性插值,音频音调会随之改变(WebAV 限制)。\r\n {usToSec(clip.durationUs).toFixed(2)}s 对应源素材{' '}\r\n {(usToSec(clip.durationUs) * clip.playbackRate).toFixed(2)}s。\r\n </p>\r\n </Section>\r\n </>\r\n )\r\n}\r\n","import { Volume2, VolumeX } from 'lucide-react'\nimport type { TimelineClip } from '@cut-workbench/core'\nimport { Button } from '@/components/ui/button'\nimport { Section, SliderField } from './Field'\nimport { formatSeconds } from '@/lib/format'\n\ninterface AudioPropsProps {\n clip: TimelineClip\n onPatch: (patch: Partial<TimelineClip>, mergeKey?: string) => void\n}\n\n/**\n * 音频属性:音量 / 静音 / 淡入淡出。\n *\n * 淡入淡出在预览里由 PreviewAudioEngine 的 GainNode 包络实现;\n * 导出时由 exporter 的 tickInterceptor 烘焙到 PCM(两条路径行为一致)。\n */\nexport function AudioProps({ clip, onPatch }: AudioPropsProps) {\n return (\n <>\n <Section\n title=\"音量\"\n action={\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={() => onPatch({ muted: !clip.muted })}\n aria-label={clip.muted ? '取消静音' : '静音'}\n >\n {clip.muted ? <VolumeX className=\"size-3\" /> : <Volume2 className=\"size-3\" />}\n </Button>\n }\n >\n <SliderField\n label=\"音量\"\n value={clip.muted ? 0 : clip.volume}\n min={0}\n max={1}\n step={0.01}\n format={(v) => `${Math.round(v * 100)}%`}\n onChange={(volume) => onPatch({ volume, muted: volume === 0 ? true : clip.muted })}\n />\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\n 多轨音频在合成时直接相加,建议每轨不超过 80% 以避免削波。\n </p>\n </Section>\n\n <Section title=\"淡入淡出\">\n <SliderField\n label=\"淡入\"\n value={clip.fadeInUs / 1_000_000}\n min={0}\n max={Math.max(0.5, clip.durationUs / 1_000_000 / 2)}\n step={0.05}\n format={(v) => `${v.toFixed(2)}s`}\n onChange={(sec) =>\n onPatch({ fadeInUs: Math.round(sec * 1_000_000) as TimelineClip['fadeInUs'] })\n }\n />\n <SliderField\n label=\"淡出\"\n value={clip.fadeOutUs / 1_000_000}\n min={0}\n max={Math.max(0.5, clip.durationUs / 1_000_000 / 2)}\n step={0.05}\n format={(v) => `${v.toFixed(2)}s`}\n onChange={(sec) =>\n onPatch({ fadeOutUs: Math.round(sec * 1_000_000) as TimelineClip['fadeOutUs'] })\n }\n />\n <p className=\"text-[10px] text-muted-foreground\">\n 片段时长 {formatSeconds(clip.durationUs)}s,淡入使用 setAnimation 线性插值。\n </p>\n </Section>\n </>\n )\n}\n","import type { TextStyle, TimelineClip } from '@cut-workbench/core'\r\nimport { Textarea } from '@/components/ui/textarea'\r\nimport { Input } from '@/components/ui/input'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select'\r\nimport { Field, NumberField, Section, SliderField } from './Field'\r\n\r\ninterface TextPropsProps {\r\n clip: TimelineClip\r\n onPatch: (patch: Partial<TimelineClip>) => void\r\n /** 文字内容或样式变化后需要重建 TextClip */\r\n onRebuild: () => void\r\n}\r\n\r\nconst FONT_FAMILIES = [\r\n { value: 'sans-serif', label: '无衬线' },\r\n { value: 'serif', label: '衬线' },\r\n { value: 'monospace', label: '等宽' },\r\n { value: \"'PingFang SC', sans-serif\", label: '苹方' },\r\n { value: \"'Microsoft YaHei', sans-serif\", label: '微软雅黑' },\r\n { value: \"'SimSun', serif\", label: '宋体' },\r\n]\r\n\r\n/** 文字素材属性:内容 / 字号 / 颜色 / 描边 / 阴影 / 对齐 */\r\nexport function TextProps({ clip, onPatch, onRebuild }: TextPropsProps) {\r\n const style: TextStyle = {\r\n fontSize: 64,\r\n color: '#ffffff',\r\n fontFamily: 'sans-serif',\r\n fontWeight: 600,\r\n fontStyle: 'normal',\r\n strokeColor: '#000000',\r\n strokeWidth: 0,\r\n shadowColor: 'rgba(0,0,0,0.6)',\r\n shadowBlur: 8,\r\n align: 'center',\r\n background: 'transparent',\r\n ...(clip.textStyle ?? {}),\r\n }\r\n\r\n const patchStyle = (next: Partial<TextStyle>) => {\r\n onPatch({ textStyle: { ...style, ...next } })\r\n onRebuild()\r\n }\r\n\r\n return (\r\n <>\r\n <Section title=\"内容\">\r\n <Textarea\r\n value={clip.text ?? ''}\r\n onChange={(e) => {\r\n onPatch({ text: e.target.value })\r\n onRebuild()\r\n }}\r\n rows={2}\r\n className=\"text-xs\"\r\n placeholder=\"输入文字…\"\r\n />\r\n </Section>\r\n\r\n <Section title=\"排版\">\r\n <Field label=\"字体\">\r\n <Select value={style.fontFamily} onValueChange={(v) => patchStyle({ fontFamily: v })}>\r\n <SelectTrigger className=\"h-7 text-xs\">\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {FONT_FAMILIES.map((font) => (\r\n <SelectItem key={font.value} value={font.value} className=\"text-xs\">\r\n {font.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </Field>\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n <NumberField\r\n label=\"字号\"\r\n value={style.fontSize}\r\n min={8}\r\n max={400}\r\n onChange={(fontSize) => patchStyle({ fontSize: Math.max(8, fontSize) })}\r\n />\r\n <NumberField\r\n label=\"粗细\"\r\n value={style.fontWeight}\r\n min={100}\r\n max={900}\r\n step={100}\r\n onChange={(fontWeight) => patchStyle({ fontWeight })}\r\n />\r\n </div>\r\n <SliderField\r\n label=\"对齐\"\r\n value={style.align === 'left' ? 0 : style.align === 'center' ? 1 : 2}\r\n min={0}\r\n max={2}\r\n step={1}\r\n format={(v) => (v === 0 ? '左' : v === 1 ? '中' : '右')}\r\n onChange={(v) => patchStyle({ align: v === 0 ? 'left' : v === 1 ? 'center' : 'right' })}\r\n />\r\n </Section>\r\n\r\n <Section title=\"颜色与描边\">\r\n <Field label=\"文字\">\r\n <ColorInput value={style.color} onChange={(color) => patchStyle({ color })} />\r\n </Field>\r\n <Field label=\"描边\">\r\n <ColorInput value={style.strokeColor} onChange={(strokeColor) => patchStyle({ strokeColor })} />\r\n </Field>\r\n <SliderField\r\n label=\"描边宽度\"\r\n value={style.strokeWidth}\r\n min={0}\r\n max={8}\r\n step={0.5}\r\n format={(v) => `${v}px`}\r\n onChange={(strokeWidth) => patchStyle({ strokeWidth })}\r\n />\r\n <Field label=\"背景\">\r\n <ColorInput\r\n value={style.background === 'transparent' ? '#000000' : style.background}\r\n onChange={(background) => patchStyle({ background })}\r\n withAlpha\r\n />\r\n </Field>\r\n </Section>\r\n\r\n <Section title=\"阴影\">\r\n <Field label=\"颜色\">\r\n <ColorInput\r\n value={style.shadowColor}\r\n onChange={(shadowColor) => patchStyle({ shadowColor })}\r\n />\r\n </Field>\r\n <SliderField\r\n label=\"模糊\"\r\n value={style.shadowBlur}\r\n min={0}\r\n max={40}\r\n step={1}\r\n format={(v) => `${v}px`}\r\n onChange={(shadowBlur) => patchStyle({ shadowBlur })}\r\n />\r\n </Section>\r\n </>\r\n )\r\n}\r\n\r\n/** 简易颜色输入(原生 color picker + 十六进制文本) */\r\nexport function ColorInput({\r\n value,\r\n onChange,\r\n withAlpha,\r\n}: {\r\n value: string\r\n onChange: (value: string) => void\r\n withAlpha?: boolean\r\n}) {\r\n void withAlpha\r\n const safeHex = /^#[0-9a-f]{6}$/i.test(value) ? value : '#ffffff'\r\n return (\r\n <div className=\"flex items-center gap-1.5\">\r\n <input\r\n type=\"color\"\r\n value={safeHex}\r\n onChange={(e) => onChange(e.target.value)}\r\n className=\"size-7 shrink-0 cursor-pointer rounded border bg-transparent p-0.5\"\r\n aria-label=\"选择颜色\"\r\n />\r\n <Input\r\n value={value}\r\n onChange={(e) => onChange(e.target.value)}\r\n className=\"h-7 font-mono text-[11px]\"\r\n aria-label=\"颜色值\"\r\n />\r\n </div>\r\n )\r\n}\r\n","import type { SubtitleStyle, TimelineClip } from '@cut-workbench/core'\r\nimport { Textarea } from '@/components/ui/textarea'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select'\r\nimport { formatSeconds } from '@/lib/format'\r\nimport { parseSrt } from '@cut-workbench/core'\r\nimport { Field, NumberField, Section, SliderField } from './Field'\r\nimport { ColorInput } from './TextProps'\r\n\r\ninterface SubtitlePropsProps {\r\n clip: TimelineClip\r\n onPatch: (patch: Partial<TimelineClip>) => void\r\n onRebuild: () => void\r\n}\r\n\r\nconst FONT_OPTIONS = [\r\n { value: 'sans-serif', label: '系统无衬线(推荐)' },\r\n { value: \"'Microsoft YaHei', sans-serif\", label: '微软雅黑' },\r\n { value: \"'PingFang SC', sans-serif\", label: '苹方' },\r\n { value: 'serif', label: '系统衬线' },\r\n]\r\n\r\n/**\r\n * 字幕属性(EmbedSubtitlesClip)。\r\n *\r\n * 注意:字号 / 字体 / 颜色等参数在 EmbedSubtitlesClip 构造时传入,修改后需要重建实例\r\n * (`onRebuild`),不会像普通 sprite 属性那样自动生效。\r\n */\r\nexport function SubtitleProps({ clip, onPatch, onRebuild }: SubtitlePropsProps) {\r\n const style: SubtitleStyle = {\r\n color: '#ffffff',\r\n textBgColor: null,\r\n fontFamily: 'sans-serif',\r\n fontSize: 30,\r\n strokeStyle: '#000000',\r\n lineWidth: null,\r\n bottomOffset: 30,\r\n ...(clip.subtitleStyle ?? {}),\r\n }\r\n\r\n const patchStyle = (next: Partial<SubtitleStyle>) => {\r\n onPatch({ subtitleStyle: { ...style, ...next } })\r\n onRebuild()\r\n }\r\n\r\n const cues = parseSrt(clip.srt ?? '')\r\n\r\n return (\r\n <>\r\n <Section title=\"SRT 内容\" action={<span className=\"text-[10px] text-muted-foreground\">{cues.length} 条</span>}>\r\n <Textarea\r\n value={clip.srt ?? ''}\r\n onChange={(e) => {\r\n onPatch({ srt: e.target.value })\r\n onRebuild()\r\n }}\r\n rows={7}\r\n className=\"font-mono text-[11px]\"\r\n placeholder={'1\\n00:00:00,000 --> 00:00:02,000\\n字幕内容'}\r\n />\r\n <p className=\"text-[10px] text-muted-foreground\">\r\n 片段时长 {formatSeconds(clip.durationUs)}s\r\n {cues.length > 0 && ` · 最后一条结束于 ${(cues[cues.length - 1]!.endMs / 1000).toFixed(2)}s`}\r\n </p>\r\n </Section>\r\n\r\n <Section title=\"样式\">\r\n <Field label=\"字体\">\r\n <Select value={style.fontFamily} onValueChange={(v) => patchStyle({ fontFamily: v })}>\r\n <SelectTrigger className=\"h-7 text-xs\">\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {FONT_OPTIONS.map((font) => (\r\n <SelectItem key={font.value} value={font.value} className=\"text-xs\">\r\n {font.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </Field>\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n <NumberField\r\n label=\"字号\"\r\n value={style.fontSize}\r\n min={8}\r\n max={200}\r\n onChange={(fontSize) => patchStyle({ fontSize: Math.max(8, fontSize) })}\r\n />\r\n <NumberField\r\n label=\"底距\"\r\n value={style.bottomOffset}\r\n min={0}\r\n max={600}\r\n onChange={(bottomOffset) => patchStyle({ bottomOffset: Math.max(0, bottomOffset) })}\r\n />\r\n </div>\r\n <Field label=\"文字色\">\r\n <ColorInput value={style.color} onChange={(color) => patchStyle({ color })} />\r\n </Field>\r\n <Field label=\"描边色\">\r\n <ColorInput value={style.strokeStyle} onChange={(strokeStyle) => patchStyle({ strokeStyle })} />\r\n </Field>\r\n <SliderField\r\n label=\"描边宽度\"\r\n value={style.lineWidth ?? 0}\r\n min={0}\r\n max={8}\r\n step={1}\r\n format={(v) => (v === 0 ? '无' : `${v}px`)}\r\n onChange={(lineWidth) => patchStyle({ lineWidth: lineWidth === 0 ? null : lineWidth })}\r\n />\r\n <Field label=\"底色\">\r\n <ColorInput\r\n value={style.textBgColor ?? '#000000'}\r\n onChange={(v) => patchStyle({ textBgColor: v })}\r\n />\r\n </Field>\r\n {style.textBgColor != null && (\r\n <button\r\n type=\"button\"\r\n className=\"text-[10px] text-primary hover:underline\"\r\n onClick={() => patchStyle({ textBgColor: null })}\r\n >\r\n 移除字幕底色\r\n </button>\r\n )}\r\n </Section>\r\n </>\r\n )\r\n}\r\n","import type { MediaAsset, TimelineClip } from '@cut-workbench/core'\nimport { SliderField, Section } from './Field'\nimport { VideoProps } from './VideoProps'\n\ninterface ImagePropsProps {\n clip: TimelineClip\n asset: MediaAsset | undefined\n onPatch: (patch: Partial<TimelineClip>, mergeKey?: string) => void\n onPatchTransform: (patch: Partial<TimelineClip['transform']>, field?: string) => void\n onPatchSize: (axis: 'w' | 'h', value: number) => void\n}\n\n/**\n * 图片属性 = 视频几何属性 + 静态素材专属的时长控制。\n *\n * ⚠️ ImgClip 的 meta.duration 是 Infinity,时间轴 clip 的 durationUs 是唯一的时长来源,\n * 所以这里必须能显式设置时长(否则 Combinator 无法确定结束时间)。\n */\nexport function ImageProps({ clip, asset, onPatch, onPatchTransform, onPatchSize }: ImagePropsProps) {\n return (\n <>\n <VideoProps\n clip={clip}\n onPatch={onPatch}\n onPatchTransform={onPatchTransform}\n onPatchSize={onPatchSize}\n />\n <Section title=\"静态素材\">\n <SliderField\n label=\"显示时长\"\n value={clip.durationUs / 1_000_000}\n min={0.5}\n max={60}\n step={0.5}\n format={(v) => `${v.toFixed(1)}s`}\n onChange={(sec) =>\n onPatch({ durationUs: Math.round(sec * 1_000_000) as TimelineClip['durationUs'] })\n }\n />\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\n {asset?.kind === 'image'\n ? '静态图片在 WebAV 中的 duration 为 Infinity,导出时必须由时间轴显式指定有限时长。'\n : '静态素材需要有限时长,否则 Combinator 无法推断结束时间。'}\n </p>\n </Section>\n </>\n )\n}\n","import { useCallback } from 'react'\r\nimport type { TimelineClip } from '@cut-workbench/core'\r\nimport { useEditor } from '@cut-workbench/react'\r\n\r\n/**\r\n * 属性面板的写操作集合。\r\n *\r\n * 所有修改都走 store 的 `updateClip`,由 preview 的「属性变化 → 重绘」路径生效;\r\n * 对需要重建 IClip 的属性(文字内容、SRT、入点),调用方额外触发 rebuild。\r\n *\r\n * **撤销粒度**:属性面板的滑块 / 数字输入会连续触发 onChange(一次拖动可能几十次)。\r\n * 若每次都压栈,用户按 Ctrl+Z 只会看到数值回退一小格,感觉「撤销不生效」。\r\n * 因此这里给每个字段生成合并键,让「同一字段的连续修改」合并成一步撤销。\r\n *\r\n * 字段名从 `patch` 的 key 自动推导,调用方无需关心。\r\n */\r\nexport function useClipInspector(clip: TimelineClip | null) {\r\n const updateClip = useEditor((s) => s.updateClip)\r\n\r\n /**\r\n * 修改片段顶层字段。\r\n *\r\n * 合并键按「片段 + 字段」生成;`transform` 这类嵌套对象再带上子字段名,\r\n * 保证「拖 X 再拖 Y」是两步,而「连续拖 X」是一步。\r\n */\r\n const patch = useCallback(\r\n (next: Partial<TimelineClip>) => {\r\n if (clip == null) return\r\n const keys = Object.keys(next)\r\n const field = keys.length === 1 ? keys[0] : keys.join('+')\r\n const nested =\r\n keys.length === 1 && next.transform != null\r\n ? `:${Object.keys(next.transform).join('+')}`\r\n : ''\r\n updateClip(clip.id, next, true, `${clip.id}:${field}${nested}`)\r\n },\r\n [clip, updateClip],\r\n )\r\n\r\n /** 修改 transform 字段(x / y / w / h / angle) */\r\n const patchTransform = useCallback(\r\n (next: Partial<TimelineClip['transform']>) => {\r\n if (clip == null) return\r\n const field = Object.keys(next).join('+')\r\n updateClip(\r\n clip.id,\r\n { transform: { ...clip.transform, ...next } },\r\n true,\r\n `${clip.id}:transform:${field}`,\r\n )\r\n },\r\n [clip, updateClip],\r\n )\r\n\r\n /**\r\n * 修改尺寸,尊重「锁定宽高比」。\r\n *\r\n * 锁定后:改宽 → 高按原比例跟随;改高 → 宽按原比例跟随。\r\n * 比例取**当前** transform 的 w/h(而不是素材原始分辨率),这样用户先自由\r\n * 改成 4:3、再上锁,后续锁定的是 4:3。\r\n *\r\n * @param axis 用户手动改的那个维度\r\n * @param value 新值(像素,已由调用方 clamp)\r\n */\r\n const patchSize = useCallback(\r\n (axis: 'w' | 'h', value: number) => {\r\n if (clip == null) return\r\n const safe = Math.max(1, value)\r\n const locked = clip.aspectLocked === true\r\n if (!locked || clip.transform.w <= 0 || clip.transform.h <= 0) {\r\n patchTransform({ [axis]: safe } as Partial<TimelineClip['transform']>)\r\n return\r\n }\r\n const ratio = clip.transform.w / clip.transform.h\r\n if (axis === 'w') {\r\n patchTransform({ w: safe, h: Math.max(1, Math.round(safe / ratio)) })\r\n } else {\r\n patchTransform({ h: safe, w: Math.max(1, Math.round(safe * ratio)) })\r\n }\r\n },\r\n [clip, patchTransform],\r\n )\r\n\r\n return { patch, patchTransform, patchSize }\r\n}\r\n","import { useCallback, useMemo } from 'react'\r\nimport { Film, Info, Music, Settings2, Subtitles, Type } from 'lucide-react'\r\nimport type { MediaAsset, TimelineClip } from '@cut-workbench/core'\r\nimport { getTrack } from '@cut-workbench/core'\r\nimport { formatDuration, formatResolution } from '@/lib/format'\r\nimport { ScrollArea } from '@/components/ui/scroll-area'\r\nimport { Separator } from '@/components/ui/separator'\r\nimport { Badge } from '@/components/ui/badge'\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'\r\nimport { EmptyState } from '@/components/common/EmptyState'\r\nimport { Timecode } from '@/components/common/Timecode'\r\nimport { VideoProps } from './VideoProps'\r\nimport { AudioProps } from './AudioProps'\r\nimport { TextProps } from './TextProps'\r\nimport { SubtitleProps } from './SubtitleProps'\r\nimport { ImageProps } from './ImageProps'\r\nimport { useClipInspector } from '../hooks/useClipInspector'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\n\r\nconst KIND_ICON = {\r\n video: Film,\r\n audio: Music,\r\n image: Film,\r\n text: Type,\r\n subtitle: Subtitles,\r\n} as const\r\n\r\nconst KIND_LABEL = {\r\n video: '视频',\r\n audio: '音频',\r\n image: '图片',\r\n text: '文字',\r\n subtitle: '字幕',\r\n} as const\r\n\r\nfunction useSelected(): { clip: TimelineClip | null; asset: MediaAsset | undefined; multi: boolean } {\r\n const selectedIds = useEditor((s) => s.selectedClipIds)\r\n const clips = useEditor((s) => s.project.clips)\r\n const assets = useEditor((s) => s.assets)\r\n return useMemo(() => {\r\n if (selectedIds.length !== 1) {\r\n return { clip: null, asset: undefined, multi: selectedIds.length > 1 }\r\n }\r\n const id = selectedIds[0]\r\n const clip = clips.find((c) => c.id === id) ?? null\r\n const asset = clip ? assets.find((a) => a.id === clip.assetId) : undefined\r\n return { clip, asset, multi: false }\r\n }, [assets, clips, selectedIds])\r\n}\r\n\r\n/**\r\n * 属性面板:根据选中 clip 的素材类型切换编辑表单。\r\n *\r\n * 所有修改通过 `updateClip` 写入;需要重建 IClip 的类型(文字 / 字幕)额外调用 rebuild。\r\n */\r\nexport function InspectorPanel() {\r\n const clips = useCutEditor().runtime.clips\r\n const { clip, asset, multi } = useSelected()\r\n const project = useEditor((s) => s.project)\r\n const { patch, patchTransform, patchSize } = useClipInspector(clip)\r\n\r\n const rebuild = useCallback(() => {\r\n if (clip == null || asset == null) return\r\n void clips.createInstance({\r\n timelineClipId: clip.id,\r\n assetId: clip.assetId,\r\n inPointUs: clip.inPointUs,\r\n durationUs: clip.durationUs * clip.playbackRate,\r\n text: clip.text,\r\n textStyle: clip.textStyle,\r\n srt: clip.srt,\r\n subtitleStyle: clip.subtitleStyle,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n })\r\n .catch((err: unknown) => {\r\n console.warn('[Inspector] rebuild failed', err)\r\n })\r\n }, [asset, clip, project.height, project.width])\r\n\r\n if (multi) {\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <PanelHeader title=\"多选\" />\r\n <EmptyState\r\n icon={Settings2}\r\n title=\"已选中多个片段\"\r\n description=\"多选状态下暂不支持批量编辑属性;可执行分割、删除、复制等操作。\"\r\n compact\r\n />\r\n </div>\r\n )\r\n }\r\n\r\n if (clip == null || asset == null) {\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <PanelHeader title=\"属性\" />\r\n <EmptyState\r\n icon={Settings2}\r\n title=\"未选中片段\"\r\n description=\"在时间轴或预览画布中选中一个片段以编辑属性\"\r\n compact\r\n />\r\n </div>\r\n )\r\n }\r\n\r\n const track = getTrack(project, clip.trackId)\r\n const Icon = KIND_ICON[asset.kind]\r\n\r\n return (\r\n <div className=\"flex min-h-0 flex-1 flex-col\">\r\n <PanelHeader title=\"属性\" />\r\n\r\n <div className=\"shrink-0 space-y-1.5 border-b px-3 py-2.5\">\r\n <div className=\"flex items-center gap-2\">\r\n <Icon className=\"size-3.5 text-muted-foreground\" />\r\n <p className=\"min-w-0 flex-1 truncate text-xs font-medium\" title={asset.name}>\r\n {asset.name}\r\n </p>\r\n <Badge variant=\"outline\" className=\"text-[10px]\">\r\n {KIND_LABEL[asset.kind]}\r\n </Badge>\r\n </div>\r\n <div className=\"flex items-center justify-between text-[10px] text-muted-foreground\">\r\n <span>{track?.name ?? '—'}</span>\r\n <span className=\"font-mono tabular-nums\">\r\n <Timecode us={clip.startUs} /> → <Timecode us={clip.startUs + clip.durationUs} />\r\n </span>\r\n </div>\r\n <div className=\"flex items-center justify-between text-[10px] text-muted-foreground\">\r\n <span>\r\n {formatDuration(clip.durationUs)} · {formatResolution(asset.width, asset.height)}\r\n </span>\r\n <span>入点 {(clip.inPointUs / 1_000_000).toFixed(2)}s</span>\r\n </div>\r\n </div>\r\n\r\n <ScrollArea className=\"thin-scrollbar min-h-0 flex-1\">\r\n <div className=\"space-y-4 px-3 py-3\">\r\n {asset.kind === 'audio' || (track?.kind === 'audio' && asset.kind !== 'text') ? (\r\n <Tabs defaultValue=\"audio\">\r\n <TabsList className=\"w-full\">\r\n <TabsTrigger value=\"audio\" className=\"flex-1 text-xs\">\r\n 音频\r\n </TabsTrigger>\r\n <TabsTrigger value=\"time\" className=\"flex-1 text-xs\">\r\n 位置\r\n </TabsTrigger>\r\n </TabsList>\r\n <TabsContent value=\"audio\" className=\"space-y-4 pt-3\">\r\n <AudioProps clip={clip} onPatch={patch} />\r\n </TabsContent>\r\n <TabsContent value=\"time\" className=\"space-y-4 pt-3\">\r\n <VideoProps clip={clip} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n <AudioProps clip={clip} onPatch={patch} />\r\n </TabsContent>\r\n </Tabs>\r\n ) : asset.kind === 'text' ? (\r\n <>\r\n <TextProps clip={clip} onPatch={patch} onRebuild={rebuild} />\r\n <Separator />\r\n <VideoProps clip={clip} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n </>\r\n ) : asset.kind === 'subtitle' ? (\r\n <SubtitleProps clip={clip} onPatch={patch} onRebuild={rebuild} />\r\n ) : asset.kind === 'image' ? (\r\n <ImageProps clip={clip} asset={asset} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n ) : (\r\n <VideoProps clip={clip} onPatch={patch} onPatchTransform={patchTransform} onPatchSize={patchSize} />\r\n )}\r\n\r\n <Separator />\r\n <div className=\"flex items-start gap-1.5 text-[10px] leading-relaxed text-muted-foreground\">\r\n <Info className=\"mt-0.5 size-3 shrink-0\" />\r\n <span>\r\n 属性修改实时反映到预览。导出时以时间轴数据为准,预览与导出使用同一套\r\n Combinator 参数。\r\n </span>\r\n </div>\r\n </div>\r\n </ScrollArea>\r\n </div>\r\n )\r\n}\r\n\r\nfunction PanelHeader({ title }: { title: string }) {\r\n return (\r\n <div className=\"flex h-9 shrink-0 items-center border-b px-3\">\r\n <h2 className=\"text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\r\n {title}\r\n </h2>\r\n </div>\r\n )\r\n}\r\n","\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Switch as SwitchPrimitive } from \"radix-ui\"\n\nfunction Switch({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof SwitchPrimitive.Root> & {\n size?: \"sm\" | \"default\"\n}) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n data-size={size}\n className={cn(\n \"peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80\",\n className\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground\"\n )}\n />\n </SwitchPrimitive.Root>\n )\n}\n\nexport { Switch }\n","import * as React from \"react\"\nimport { cn } from \"@/lib/cn\"\nimport { Progress as ProgressPrimitive } from \"radix-ui\"\n\nfunction Progress({\n className,\n value,\n ...props\n}: React.ComponentProps<typeof ProgressPrimitive.Root>) {\n return (\n <ProgressPrimitive.Root\n data-slot=\"progress\"\n className={cn(\n \"relative h-2 w-full overflow-hidden rounded-full bg-primary/20\",\n className\n )}\n {...props}\n >\n <ProgressPrimitive.Indicator\n data-slot=\"progress-indicator\"\n className=\"h-full w-full flex-1 bg-primary transition-all\"\n style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n />\n </ProgressPrimitive.Root>\n )\n}\n\nexport { Progress }\n","/**\n * 导出 Worker 的 URL(包产物形态)。\n *\n * 为什么单独抽一个模块:UMD 构建会把 Worker 内联成字符串,不需要也不应该\n * 保留随包的 worker 文件;而 ESM / CJS 产物需要一个**相对于 dist/index.js**\n * 的 URL。两者靠 `build-umd.mjs` 的 alias 把本模块换成 `worker-url.umd.ts`\n * 来切换,这样 UMD 里连 `new URL(...)` 都不会被求值。\n *\n * `?worker&url` 让 Vite 把 Worker 作为独立 chunk 产出,并给出它的 URL;\n * 配合 `base: './'`,ESM 产物里是 `new URL(\"assets/export.worker-xxx.js\", import.meta.url).href`、\n * CJS 产物里是 `pathToFileURL(__dirname + \"/assets/...\")` —— 装进 node_modules 后\n * 都能正确解析。若用默认的绝对 base,产物会是 `\"/assets/...\"`,解析到使用方\n * 站点的根目录,Worker 必然 404。\n */\nimport workerUrl from './export.worker.ts?worker&url'\n\nexport const exportWorkerUrl: string = workerUrl\n","/**\n * 导出 Worker 工厂(构建产物形态相关)。\n *\n * 同一个导出流程要跑在两种产物里:\n * - **ESM / CJS**:Worker 是独立 chunk,URL 由 `worker-url.ts` 在构建期给出;\n * - **UMD 单文件**:没有独立 chunk,`import.meta.url` 也不可靠,必须内联成\n * base64 blob URL。\n *\n * 这里用构建期常量 `__CUT_INLINE_WORKER__` 二选一,避免运行时探测:\n * 探测(例如判断是否 UMD)在打包后往往会得到错误结论,而且两种路径的\n * 打包行为本身不同 —— Worker 是否被抽取成独立 chunk 是构建期决定的。\n *\n * 接入方还可以通过 `HostBridge.createExportWorker` 完全接管(例如 Electron\n * 里换成 utilityProcess,或严格 CSP 下换成自定义 URL)。\n */\n\ndeclare const __CUT_INLINE_WORKER__: boolean\n\nimport { exportWorkerUrl } from './worker-url'\nimport type { HostBridge } from '@cut-workbench/core'\n\n/** 内联 Worker 的源码(仅内联模式注入) */\ndeclare const __CUT_WORKER_SOURCE__: string | undefined\n\nlet cachedBlobUrl: string | null = null\n\n/**\n * 创建导出 Worker。\n *\n * @throws 内联模式下缺失 Worker 源码时抛出(构建配置错误,应在构建期暴露)\n */\nexport function createExportWorker(bridge: HostBridge): Worker {\n // 1. 宿主完全接管\n const custom = bridge.createExportWorker?.()\n if (custom != null) return custom\n\n // 2. 内联模式(UMD)\n if (typeof __CUT_INLINE_WORKER__ !== 'undefined' && __CUT_INLINE_WORKER__) {\n if (typeof __CUT_WORKER_SOURCE__ === 'undefined' || __CUT_WORKER_SOURCE__ === '') {\n throw new Error('[cut-workbench] 内联 Worker 模式缺少 Worker 源码,请检查构建配置')\n }\n cachedBlobUrl ??= URL.createObjectURL(\n new Blob([__CUT_WORKER_SOURCE__], { type: 'text/javascript' }),\n )\n return new Worker(cachedBlobUrl, { name: 'cut-workbench-export' })\n }\n\n // 3. 默认:独立 chunk(`exportWorkerUrl` 在构建期已按包内 dist 目录解析成绝对 URL)\n return new Worker(exportWorkerUrl, {\n type: 'module',\n name: 'cut-workbench-export',\n })\n}\n\n/** 释放缓存的 blob URL(dispose 时调用) */\nexport function releaseExportWorker(): void {\n if (cachedBlobUrl == null) return\n URL.revokeObjectURL(cachedBlobUrl)\n cachedBlobUrl = null\n}\n","/**\r\n * 导出编排:构建 payload、派发 Worker、回退主线程、下载与修复时长。\r\n *\r\n * 分层:本模块在 features 层,不直接 import `@webav/*`;\r\n * WebAV 调用全部经 `@/core/webav/exporter` 与 Worker runtime。\r\n *\r\n * 两条路径的选择:\r\n * - 默认走 Worker(不阻塞 UI);\r\n * - 含文字片段时回退主线程(文字渲染依赖 DOM);\r\n * - 含音频 / 视频音轨时,主线程会先解码 PCM 再传给 Worker\r\n * (Worker 内没有 AudioContext,AudioClip 无法自行解码)。\r\n */\r\n\r\nimport {\r\n checkExportSupport,\r\n exportProject,\r\n type ExportClipInput,\r\n} from '@cut-workbench/core/engine'\r\nimport { loadPcmForAsset } from '@cut-workbench/core/engine'\r\nimport type { ClipRegistry } from '@cut-workbench/core/engine'\r\nimport type {\r\n WorkerAssetPayload,\r\n WorkerResponse,\r\n WorkerTimelinePayload,\r\n} from '@cut-workbench/core/engine'\r\nimport type { ExportSettings, MediaAsset, Project } from '@cut-workbench/core'\r\n\r\nexport interface ExportPlanOptions {\r\n /** clip 实例注册表(由调用方从编辑器 runtime 取,避免本模块依赖 React) */\r\n registry: ClipRegistry\r\n /**\r\n * Worker 工厂。由调用方注入(通常来自 `editor.bridge`),\r\n * 使本模块不依赖具体宿主与构建形态。\r\n */\r\n createWorker: () => Worker\r\n project: Project\r\n assets: MediaAsset[]\r\n settings: ExportSettings\r\n /** 导出上限(微秒),默认全片 */\r\n maxTimeUs?: number\r\n}\r\n\r\nexport interface ExportResult {\r\n blob: Blob\r\n elapsedMs: number\r\n /** 走的是 Worker 还是主线程回退 */\r\n mode: 'worker' | 'main'\r\n skipped: Array<{ clipId: string; reason: string }>\r\n}\r\n\r\n/** 判断是否可以用 Worker 导出(文字素材依赖 DOM) */\r\nexport function canUseWorker(project: Project, assets: MediaAsset[]): boolean {\r\n const kinds = new Map(assets.map((a) => [a.id, a.kind]))\r\n return !project.clips.some((clip) => kinds.get(clip.assetId) === 'text')\r\n}\r\n\r\n/**\r\n * 构建 Worker 载荷。\r\n *\r\n * 音频素材需要主线程预解码 PCM(Worker 无 AudioContext)。为避免无谓解码,\r\n * 只有「音频轨素材」或「含音轨且会被播放的视频」才解码。\r\n */\r\nexport async function buildWorkerPayload(\r\n registry: ClipRegistry,\r\n project: Project,\r\n assets: MediaAsset[],\r\n settings: ExportSettings,\r\n maxTimeUs?: number,\r\n): Promise<{ assets: WorkerAssetPayload[]; timeline: WorkerTimelinePayload; missing: string[] }> {\r\n const usedAssetIds = new Set(project.clips.map((c) => c.assetId))\r\n const trackKindOf = new Map(project.tracks.map((t) => [t.id, t.kind]))\r\n const clipKinds = new Map<string, 'visual' | 'audio'>()\r\n for (const clip of project.clips) {\r\n clipKinds.set(clip.assetId, trackKindOf.get(clip.trackId) ?? 'visual')\r\n }\r\n\r\n const missing: string[] = []\r\n const payloadAssets: WorkerAssetPayload[] = []\r\n\r\n for (const asset of assets) {\r\n if (!usedAssetIds.has(asset.id)) continue\r\n const file = registry.getFile(asset.id)\r\n if (file == null && asset.kind !== 'text' && asset.kind !== 'subtitle') {\r\n missing.push(asset.name)\r\n continue\r\n }\r\n\r\n let pcm: Float32Array[] | undefined\r\n const needsAudio =\r\n settings.includeAudio &&\r\n (asset.kind === 'audio' || (asset.kind === 'video' && asset.hasAudio))\r\n if (needsAudio) {\r\n pcm = await loadPcmForAsset(registry, asset.id)\r\n }\r\n\r\n payloadAssets.push({\r\n id: asset.id,\r\n kind: asset.kind,\r\n file,\r\n width: asset.width,\r\n height: asset.height,\r\n durationUs: asset.durationUs,\r\n hasAudio: asset.hasAudio,\r\n pcm,\r\n text: asset.text,\r\n textStyle: asset.textStyle,\r\n srt: asset.srt,\r\n subtitleStyle: asset.subtitleStyle,\r\n })\r\n }\r\n\r\n const kindOf = new Map(assets.map((a) => [a.id, a.kind]))\r\n const clips = project.clips.map((clip) => ({\r\n clip,\r\n kind: kindOf.get(clip.assetId) ?? 'video',\r\n }))\r\n\r\n return {\r\n assets: payloadAssets,\r\n timeline: {\r\n width: settings.width,\r\n height: settings.height,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n fps: settings.fps,\r\n bitrate: settings.bitrate,\r\n includeAudio: settings.includeAudio,\r\n bgColor: '#000000',\r\n maxTimeUs,\r\n clips,\r\n },\r\n missing,\r\n }\r\n}\r\n\r\n/**\r\n * 主线程回退导出:为每个 clip 从 registry 建实例后交给 core exporter。\r\n *\r\n * 注意:实例用 `export:<clipId>` 作为临时 key 注册进 registry,导出结束后统一销毁,\r\n * 避免污染预览用的实例(两者不可共用,因为共享 tick 状态)。\r\n */\r\nasync function exportOnMainThread(\r\n registry: ClipRegistry,\r\n project: Project,\r\n settings: ExportSettings,\r\n onProgress: (progress: number) => void,\r\n shouldCancel: () => boolean,\r\n): Promise<ExportResult> {\r\n const trackMap = new Map(project.tracks.map((t) => [t.id, t]))\r\n const inputs: ExportClipInput[] = []\r\n const skipped: Array<{ clipId: string; reason: string }> = []\r\n const tempIds: string[] = []\r\n\r\n try {\r\n for (const clip of project.clips) {\r\n if (shouldCancel()) throw new Error('导出已取消')\r\n const track = trackMap.get(clip.trackId)\r\n if (!track || track.hidden) continue\r\n const asset = registry.getAsset(clip.assetId)\r\n if (!asset) {\r\n skipped.push({ clipId: clip.id, reason: '素材缺失' })\r\n continue\r\n }\r\n const tempId = `export:${clip.id}`\r\n try {\r\n const instance = await registry.createInstance({\r\n timelineClipId: tempId,\r\n assetId: clip.assetId,\r\n inPointUs: clip.inPointUs,\r\n durationUs: clip.durationUs * clip.playbackRate,\r\n text: clip.text,\r\n textStyle: clip.textStyle,\r\n srt: clip.srt,\r\n subtitleStyle: clip.subtitleStyle,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n })\r\n tempIds.push(tempId)\r\n inputs.push({ clip, instance, kind: asset.kind, track })\r\n } catch (err) {\r\n skipped.push({\r\n clipId: clip.id,\r\n reason: err instanceof Error ? err.message : String(err),\r\n })\r\n }\r\n }\r\n\r\n if (inputs.length === 0) throw new Error('时间轴为空,无法导出')\r\n\r\n const startedAt = performance.now()\r\n const { buffer, skipped: buildSkipped } = await exportProject(\r\n inputs,\r\n {\r\n width: settings.width,\r\n height: settings.height,\r\n fps: settings.fps,\r\n bitrate: settings.bitrate,\r\n includeAudio: settings.includeAudio,\r\n projectWidth: project.width,\r\n projectHeight: project.height,\r\n },\r\n onProgress,\r\n )\r\n const blob = new Blob([buffer], { type: 'video/mp4' })\r\n return {\r\n blob,\r\n elapsedMs: performance.now() - startedAt,\r\n mode: 'main',\r\n skipped: [...skipped, ...buildSkipped],\r\n }\r\n } finally {\r\n for (const id of tempIds) registry.destroyInstance(id)\r\n }\r\n}\r\n\r\nexport interface ExportHandle {\r\n promise: Promise<ExportResult>\r\n cancel: () => void\r\n}\r\n\r\n/**\r\n * 启动导出。\r\n *\r\n * 优先走 Worker(不阻塞 UI);含文字片段或 Worker 失败时回退主线程。\r\n */\r\nexport function startExport(\r\n options: ExportPlanOptions,\r\n onProgress: (progress: number) => void,\r\n): ExportHandle {\r\n const { registry, createWorker, project, assets, settings, maxTimeUs } = options\r\n let cancelled = false\r\n let worker: Worker | null = null\r\n\r\n const cancel = () => {\r\n cancelled = true\r\n if (worker != null) {\r\n worker.postMessage({ type: 'cancel' })\r\n worker.terminate()\r\n worker = null\r\n }\r\n }\r\n\r\n const promise = (async (): Promise<ExportResult> => {\r\n const supported = await checkExportSupport({\r\n width: settings.width,\r\n height: settings.height,\r\n bitrate: settings.bitrate,\r\n })\r\n if (!supported) {\r\n throw new Error(\r\n `当前设备不支持编码 ${settings.width}×${settings.height} @ ${(settings.bitrate / 1e6).toFixed(1)}Mbps,请降低分辨率或码率`,\r\n )\r\n }\r\n\r\n if (!canUseWorker(project, assets)) {\r\n return exportOnMainThread(registry, project, settings, onProgress, () => cancelled)\r\n }\r\n\r\n try {\r\n return await exportViaWorker(\r\n createWorker,\r\n registry,\r\n project,\r\n assets,\r\n settings,\r\n maxTimeUs,\r\n onProgress,\r\n () => cancelled,\r\n (w) => {\r\n worker = w\r\n },\r\n )\r\n } catch (err) {\r\n if (cancelled) throw new Error('导出已取消', { cause: err })\r\n console.warn('[export] Worker 导出失败,回退主线程', err)\r\n return exportOnMainThread(registry, project, settings, onProgress, () => cancelled)\r\n }\r\n })()\r\n\r\n return { promise, cancel }\r\n}\r\n\r\nasync function exportViaWorker(\r\n createWorker: () => Worker,\r\n registry: ClipRegistry,\r\n project: Project,\r\n assets: MediaAsset[],\r\n settings: ExportSettings,\r\n maxTimeUs: number | undefined,\r\n onProgress: (progress: number) => void,\r\n shouldCancel: () => boolean,\r\n register: (worker: Worker) => void,\r\n): Promise<ExportResult> {\r\n const { assets: payloadAssets, timeline, missing } = await buildWorkerPayload(\r\n registry,\r\n project,\r\n assets,\r\n settings,\r\n maxTimeUs,\r\n )\r\n if (missing.length > 0) {\r\n throw new Error(`以下素材缺少源文件,无法导出:${missing.join('、')}`)\r\n }\r\n\r\n return new Promise<ExportResult>((resolve, reject) => {\r\n const worker = createWorker()\r\n register(worker)\r\n const startedAt = performance.now()\r\n\r\n worker.onmessage = (event: MessageEvent<WorkerResponse>) => {\r\n const message = event.data\r\n if (message.type === 'progress') {\r\n onProgress(message.progress)\r\n return\r\n }\r\n if (message.type === 'done') {\r\n const blob = new Blob([message.buffer], { type: 'video/mp4' })\r\n worker.terminate()\r\n resolve({\r\n blob,\r\n elapsedMs: performance.now() - startedAt,\r\n mode: 'worker',\r\n skipped: message.skipped ?? [],\r\n })\r\n return\r\n }\r\n if (message.type === 'error') {\r\n worker.terminate()\r\n reject(new Error(message.message))\r\n }\r\n }\r\n\r\n worker.onerror = (event) => {\r\n worker.terminate()\r\n reject(new Error(event.message || '导出 Worker 异常'))\r\n }\r\n\r\n if (shouldCancel()) {\r\n worker.terminate()\r\n reject(new Error('导出已取消'))\r\n return\r\n }\r\n\r\n worker.postMessage({ type: 'export', assets: payloadAssets, timeline, fixDuration: true })\r\n })\r\n}\r\n\r\n/** 触发下载 */\r\nexport function downloadBlob(blob: Blob, filename: string): void {\r\n const url = URL.createObjectURL(blob)\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n document.body.appendChild(link)\r\n link.click()\r\n link.remove()\r\n // 立即 revoke 会让部分浏览器来不及下载,延迟释放\r\n window.setTimeout(() => URL.revokeObjectURL(url), 10_000)\r\n}\r\n\r\nexport function defaultFilename(): string {\r\n const now = new Date()\r\n const pad = (n: number) => String(n).padStart(2, '0')\r\n return `cut-test-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.mp4`\r\n}\r\n","import { useCallback, useRef, useState } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport { createExportWorker } from '../worker-factory'\r\nimport type { ExportSettings, ExportPreset, MediaAsset, Project } from '@cut-workbench/core'\r\nimport { DEFAULT_BITRATE } from '@/lib/constants'\r\nimport { computeTimelineEnd } from '@cut-workbench/core/engine'\r\nimport { fixFMP4DurationStream } from '@cut-workbench/core/engine'\r\nimport {\r\n defaultFilename,\r\n downloadBlob,\r\n startExport,\r\n type ExportResult,\r\n} from '../export-runner'\r\n\r\nexport type ExportPhase = 'idle' | 'preparing' | 'running' | 'done' | 'error' | 'cancelled'\r\n\r\nexport interface UseExportState {\r\n phase: ExportPhase\r\n progress: number\r\n error: string | null\r\n result: ExportResult | null\r\n /** 正在修复时长 */\r\n fixing: boolean\r\n}\r\n\r\nexport interface ExportConfigInput {\r\n preset: ExportPreset\r\n fps: ExportSettings['fps']\r\n bitrate: number\r\n includeAudio: boolean\r\n}\r\n\r\n/** 按预设推导输出分辨率(preset = source 时取工程尺寸并向下对齐到偶数) */\r\nexport function resolveDimensions(\r\n preset: ExportPreset,\r\n project: Project,\r\n): { width: number; height: number } {\r\n const even = (n: number) => Math.max(2, Math.round(n / 2) * 2)\r\n switch (preset) {\r\n case '1080p':\r\n return { width: 1920, height: 1080 }\r\n case '720p':\r\n return { width: 1280, height: 720 }\r\n case 'source':\r\n default:\r\n return { width: even(project.width), height: even(project.height) }\r\n }\r\n}\r\n\r\nexport function buildExportSettings(\r\n config: ExportConfigInput,\r\n project: Project,\r\n): ExportSettings {\r\n const { width, height } = resolveDimensions(config.preset, project)\r\n return {\r\n preset: config.preset,\r\n width,\r\n height,\r\n fps: config.fps,\r\n bitrate: config.bitrate,\r\n includeAudio: config.includeAudio,\r\n }\r\n}\r\n\r\n/**\r\n * 导出状态机。\r\n *\r\n * - `start` 触发导出(Worker 优先,文字素材自动回退主线程);\r\n * - `cancel` 终止 Worker 并丢弃结果,不残留悬挂线程;\r\n * - `fixDuration` 对已有结果调用 fixFMP4Duration 修正 moov 时长。\r\n */\r\nexport function useExport(project: Project, assets: MediaAsset[]) {\r\n const editor = useCutEditor()\r\n // 运行时实例在编辑器生命周期内恒定;解构出 clips 供导出使用\r\n const clips = editor.runtime.clips\r\n const [state, setState] = useState<UseExportState>({\r\n phase: 'idle',\r\n progress: 0,\r\n error: null,\r\n result: null,\r\n fixing: false,\r\n })\r\n const handleRef = useRef<ReturnType<typeof startExport> | null>(null)\r\n\r\n const start = useCallback(\r\n async (config: ExportConfigInput) => {\r\n if (handleRef.current != null) return\r\n const settings = buildExportSettings(config, project)\r\n setState({ phase: 'preparing', progress: 0, error: null, result: null, fixing: false })\r\n\r\n const maxTimeUs = computeTimelineEnd(project.clips)\r\n const handle = startExport(\r\n {\r\n registry: clips,\r\n createWorker: () => createExportWorker(editor.bridge),\r\n project,\r\n assets,\r\n settings,\r\n maxTimeUs,\r\n },\r\n (progress) => {\r\n setState((prev) =>\r\n prev.phase === 'running' || prev.phase === 'preparing'\r\n ? { ...prev, phase: 'running', progress }\r\n : prev,\r\n )\r\n },\r\n )\r\n handleRef.current = handle\r\n\r\n try {\r\n const result = await handle.promise\r\n if (handleRef.current !== handle) return\r\n handleRef.current = null\r\n setState({ phase: 'done', progress: 1, error: null, result, fixing: false })\r\n } catch (err) {\r\n if (handleRef.current !== handle) return\r\n handleRef.current = null\r\n const message = err instanceof Error ? err.message : String(err)\r\n const cancelled = message.includes('取消')\r\n setState({\r\n phase: cancelled ? 'cancelled' : 'error',\r\n progress: 0,\r\n error: cancelled ? null : message,\r\n result: null,\r\n fixing: false,\r\n })\r\n }\r\n },\r\n [assets, project],\r\n )\r\n\r\n const cancel = useCallback(() => {\r\n handleRef.current?.cancel()\r\n handleRef.current = null\r\n setState((prev) => ({ ...prev, phase: 'cancelled', error: null }))\r\n }, [])\r\n\r\n const reset = useCallback(() => {\r\n setState({ phase: 'idle', progress: 0, error: null, result: null, fixing: false })\r\n }, [])\r\n\r\n /**\r\n * 把导出结果交给宿主。\r\n *\r\n * 优先走 `HostBridge.files.saveBlob`:Electron 里宿主会弹保存对话框让用户选路径,\r\n * 浏览器里宿主可能上传到自己的服务。只有宿主没有实现(或明确返回未处理)时,\r\n * 才回退到 `<a download>`。\r\n *\r\n * 不接管会导致桌面端表现错误:Electron 的 `<a download>` 会静默落到下载目录,\r\n * 用户既看不到存哪了,也没法选位置。\r\n */\r\n const download = useCallback(\r\n (filename?: string) => {\r\n setState((prev) => {\r\n if (prev.result != null) {\r\n const name = filename ?? defaultFilename()\r\n const save = editor.bridge.files?.saveBlob\r\n if (save == null) {\r\n downloadBlob(prev.result.blob, name)\r\n return prev\r\n }\r\n void save\r\n .call(editor.bridge.files, prev.result.blob, name)\r\n .then((result) => {\r\n // 用户取消不算失败,不提示;真正的错误才透出去\r\n if (!result.ok && !result.cancelled) {\r\n setState((s) => ({ ...s, error: result.error ?? '保存失败' }))\r\n }\r\n })\r\n .catch((err: unknown) => {\r\n setState((s) => ({\r\n ...s,\r\n error: err instanceof Error ? err.message : String(err),\r\n }))\r\n })\r\n }\r\n return prev\r\n })\r\n },\r\n [editor],\r\n )\r\n\r\n const fixDuration = useCallback(async () => {\r\n setState((prev) => ({ ...prev, fixing: true }))\r\n try {\r\n const current = state.result\r\n if (current == null) return\r\n const fixed = await fixFMP4DurationStream(current.blob)\r\n setState((prev) => ({\r\n ...prev,\r\n fixing: false,\r\n result: prev.result == null ? prev.result : { ...prev.result, blob: fixed },\r\n }))\r\n } catch (err) {\r\n setState((prev) => ({\r\n ...prev,\r\n fixing: false,\r\n error: err instanceof Error ? err.message : String(err),\r\n }))\r\n }\r\n }, [state.result])\r\n\r\n return { state, start, cancel, reset, download, fixDuration, defaultBitrate: DEFAULT_BITRATE }\r\n}\r\n","import { Download, Wrench } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport type { ExportResult } from '../export-runner'\n\ninterface ExportResultViewProps {\n result: ExportResult\n fixing: boolean\n onDownload: () => void\n onFixDuration: () => void\n}\n\n/**\n * 导出结果:下载 + 修复时长。\n *\n * WebAV 输出的是 fMP4(moof/mdat 分片),moov 里的 duration 为 0 时,\n * 部分播放器会显示「时长未知」。fixFMP4Duration 会重写该字段。\n */\nexport function ExportResultView({\n result,\n fixing,\n onDownload,\n onFixDuration,\n}: ExportResultViewProps) {\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button size=\"sm\" onClick={onDownload}>\n <Download className=\"size-3.5\" />\n 下载视频\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onFixDuration} disabled={fixing}>\n <Wrench className=\"size-3.5\" />\n {fixing ? '修复中…' : '修复时长'}\n </Button>\n {result.skipped.length > 0 && (\n <div className=\"w-full space-y-0.5 rounded-md bg-warning/10 p-2 text-[11px] text-warning\">\n <p>{result.skipped.length} 个片段被跳过:</p>\n <ul className=\"list-disc pl-4\">\n {result.skipped.slice(0, 4).map((item, index) => (\n <li key={`${item.clipId}-${index}`} className=\"truncate\">\n {item.reason}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { AlertTriangle, CheckCircle2, Download, Loader2, RotateCw, XCircle } from 'lucide-react'\nimport type { ExportFps, ExportPreset } from '@cut-workbench/core'\nimport { useUi } from '../../../context'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport { Label } from '@/components/ui/label'\nimport { Switch } from '@/components/ui/switch'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select'\nimport { Progress } from '@/components/ui/progress'\nimport { Separator } from '@/components/ui/separator'\nimport { formatBytes, formatDuration, formatElapsed } from '@/lib/format'\nimport { DEFAULT_BITRATE } from '@/lib/constants'\nimport { getProjectDuration } from '@cut-workbench/core'\nimport { resolveDimensions, useExport } from '../hooks/useExportState'\nimport { ExportResultView } from './ExportResult'\nimport { useEditor } from '@cut-workbench/react'\n\nconst PRESETS: Array<{ value: ExportPreset; label: string; hint: string }> = [\n { value: '1080p', label: '1080p', hint: '1920×1080' },\n { value: '720p', label: '720p', hint: '1280×720' },\n { value: 'source', label: '原尺寸', hint: '与工程一致' },\n]\n\nconst FPS_OPTIONS: ExportFps[] = [24, 30, 60]\n\n/**\n * 导出对话框:配置 → 进度 → 结果。\n *\n * 所有重活都在 Worker 里;这里只负责 UI 与状态机。\n */\nexport function ExportDialog() {\n const open = useUi((s) => s.dialog === 'export')\n const closeDialog = useUi((s) => s.closeDialog)\n const project = useEditor((s) => s.project)\n const assets = useEditor((s) => s.assets)\n\n const [preset, setPreset] = useState<ExportPreset>('1080p')\n const [fps, setFps] = useState<ExportFps>(30)\n const [bitrateMbps, setBitrateMbps] = useState(DEFAULT_BITRATE / 1_000_000)\n const [includeAudio, setIncludeAudio] = useState(true)\n\n const { state, start, cancel, reset, download, fixDuration } = useExport(project, assets)\n\n const dimensions = useMemo(() => resolveDimensions(preset, project), [preset, project])\n const durationUs = getProjectDuration(project)\n const estimatedBytes = useMemo(() => {\n const seconds = durationUs / 1_000_000\n return Math.round((bitrateMbps * 1_000_000 * seconds) / 8)\n }, [bitrateMbps, durationUs])\n\n const running = state.phase === 'preparing' || state.phase === 'running'\n const hasTextClips = useMemo(() => {\n const kinds = new Map(assets.map((a) => [a.id, a.kind]))\n return project.clips.some((c) => kinds.get(c.assetId) === 'text')\n }, [assets, project.clips])\n\n // 关闭对话框时重置\n useEffect(() => {\n if (!open && !running) reset()\n }, [open, reset, running])\n\n const handleStart = () => {\n void start({ preset, fps, bitrate: Math.round(bitrateMbps * 1_000_000), includeAudio })\n }\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && !running && closeDialog()}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>导出视频</DialogTitle>\n <DialogDescription>\n 输出格式固定为 MP4(H.264 + AAC 48kHz 双声道)。WebAV 生成的是 fMP4 分片,\n 部分播放器需要「修复时长」才能正确显示总长。\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label className=\"text-xs\">分辨率</Label>\n <Select\n value={preset}\n onValueChange={(v) => setPreset(v as ExportPreset)}\n disabled={running}\n >\n <SelectTrigger className=\"h-8\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRESETS.map((item) => (\n <SelectItem key={item.value} value={item.value}>\n {item.label}\n <span className=\"text-muted-foreground\"> · {item.hint}</span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label className=\"text-xs\">帧率</Label>\n <Select\n value={String(fps)}\n onValueChange={(v) => setFps(Number(v) as ExportFps)}\n disabled={running}\n >\n <SelectTrigger className=\"h-8\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {FPS_OPTIONS.map((option) => (\n <SelectItem key={option} value={String(option)}>\n {option} fps\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-xs\">码率</Label>\n <span className=\"font-mono text-[11px] text-muted-foreground tabular-nums\">\n {bitrateMbps.toFixed(1)} Mbps\n </span>\n </div>\n <input\n type=\"range\"\n className=\"h-1 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary\"\n min={1}\n max={20}\n step={0.5}\n value={bitrateMbps}\n disabled={running}\n onChange={(e) => setBitrateMbps(Number(e.target.value))}\n />\n </div>\n\n <div className=\"flex items-center justify-between rounded-md border px-3 py-2\">\n <div>\n <Label className=\"text-xs\">包含音轨</Label>\n <p className=\"text-[10px] text-muted-foreground\">\n 关闭后输出无声音(Combinator audio: false)\n </p>\n </div>\n <Switch checked={includeAudio} onCheckedChange={setIncludeAudio} disabled={running} />\n </div>\n\n <div className=\"space-y-1 rounded-md bg-muted/50 px-3 py-2 text-[11px] text-muted-foreground\">\n <div className=\"flex justify-between\">\n <span>输出尺寸</span>\n <span className=\"font-mono tabular-nums\">\n {dimensions.width}×{dimensions.height}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span>时长</span>\n <span className=\"font-mono tabular-nums\">{formatDuration(durationUs)}</span>\n </div>\n <div className=\"flex justify-between\">\n <span>预估大小</span>\n <span className=\"font-mono tabular-nums\">{formatBytes(estimatedBytes)}</span>\n </div>\n {hasTextClips && (\n <div className=\"flex items-start gap-1 pt-1 text-warning\">\n <AlertTriangle className=\"mt-0.5 size-3 shrink-0\" />\n <span>包含文字片段,导出会在主线程执行(文字渲染依赖 DOM),期间界面会短暂卡顿。</span>\n </div>\n )}\n </div>\n\n {running && (\n <div className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"flex items-center gap-1.5\">\n <Loader2 className=\"size-3 animate-spin\" />\n {state.phase === 'preparing' ? '准备中…' : '正在合成…'}\n </span>\n <span className=\"font-mono tabular-nums\">{Math.round(state.progress * 100)}%</span>\n </div>\n <Progress value={state.progress * 100} />\n </div>\n )}\n\n {state.phase === 'error' && (\n <div className=\"flex items-start gap-2 rounded-md bg-destructive/10 p-2.5 text-xs text-destructive\">\n <XCircle className=\"mt-0.5 size-3.5 shrink-0\" />\n <span>{state.error}</span>\n </div>\n )}\n\n {state.phase === 'cancelled' && (\n <div className=\"flex items-center gap-2 rounded-md bg-muted p-2.5 text-xs text-muted-foreground\">\n <XCircle className=\"size-3.5\" />\n 导出已取消,未生成文件。\n </div>\n )}\n\n {state.phase === 'done' && state.result != null && (\n <>\n <Separator />\n <div className=\"flex items-center gap-2 text-xs text-success\">\n <CheckCircle2 className=\"size-3.5\" />\n 导出完成 · 耗时 {formatElapsed(state.result.elapsedMs / 1000)} ·{' '}\n {formatBytes(state.result.blob.size)}\n {state.result.mode === 'main' && '(主线程)'}\n </div>\n <ExportResultView\n result={state.result}\n fixing={state.fixing}\n onDownload={() => download()}\n onFixDuration={() => void fixDuration()}\n />\n </>\n )}\n </div>\n\n <DialogFooter>\n {running ? (\n <Button variant=\"outline\" size=\"sm\" onClick={cancel}>\n 取消导出\n </Button>\n ) : state.phase === 'done' ? (\n <>\n <Button variant=\"outline\" size=\"sm\" onClick={closeDialog}>\n 关闭\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={reset}>\n 返回配置\n </Button>\n <Button size=\"sm\" onClick={() => download()}>\n <Download className=\"size-3.5\" />\n 再次下载\n </Button>\n </>\n ) : (\n <>\n <Button variant=\"outline\" size=\"sm\" onClick={closeDialog}>\n 关闭\n </Button>\n <Button size=\"sm\" onClick={handleStart} disabled={project.clips.length === 0}>\n {state.phase === 'error' || state.phase === 'cancelled' ? (\n <>\n <RotateCw className=\"size-3.5\" />\n 重试导出\n </>\n ) : (\n '开始导出'\n )}\n </Button>\n </>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n","import { Loader2 } from 'lucide-react'\nimport { Progress } from '@/components/ui/progress'\nimport { formatPercent } from '@/lib/format'\n\ninterface ExportProgressProps {\n progress: number\n label?: string\n className?: string\n}\n\n/** 内嵌式进度条(导出对话框之外的场景,例如 Lab) */\nexport function ExportProgress({ progress, label = '正在合成…', className }: ExportProgressProps) {\n return (\n <div className={className}>\n <div className=\"mb-1 flex items-center justify-between text-[11px]\">\n <span className=\"flex items-center gap-1.5 text-muted-foreground\">\n <Loader2 className=\"size-3 animate-spin\" />\n {label}\n </span>\n <span className=\"font-mono tabular-nums\">{formatPercent(progress)}</span>\n </div>\n <Progress value={progress * 100} />\n </div>\n )\n}\n","import { useMemo, useState } from 'react'\nimport { Monitor } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { cn } from '@/lib/cn'\nimport { useEditor } from '@cut-workbench/react'\nimport {\n ASPECT_PRESETS,\n normalizeSize,\n ratioLabel,\n type SizeChangeMode,\n} from '@cut-workbench/core'\n\ninterface CanvasSettingsDialogProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n}\n\n/**\n * 画布设置:比例 / 自定义尺寸 / 已有素材如何处理。\n *\n * 这是 setProjectSize 的唯一入口。之前 store 里有这个方法却没有任何 UI,\n * 用户自然找不到「设置画布比例」的地方;而即便直接调用,素材坐标也不会跟着\n * 重映射,看起来就像「改了没用」。\n *\n * 表单状态放在内层组件:Dialog 关闭时内容会被卸载,下次打开自然重新初始化,\n * 不需要用 effect 去同步外部值(那会触发级联渲染)。\n */\nexport function CanvasSettingsDialog({ open, onOpenChange }: CanvasSettingsDialogProps) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <CanvasSettingsBody onDone={() => onOpenChange(false)} />\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction CanvasSettingsBody({ onDone }: { onDone: () => void }) {\n const width = useEditor((s) => s.project.width)\n const height = useEditor((s) => s.project.height)\n const clipCount = useEditor((s) => s.project.clips.length)\n const setProjectSize = useEditor((s) => s.setProjectSize)\n\n // 初始值直接取自 store;组件随 Dialog 一起挂载,因此每次打开都是最新值\n const [draftW, setDraftW] = useState(String(width))\n const [draftH, setDraftH] = useState(String(height))\n const [mode, setMode] = useState<SizeChangeMode>('scale')\n\n const parsed = useMemo(() => {\n const w = Number(draftW)\n const h = Number(draftH)\n if (!Number.isFinite(w) || !Number.isFinite(h) || w < 16 || h < 16) return null\n return normalizeSize(w, h)\n }, [draftW, draftH])\n\n const changed = parsed != null && (parsed.width !== width || parsed.height !== height)\n const activePreset = ASPECT_PRESETS.find(\n (p) => p.width === (parsed?.width ?? width) && p.height === (parsed?.height ?? height),\n )\n\n const apply = () => {\n if (parsed == null) return\n setProjectSize(parsed.width, parsed.height, mode)\n onDone()\n }\n\n return (\n <>\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <Monitor className=\"size-4\" />\n 画布设置\n </DialogTitle>\n <DialogDescription>\n 画布尺寸决定预览构图与「原尺寸导出」的分辨率。当前 {width}×{height}(\n {ratioLabel(width, height)})。\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label className=\"text-xs\">比例预设</Label>\n <div className=\"grid grid-cols-4 gap-1.5\">\n {ASPECT_PRESETS.map((preset) => (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => {\n setDraftW(String(preset.width))\n setDraftH(String(preset.height))\n }}\n className={cn(\n 'flex flex-col items-center gap-1 rounded-md border px-2 py-2 text-[11px] transition-colors',\n activePreset?.id === preset.id\n ? 'border-primary bg-primary/10 text-foreground'\n : 'text-muted-foreground hover:border-primary/50 hover:text-foreground',\n )}\n title={preset.hint}\n >\n <span\n className=\"block rounded-[2px] border border-current opacity-70\"\n style={{\n width: preset.width >= preset.height ? 22 : (22 * preset.width) / preset.height,\n height: preset.height >= preset.width ? 22 : (22 * preset.height) / preset.width,\n }}\n />\n <span className=\"font-medium\">{preset.label}</span>\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"canvas-width\" className=\"text-xs\">\n 宽度\n </Label>\n <Input\n id=\"canvas-width\"\n type=\"number\"\n min={16}\n step={2}\n value={draftW}\n onChange={(e) => setDraftW(e.target.value)}\n className=\"h-8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"canvas-height\" className=\"text-xs\">\n 高度\n </Label>\n <Input\n id=\"canvas-height\"\n type=\"number\"\n min={16}\n step={2}\n value={draftH}\n onChange={(e) => setDraftH(e.target.value)}\n className=\"h-8\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label className=\"text-xs\">已有素材</Label>\n <div className=\"grid grid-cols-2 gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setMode('scale')}\n className={cn(\n 'rounded-md border px-2.5 py-2 text-left text-[11px] transition-colors',\n mode === 'scale'\n ? 'border-primary bg-primary/10'\n : 'text-muted-foreground hover:border-primary/50',\n )}\n >\n <span className=\"block font-medium text-foreground\">随画布缩放</span>\n <span className=\"mt-0.5 block leading-snug\">保持原有构图,按比例适配新画布</span>\n </button>\n <button\n type=\"button\"\n onClick={() => setMode('keep')}\n className={cn(\n 'rounded-md border px-2.5 py-2 text-left text-[11px] transition-colors',\n mode === 'keep'\n ? 'border-primary bg-primary/10'\n : 'text-muted-foreground hover:border-primary/50',\n )}\n >\n <span className=\"block font-medium text-foreground\">保持原位置</span>\n <span className=\"mt-0.5 block leading-snug\">素材坐标不变,画布留白变化</span>\n </button>\n </div>\n {mode === 'keep' && clipCount > 0 && (\n <p className=\"text-[10px] leading-relaxed text-warning\">\n 当前有 {clipCount} 个片段,保持原位置可能让部分素材跑到画布外。\n </p>\n )}\n </div>\n\n {parsed != null && (\n <p className=\"text-[10px] text-muted-foreground\">\n 将变为 {parsed.width}×{parsed.height}({ratioLabel(parsed.width, parsed.height)}),\n 可撤销(Ctrl+Z)。\n </p>\n )}\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" size=\"sm\" onClick={onDone}>\n 取消\n </Button>\n <Button size=\"sm\" onClick={apply} disabled={!changed}>\n {changed ? '应用' : '尺寸未变化'}\n </Button>\n </DialogFooter>\n </>\n )\n}\n","import { useCallback, useEffect } from 'react'\r\nimport type { MediaAsset } from '@cut-workbench/core'\r\nimport { createClip } from '@cut-workbench/core'\r\nimport { getTrackEnd } from '@cut-workbench/core'\r\nimport { DEFAULT_STILL_DURATION_US } from '@/lib/constants'\r\nimport { notify } from '@/lib/notify'\r\nimport { useWorkbench } from '@/context'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\n\r\n/**\r\n * 把素材添加到时间轴。\r\n *\r\n * 规则:\r\n * - 视频 / 图片 / 文字 / 字幕 → 视觉轨;\r\n * - 音频 → 音频轨(若存在);\r\n * - 起点接在该轨道最后一个 clip 的结束位置(磁吸追加),没有则从播放头开始。\r\n * - 静态素材(图片/文字/字幕)没有自然时长,用默认 5s,可后续在属性面板调整。\r\n */\r\nexport function useAddToTimeline() {\r\n const project = useEditor((s) => s.project)\r\n const assets = useEditor((s) => s.assets)\r\n const playheadUs = useEditor((s) => s.playheadUs)\r\n const addClip = useEditor((s) => s.addClip)\r\n const { bridge } = useWorkbench()\r\n\r\n return useCallback(\r\n (assetId: string) => {\r\n const asset = assets.find((a) => a.id === assetId)\r\n if (!asset) {\r\n notify(bridge, { level: 'error', title: '素材不存在,可能已被删除' })\r\n return\r\n }\r\n const kindIsAudio = asset.kind === 'audio'\r\n const track =\r\n project.tracks.find((t) => t.kind === (kindIsAudio ? 'audio' : 'visual')) ??\r\n project.tracks[0]\r\n if (!track) {\r\n notify(bridge, { level: 'error', title: '没有可用轨道' })\r\n return\r\n }\r\n\r\n const trackEnd = getTrackEnd(project, track.id)\r\n const startUs = trackEnd > 0 ? trackEnd : playheadUs\r\n const durationUs = clampDuration(asset)\r\n\r\n const clip = createClip(\r\n {\r\n assetId: asset.id,\r\n trackId: track.id,\r\n startUs,\r\n durationUs,\r\n assetWidth: asset.width,\r\n assetHeight: asset.height,\r\n },\r\n project,\r\n )\r\n\r\n if (asset.kind === 'text') {\r\n clip.text = asset.text\r\n clip.textStyle = asset.textStyle\r\n }\r\n if (asset.kind === 'subtitle') {\r\n clip.srt = asset.srt\r\n clip.subtitleStyle = asset.subtitleStyle\r\n // 字幕自身有负空间(透明遮罩层),这里把它放大到整个画布\r\n clip.transform = {\r\n x: 0,\r\n y: 0,\r\n w: project.width,\r\n h: project.height,\r\n angle: 0,\r\n }\r\n }\r\n\r\n addClip(clip)\r\n },\r\n [addClip, assets, bridge, playheadUs, project],\r\n )\r\n}\r\n\r\nfunction clampDuration(asset: MediaAsset): number {\r\n const duration = asset.durationUs\r\n if (!Number.isFinite(duration) || duration <= 0) return DEFAULT_STILL_DURATION_US\r\n return duration\r\n}\r\n\r\n/**\r\n * 监听素材被删除事件,回收 registry 中的母版与缩略图 URL。\r\n * 放在 App 层调用一次。\r\n */\r\nexport function useAssetLifecycle(): void {\r\n const editor = useCutEditor()\r\n\r\n useEffect(() => {\r\n const onRemoved = (event: Event) => {\r\n const detail = (event as CustomEvent<{ assetId: string }>).detail\r\n if (detail?.assetId == null) return\r\n editor.runtime.clips.removeAsset(detail.assetId)\r\n }\r\n window.addEventListener('cut-test:asset-removed', onRemoved)\r\n return () => window.removeEventListener('cut-test:asset-removed', onRemoved)\r\n }, [editor])\r\n\r\n useEffect(() => {\r\n return () => {\r\n void editor.runtime.clips.dispose()\r\n }\r\n }, [editor])\r\n}\r\n","import { useCallback, useEffect } from 'react'\r\nimport { useCutEditor } from '@cut-workbench/react'\r\nimport { useUiStoreApi } from '../context'\r\nimport { MAX_PX_PER_SECOND, MIN_PX_PER_SECOND } from '@/lib/constants'\r\nimport { clamp } from '@cut-workbench/core'\r\n\r\nconst FRAME_US = 1_000_000 / 30\r\n\r\n/**\r\n * 全局快捷键。\r\n *\r\n * | 键 | 行为 |\r\n * |---|---|\r\n * | Space | 播放 / 暂停 |\r\n * | S | 在播放头分割选中片段(未选中则分割命中的全部片段) |\r\n * | Delete / Backspace | 删除选中片段 |\r\n * | Ctrl+Z / Ctrl+Shift+Z | 撤销 / 重做 |\r\n * | Ctrl+D | 复制选中片段 |\r\n * | Ctrl+A | 全选 |\r\n * | ← / → | 逐帧(1/30 s) |\r\n * | Home / End | 跳首 / 跳尾 |\r\n * | + / - | 时间轴缩放 |\r\n * | Esc | 清空选择 |\r\n *\r\n * 设计:编辑类操作直接调 store;播放类操作通过 `cut-test:*` 自定义事件通知预览面板,\r\n * 避免 store → features 的反向依赖。\r\n */\r\nexport function useKeyboardShortcuts(): void {\r\n const editor = useCutEditor()\r\n const uiApi = useUiStoreApi()\r\n const handler = useCallback((event: KeyboardEvent) => {\r\n const target = event.target as HTMLElement | null\r\n const isEditable =\r\n target != null &&\r\n (target.tagName === 'INPUT' ||\r\n target.tagName === 'TEXTAREA' ||\r\n target.isContentEditable ||\r\n target.getAttribute('role') === 'textbox' ||\r\n target.closest('[role=menu]') != null)\r\n const mod = event.ctrlKey || event.metaKey\r\n\r\n // 撤销 / 重做 / 复制 / 全选:即使焦点在输入框也接管(避免触发浏览器文本撤销)\r\n if (mod && event.key.toLowerCase() === 'z') {\r\n event.preventDefault()\r\n if (event.shiftKey) editor.store.getState().redo()\r\n else editor.store.getState().undo()\r\n return\r\n }\r\n if (mod && event.key.toLowerCase() === 'd') {\r\n event.preventDefault()\r\n editor.store.getState().duplicateSelection(500_000)\r\n return\r\n }\r\n if (mod && event.key.toLowerCase() === 'a' && !isEditable) {\r\n event.preventDefault()\r\n const state = editor.store.getState()\r\n state.selectClips(state.project.clips.map((c) => c.id))\r\n return\r\n }\r\n if (mod && (event.key === '+' || event.key === '=' || event.key === '-' || event.key === '_')) {\r\n event.preventDefault()\r\n const ui = uiApi.getState()\r\n const factor = event.key === '-' || event.key === '_' ? 1 / 1.25 : 1.25\r\n ui.setPxPerSecond(clamp(ui.pxPerSecond * factor, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND))\r\n return\r\n }\r\n\r\n if (isEditable || mod) return\r\n\r\n switch (event.key) {\r\n case ' ':\r\n event.preventDefault()\r\n window.dispatchEvent(new CustomEvent('cut-test:toggle-play'))\r\n break\r\n case 's':\r\n case 'S': {\r\n event.preventDefault()\r\n const state = editor.store.getState()\r\n state.splitClipsAt(\r\n state.playheadUs,\r\n state.selectedClipIds.length > 0 ? state.selectedClipIds : undefined,\r\n )\r\n break\r\n }\r\n case 'Delete':\r\n case 'Backspace': {\r\n const state = editor.store.getState()\r\n if (state.selectedClipIds.length === 0) return\r\n event.preventDefault()\r\n state.removeClips(state.selectedClipIds)\r\n break\r\n }\r\n case 'ArrowLeft':\r\n case 'ArrowRight': {\r\n event.preventDefault()\r\n const direction = event.key === 'ArrowLeft' ? -1 : 1\r\n const state = editor.store.getState()\r\n const total = state.project.clips.reduce(\r\n (max, c) => Math.max(max, c.startUs + c.durationUs),\r\n 0,\r\n )\r\n const next = Math.max(\r\n 0,\r\n Math.min(total, state.playheadUs + direction * FRAME_US),\r\n )\r\n state.setPlayhead(next)\r\n window.dispatchEvent(\r\n new CustomEvent('cut-test:step-frame', { detail: { direction } }),\r\n )\r\n break\r\n }\r\n case 'Home': {\r\n event.preventDefault()\r\n editor.store.getState().setPlayhead(0)\r\n window.dispatchEvent(new CustomEvent('cut-test:seek', { detail: { us: 0 } }))\r\n break\r\n }\r\n case 'End': {\r\n event.preventDefault()\r\n const state = editor.store.getState()\r\n const total = state.project.clips.reduce(\r\n (max, c) => Math.max(max, c.startUs + c.durationUs),\r\n 0,\r\n )\r\n state.setPlayhead(total)\r\n window.dispatchEvent(new CustomEvent('cut-test:seek', { detail: { us: total } }))\r\n break\r\n }\r\n case '+':\r\n case '=': {\r\n event.preventDefault()\r\n const ui = uiApi.getState()\r\n ui.setPxPerSecond(clamp(ui.pxPerSecond * 1.25, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND))\r\n break\r\n }\r\n case '-':\r\n case '_': {\r\n event.preventDefault()\r\n const ui = uiApi.getState()\r\n ui.setPxPerSecond(clamp(ui.pxPerSecond / 1.25, MIN_PX_PER_SECOND, MAX_PX_PER_SECOND))\r\n break\r\n }\r\n case 'Escape':\r\n editor.store.getState().selectClips([])\r\n break\r\n default:\r\n break\r\n }\r\n }, [])\r\n\r\n useEffect(() => {\r\n window.addEventListener('keydown', handler)\r\n return () => window.removeEventListener('keydown', handler)\r\n }, [handler])\r\n}\r\n\r\n","import { useEffect, useState } from 'react'\nimport { detectEnvironment, type CapabilityReport } from '@cut-workbench/core/engine'\n\ninterface CapabilityState {\n report: CapabilityReport | null\n loading: boolean\n error: string | null\n}\n\nlet cached: CapabilityReport | null = null\n\n/**\n * 启动时探测一次浏览器能力(结果在会话内缓存)。\n *\n * 必须在渲染任何 WebAV 组件前完成,否则 opfs-tools 的模块副作用可能直接抛错。\n */\nexport function useCapability(): CapabilityState {\n const [state, setState] = useState<CapabilityState>({\n report: cached,\n loading: cached == null,\n error: null,\n })\n\n useEffect(() => {\n if (cached != null) return\n let cancelled = false\n detectEnvironment()\n .then((report) => {\n cached = report\n if (!cancelled) setState({ report, loading: false, error: null })\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setState({\n report: null,\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n })\n return () => {\n cancelled = true\n }\n }, [])\n\n return state\n}\n","import { useCapability } from '@/hooks/use-capability'\nimport type { CapabilityReport } from '@cut-workbench/core/engine'\nimport { summarizeCapabilities } from '@cut-workbench/core/engine'\n\nexport type CapabilityStatus = 'checking' | 'ready' | 'degraded' | 'unsupported'\n\nexport interface CapabilityInfo {\n report: CapabilityReport | null\n loading: boolean\n error: string | null\n status: CapabilityStatus\n summary: string\n}\n\nfunction deriveStatus(report: CapabilityReport | null, loading: boolean): CapabilityStatus {\n if (loading) return 'checking'\n if (report == null) return 'unsupported'\n if (report.opfs && report.composerSupported && report.issues.length === 0) return 'ready'\n if (report.opfs && report.composerSupported) return 'degraded'\n return 'unsupported'\n}\n\n/** 能力探测的 UI 适配层:把 core 的报告转成状态与文案 */\nexport function useCapabilityInfo(): CapabilityInfo {\n const { report, loading, error } = useCapability()\n return {\n report,\n loading,\n error,\n status: deriveStatus(report, loading),\n summary: summarizeCapabilities(report),\n }\n}\n","/**\r\n * 开发环境调试桥。\r\n *\r\n * 仅用于自动化测试 / DevTools 检查内部状态(例如验证音频是否真的排程、撤销是否正确)。\r\n * 生产构建会被 `import.meta.env.DEV` 条件完全剔除。\r\n */\r\n\r\nimport type { CutEditorHandle } from '@cut-workbench/core/engine'\r\n\r\nexport function installDebugBridge(editor: CutEditorHandle): void {\r\n if (!import.meta.env.DEV || typeof window === 'undefined') return\r\n Object.assign(window, {\r\n __cutTestEditor: editor,\r\n // 兼容既有测试脚本:它们直接调用 getState() / subscribe()\r\n __cutTestStore: { getState: () => editor.store.getState(), subscribe: editor.store.subscribe },\r\n __cutTestHistory: {\r\n getState: () => editor.history.getState(),\r\n subscribe: editor.history.subscribe,\r\n },\r\n __cutTestAudio: editor.runtime.audio,\r\n __cutTestClock: editor.runtime.clock,\r\n __cutTestRegistry: editor.runtime.clips,\r\n })\r\n}\r\n","/**\r\n * 工作台容器:把「三栏骨架 + 顶栏 + 弹窗」组装成一个可配置组件。\r\n *\r\n * 与旧 `App` 的区别:\r\n * - 不自己创建编辑器(由外层 Provider 注入),因此支持多实例;\r\n * - 顶栏的保存/打开走调用方传入的回调,不写死 IndexedDB;\r\n * - 每个面板都可以被 `slots` 替换,宿主想换掉哪块就换哪块;\r\n * - 不强制能力阻断页(宿主可自行决定)。\r\n */\r\n\r\nimport {\r\n Suspense,\r\n lazy,\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from 'react'\r\nimport { useCutEditor, useEditor } from '@cut-workbench/react'\r\nimport { isEnvironmentUsable } from '@cut-workbench/core/engine'\r\nimport { AppShell } from './components/layout/AppShell'\r\nimport { WorkbenchToolbar, type WorkbenchToolbarLabels } from './components/layout/WorkbenchToolbar'\r\nimport { PanelResizer } from './components/layout/PanelResizer'\r\nimport { UnsupportedBrowser } from './components/common/UnsupportedBrowser'\r\nimport { LoadingOverlay } from './components/common/LoadingOverlay'\r\nimport { Toaster } from './components/ui/sonner'\r\nimport { MediaLibraryPanel } from './features/media-library'\r\nimport { PreviewPanel } from './features/preview'\r\nimport { Timeline } from './features/timeline'\r\nimport { InspectorPanel } from './features/inspector'\r\nimport { ExportDialog } from './features/export'\r\nimport { CanvasSettingsDialog } from './features/canvas-settings'\r\nimport { useAddToTimeline, useAssetLifecycle } from './hooks/use-editor-actions'\r\nimport { useKeyboardShortcuts } from './hooks/use-keyboard-shortcuts'\r\nimport { useCapabilityInfo } from './components/layout/use-capability-info'\r\nimport { reportError } from './hooks/use-toast-safe'\r\nimport { useUi, useWorkbench } from './context'\r\nimport { installDebugBridge } from './app/debug-bridge'\r\n\r\n/**\r\n * Lab 的懒加载器。\r\n *\r\n * ⚠️ 用**字面量**模块说明符做动态 import,而不是先从 `./features/lab` 拿组件:\r\n * 前者会被打包成 external(`import('@cut-workbench/workbench/lab')`),由宿主\r\n * 打包器解析;后者会把 lab 整棵子树(含 `@webav/av-recorder`)拉回本包的模块图。\r\n *\r\n * 解析到 lab 还是 stub,由宿主的 `resolve.alias` 决定 —— 这是移除 lab 的\r\n * 唯一可靠方式(运行时开关删不掉已产出的 chunk,见 src/lab.ts 顶部说明)。\r\n */\r\nconst LabPage = lazy(() => import('@cut-workbench/workbench/lab').then((m) => ({ default: m.LabPage })))\r\n\r\nexport interface CutWorkbenchSlots {\r\n /** 替换内置顶栏;传 `null` 表示不要顶栏 */\r\n topBar?: ReactNode | null\r\n mediaLibrary?: ReactNode\r\n preview?: ReactNode\r\n inspector?: ReactNode\r\n timeline?: ReactNode\r\n}\r\n\r\n/**\r\n * 顶栏的局部自定义。\r\n *\r\n * 与 `slots.topBar` 的区别:`slots.topBar` 会替换**整条**顶栏(撤销、导出等\r\n * 内置动作一并消失,宿主自己负责);`toolbar` 只替换其中某一块,其余保留。\r\n */\r\nexport interface CutWorkbenchToolbarOptions {\r\n /** 左侧品牌区;传 `null` 表示不要(默认就没有,避免在宿主产品里露出本库品牌) */\r\n brand?: ReactNode | null\r\n /** 工程名区;传 `null` 表示不要 */\r\n title?: ReactNode | null\r\n /** 工程名右侧状态(默认「空工程 · 16:9」);传 `null` 表示不要 */\r\n status?: ReactNode | null\r\n /** 在右侧按钮组末尾追加宿主动作 */\r\n actions?: ReactNode\r\n /** 是否显示引擎能力徽标,默认 true */\r\n showCapabilityBadge?: boolean\r\n /** 是否允许双击工程名重命名,默认 false */\r\n editableTitle?: boolean\r\n /** 覆盖内置文案 */\r\n labels?: WorkbenchToolbarLabels\r\n}\r\n\r\nexport interface CutWorkbenchProps {\r\n slots?: CutWorkbenchSlots\r\n /** 顶栏的局部自定义(`slots.topBar` 优先级更高) */\r\n toolbar?: CutWorkbenchToolbarOptions\r\n className?: string\r\n /**\r\n * 工程持久化回调。\r\n *\r\n * 不传则顶栏的「保存 / 打开」按钮隐藏 —— 库不假设宿主用 IndexedDB、文件还是云端。\r\n */\r\n onSaveProject?: () => Promise<void> | void\r\n onOpenProject?: () => Promise<void> | void\r\n /** 是否在能力不足时显示阻断页(宿主自己负责环境校验时可关闭) */\r\n enforceCapability?: boolean\r\n}\r\n\r\n/**\r\n * 工作台主体。\r\n *\r\n * 必须在 `<WorkbenchProvider>` + `<CutEditorProvider>` 之内使用。\r\n */\r\nexport function CutWorkbench({\r\n slots,\r\n toolbar: toolbarOptions,\r\n className,\r\n onSaveProject,\r\n onOpenProject,\r\n enforceCapability = true,\r\n}: CutWorkbenchProps) {\r\n const editor = useCutEditor()\r\n const capability = useCapabilityInfo()\r\n const leftWidth = useUi((s) => s.leftWidth)\r\n const rightWidth = useUi((s) => s.rightWidth)\r\n const timelineHeight = useUi((s) => s.timelineHeight)\r\n const setLeftWidth = useUi((s) => s.setLeftWidth)\r\n const setRightWidth = useUi((s) => s.setRightWidth)\r\n const setTimelineHeight = useUi((s) => s.setTimelineHeight)\r\n const openDialog = useUi((s) => s.openDialog)\r\n const closeDialog = useUi((s) => s.closeDialog)\r\n const dialog = useUi((s) => s.dialog)\r\n const { config, theme } = useWorkbench()\r\n const route = useUi((s) => s.route)\r\n\r\n const addToTimeline = useAddToTimeline()\r\n const removeAsset = useEditor((s) => s.removeAsset)\r\n const [busy, setBusy] = useState<string | null>(null)\r\n\r\n useAssetLifecycle()\r\n useKeyboardShortcuts()\r\n\r\n // 调试桥只装一次:多次挂载不应重复覆盖 window 上的调试对象\r\n const installed = useRef(false)\r\n useEffect(() => {\r\n if (installed.current) return\r\n installed.current = true\r\n installDebugBridge(editor)\r\n }, [editor])\r\n\r\n // 素材卡片「+」/ 双击:通过事件解耦(feature 之间不互相 import)。\r\n // 事件名保持旧值,现有测试脚本依赖它。\r\n useEffect(() => {\r\n const onRequest = (event: Event) => {\r\n const detail = (event as CustomEvent<{ assetId: string }>).detail\r\n if (detail?.assetId) addToTimeline(detail.assetId)\r\n }\r\n window.addEventListener('cut-test:add-asset-request', onRequest)\r\n return () => window.removeEventListener('cut-test:add-asset-request', onRequest)\r\n }, [addToTimeline])\r\n\r\n const handleExport = useCallback(() => openDialog('export'), [openDialog])\r\n\r\n const handleSave = useCallback(async () => {\r\n if (onSaveProject == null) return\r\n setBusy('正在保存工程…')\r\n try {\r\n await onSaveProject()\r\n editor.store.getState().markSaved()\r\n } catch (err) {\r\n reportError(err, '保存工程失败')\r\n } finally {\r\n setBusy(null)\r\n }\r\n }, [onSaveProject, editor])\r\n\r\n const handleOpen = useCallback(async () => {\r\n if (onOpenProject == null) return\r\n setBusy('正在加载工程…')\r\n try {\r\n await onOpenProject()\r\n editor.history.getState().clear()\r\n } catch (err) {\r\n reportError(err, '加载工程失败')\r\n } finally {\r\n setBusy(null)\r\n }\r\n }, [onOpenProject, editor])\r\n\r\n const toolbar = useMemo(() => {\r\n if (slots?.topBar !== undefined) return slots.topBar\r\n return (\r\n <WorkbenchToolbar\r\n capability={capability}\r\n onExport={handleExport}\r\n onSaveProject={onSaveProject != null ? handleSave : undefined}\r\n onOpenProject={onOpenProject != null ? handleOpen : undefined}\r\n brand={toolbarOptions?.brand}\r\n title={toolbarOptions?.title}\r\n status={toolbarOptions?.status}\r\n actions={toolbarOptions?.actions}\r\n showCapabilityBadge={toolbarOptions?.showCapabilityBadge}\r\n editableTitle={toolbarOptions?.editableTitle}\r\n labels={toolbarOptions?.labels}\r\n showLab={config.features.lab}\r\n />\r\n )\r\n }, [\r\n slots?.topBar,\r\n toolbarOptions,\r\n capability,\r\n handleExport,\r\n handleSave,\r\n handleOpen,\r\n onSaveProject,\r\n onOpenProject,\r\n config.features.lab,\r\n ])\r\n\r\n if (enforceCapability) {\r\n if (capability.loading) {\r\n return <UnsupportedBrowser report={null} loading error={null} />\r\n }\r\n if (!isEnvironmentUsable(capability.report)) {\r\n return (\r\n <UnsupportedBrowser report={capability.report} loading={false} error={capability.error} />\r\n )\r\n }\r\n }\r\n\r\n if (config.features.lab && route === 'lab') {\r\n return (\r\n <div className=\"flex h-full min-h-0 flex-col\">\r\n {toolbar}\r\n <div className=\"min-h-0 flex-1 overflow-auto\">\r\n {/* LabPage 是 lazy 的(见 features/lab/index.ts):\r\n 它连同 @webav/av-recorder 只在进入 lab 路由时才下载。\r\n 这里不存在「一直挂着 Suspense」的问题 —— 分支本身已经按路由隔离。 */}\r\n <Suspense\r\n fallback={\r\n <div className=\"flex h-full items-center justify-center\">\r\n <LoadingOverlay visible title=\"正在加载能力实验室…\" fullscreen={false} />\r\n </div>\r\n }\r\n >\r\n <LabPage />\r\n </Suspense>\r\n </div>\r\n <ExportDialog />\r\n <CanvasSettingsDialog\r\n open={dialog === 'projectSettings'}\r\n onOpenChange={(next) => (next ? openDialog('projectSettings') : closeDialog())}\r\n />\r\n <LoadingOverlay visible={busy != null} title={busy ?? ''} fullscreen />\r\n <Toaster position=\"bottom-right\" richColors closeButton theme={theme.resolved} />\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex h-full min-h-0 flex-col\">\r\n <AppShell\r\n className={className}\r\n topBar={toolbar}\r\n left={\r\n slots?.mediaLibrary ?? (\r\n <MediaLibraryPanel\r\n onAddToTimeline={addToTimeline}\r\n onRemoveAsset={(assetId) => {\r\n removeAsset(assetId)\r\n window.dispatchEvent(\r\n new CustomEvent('cut-test:asset-removed', { detail: { assetId } }),\r\n )\r\n }}\r\n />\r\n )\r\n }\r\n center={slots?.preview ?? <PreviewPanel />}\r\n right={slots?.inspector ?? <InspectorPanel />}\r\n timeline={slots?.timeline ?? <Timeline />}\r\n leftWidth={leftWidth}\r\n rightWidth={rightWidth}\r\n timelineHeight={timelineHeight}\r\n leftResizer={\r\n <PanelResizer\r\n orientation=\"vertical\"\r\n value={leftWidth}\r\n min={240}\r\n max={520}\r\n onChange={setLeftWidth}\r\n />\r\n }\r\n rightResizer={\r\n <PanelResizer\r\n orientation=\"vertical\"\r\n value={rightWidth}\r\n min={240}\r\n max={480}\r\n invert\r\n onChange={setRightWidth}\r\n />\r\n }\r\n timelineResizer={\r\n <PanelResizer\r\n orientation=\"horizontal\"\r\n value={timelineHeight}\r\n min={180}\r\n max={560}\r\n invert\r\n onChange={setTimelineHeight}\r\n />\r\n }\r\n />\r\n <ExportDialog />\r\n <CanvasSettingsDialog\r\n open={dialog === 'projectSettings'}\r\n onOpenChange={(next) => (next ? openDialog('projectSettings') : closeDialog())}\r\n />\r\n <LoadingOverlay visible={busy != null} title={busy ?? ''} fullscreen />\r\n <Toaster position=\"bottom-right\" richColors closeButton theme={theme.resolved} />\r\n </div>\r\n )\r\n}\r\n","/**\n * 工作台默认宿主桥。\n *\n * 在 `createWebBridge()`(core 提供的基础能力)之上补一层「与构建形态相关」的\n * 部分:导出 Worker 怎么创建。\n *\n * 为什么要单独一层:\n * - core 不能知道 UI 包的 Worker 实现(那会形成反向依赖);\n * - 默认桥必须自己能创建 Worker,否则「不传 bridge」的接入方在 UMD 下会\n * 走不到内联分支,导出直接失败(本项目就是这样发现的)。\n */\n\nimport { createWebBridge, type HostBridge } from '@cut-workbench/core'\nimport { createExportWorker } from './features/export/worker-factory'\n\nexport interface CreateDefaultBridgeOptions {\n /** 覆盖下载文件名 */\n downloadName?: (suggested: string) => string\n}\n\n/** 浏览器内置桥 + 导出 Worker 工厂 */\nexport function createDefaultBridge(options: CreateDefaultBridgeOptions = {}): HostBridge {\n const base = createWebBridge({ downloadName: options.downloadName })\n return {\n ...base,\n createExportWorker: () => createExportWorker(base),\n }\n}\n","// src/utils/env.ts\nvar NOTHING = Symbol.for(\"immer-nothing\");\nvar DRAFTABLE = Symbol.for(\"immer-draftable\");\nvar DRAFT_STATE = Symbol.for(\"immer-state\");\n\n// src/utils/errors.ts\nvar errors = process.env.NODE_ENV !== \"production\" ? [\n // All error codes, starting by 0:\n function(plugin) {\n return `The plugin for '${plugin}' has not been loaded into Immer. To enable the plugin, import and call \\`enable${plugin}()\\` when initializing your application.`;\n },\n function(thing) {\n return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${thing}'`;\n },\n \"This object has been frozen and should not be mutated\",\n function(data) {\n return \"Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? \" + data;\n },\n \"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.\",\n \"Immer forbids circular references\",\n \"The first or second argument to `produce` must be a function\",\n \"The third argument to `produce` must be a function or undefined\",\n \"First argument to `createDraft` must be a plain object, an array, or an immerable object\",\n \"First argument to `finishDraft` must be a draft returned by `createDraft`\",\n function(thing) {\n return `'current' expects a draft, got: ${thing}`;\n },\n \"Object.defineProperty() cannot be used on an Immer draft\",\n \"Object.setPrototypeOf() cannot be used on an Immer draft\",\n \"Immer only supports deleting array indices\",\n \"Immer only supports setting array indices and the 'length' property\",\n function(thing) {\n return `'original' expects a draft, got: ${thing}`;\n }\n // Note: if more errors are added, the errorOffset in Patches.ts should be increased\n // See Patches.ts for additional errors\n] : [];\nfunction die(error, ...args) {\n if (process.env.NODE_ENV !== \"production\") {\n const e = errors[error];\n const msg = isFunction(e) ? e.apply(null, args) : e;\n throw new Error(`[Immer] ${msg}`);\n }\n throw new Error(\n `[Immer] minified error nr: ${error}. Full error at: https://bit.ly/3cXEKWf`\n );\n}\n\n// src/utils/common.ts\nvar O = Object;\nvar getPrototypeOf = O.getPrototypeOf;\nvar CONSTRUCTOR = \"constructor\";\nvar PROTOTYPE = \"prototype\";\nvar CONFIGURABLE = \"configurable\";\nvar ENUMERABLE = \"enumerable\";\nvar WRITABLE = \"writable\";\nvar VALUE = \"value\";\nvar isDraft = (value) => !!value && !!value[DRAFT_STATE];\nfunction isModified(value) {\n if (!isDraft(value))\n return false;\n return value[DRAFT_STATE].modified_;\n}\nfunction isDraftable(value) {\n if (!value)\n return false;\n return isPlainObject(value) || isArray(value) || !!value[DRAFTABLE] || !!value[CONSTRUCTOR]?.[DRAFTABLE] || isMap(value) || isSet(value);\n}\nvar objectCtorString = O[PROTOTYPE][CONSTRUCTOR].toString();\nvar cachedCtorStrings = /* @__PURE__ */ new WeakMap();\nfunction isPlainObject(value) {\n if (!value || !isObjectish(value))\n return false;\n const proto = getPrototypeOf(value);\n if (proto === null || proto === O[PROTOTYPE])\n return true;\n const Ctor = O.hasOwnProperty.call(proto, CONSTRUCTOR) && proto[CONSTRUCTOR];\n if (Ctor === Object)\n return true;\n if (!isFunction(Ctor))\n return false;\n let ctorString = cachedCtorStrings.get(Ctor);\n if (ctorString === void 0) {\n ctorString = Function.toString.call(Ctor);\n cachedCtorStrings.set(Ctor, ctorString);\n }\n return ctorString === objectCtorString;\n}\nfunction original(value) {\n if (!isDraft(value))\n die(15, value);\n return value[DRAFT_STATE].base_;\n}\nfunction each(obj, iter, strict = true) {\n if (getArchtype(obj) === 0 /* Object */) {\n const keys = strict ? Reflect.ownKeys(obj) : O.keys(obj);\n keys.forEach((key) => {\n iter(key, obj[key], obj);\n });\n } else {\n obj.forEach((entry, index) => iter(index, entry, obj));\n }\n}\nfunction getArchtype(thing) {\n const state = thing[DRAFT_STATE];\n return state ? state.type_ : isArray(thing) ? 1 /* Array */ : isMap(thing) ? 2 /* Map */ : isSet(thing) ? 3 /* Set */ : 0 /* Object */;\n}\nvar has = (thing, prop, type = getArchtype(thing)) => type === 2 /* Map */ ? thing.has(prop) : O[PROTOTYPE].hasOwnProperty.call(thing, prop);\nvar get = (thing, prop, type = getArchtype(thing)) => (\n // @ts-ignore\n type === 2 /* Map */ ? thing.get(prop) : thing[prop]\n);\nvar set = (thing, propOrOldValue, value, type = getArchtype(thing)) => {\n if (type === 2 /* Map */)\n thing.set(propOrOldValue, value);\n else if (type === 3 /* Set */) {\n thing.add(value);\n } else\n thing[propOrOldValue] = value;\n};\nfunction is(x, y) {\n if (x === y) {\n return x !== 0 || 1 / x === 1 / y;\n } else {\n return x !== x && y !== y;\n }\n}\nvar isArray = Array.isArray;\nvar isMap = (target) => target instanceof Map;\nvar isSet = (target) => target instanceof Set;\nvar isObjectish = (target) => typeof target === \"object\";\nvar isFunction = (target) => typeof target === \"function\";\nvar isBoolean = (target) => typeof target === \"boolean\";\nfunction isArrayIndex(value) {\n const n = +value;\n return Number.isInteger(n) && String(n) === value;\n}\nvar getProxyDraft = (value) => {\n if (!isObjectish(value))\n return null;\n return value?.[DRAFT_STATE];\n};\nvar latest = (state) => state.copy_ || state.base_;\nvar getValue = (value) => {\n const proxyDraft = getProxyDraft(value);\n return proxyDraft ? getFinalValue(proxyDraft) : value;\n};\nvar getFinalValue = (state) => state.modified_ ? state.copy_ : state.base_;\nfunction shallowCopy(base, strict) {\n if (isMap(base)) {\n return new Map(base);\n }\n if (isSet(base)) {\n return new Set(base);\n }\n if (isArray(base))\n return Array[PROTOTYPE].slice.call(base);\n const isPlain = isPlainObject(base);\n if (strict === true || strict === \"class_only\" && !isPlain) {\n const descriptors = O.getOwnPropertyDescriptors(base);\n delete descriptors[DRAFT_STATE];\n let keys = Reflect.ownKeys(descriptors);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const desc = descriptors[key];\n if (desc[WRITABLE] === false) {\n desc[WRITABLE] = true;\n desc[CONFIGURABLE] = true;\n }\n if (desc.get || desc.set)\n descriptors[key] = {\n [CONFIGURABLE]: true,\n [WRITABLE]: true,\n // could live with !!desc.set as well here...\n [ENUMERABLE]: desc[ENUMERABLE],\n [VALUE]: base[key]\n };\n }\n return O.create(getPrototypeOf(base), descriptors);\n } else {\n const proto = getPrototypeOf(base);\n if (proto !== null && isPlain) {\n return { ...base };\n }\n const obj = O.create(proto);\n return O.assign(obj, base);\n }\n}\nfunction freeze(obj, deep = false) {\n if (isFrozen(obj) || isDraft(obj) || !isDraftable(obj))\n return obj;\n if (getArchtype(obj) > 1) {\n O.defineProperties(obj, {\n set: dontMutateMethodOverride,\n add: dontMutateMethodOverride,\n clear: dontMutateMethodOverride,\n delete: dontMutateMethodOverride\n });\n }\n O.freeze(obj);\n if (deep)\n each(\n obj,\n (_key, value) => {\n freeze(value, true);\n },\n false\n );\n return obj;\n}\nfunction dontMutateFrozenCollections() {\n die(2);\n}\nvar dontMutateMethodOverride = {\n [VALUE]: dontMutateFrozenCollections\n};\nfunction isFrozen(obj) {\n if (obj === null || !isObjectish(obj))\n return true;\n return O.isFrozen(obj);\n}\n\n// src/utils/plugins.ts\nvar PluginMapSet = \"MapSet\";\nvar PluginPatches = \"Patches\";\nvar PluginArrayMethods = \"ArrayMethods\";\nvar plugins = {};\nfunction getPlugin(pluginKey) {\n const plugin = plugins[pluginKey];\n if (!plugin) {\n die(0, pluginKey);\n }\n return plugin;\n}\nvar isPluginLoaded = (pluginKey) => !!plugins[pluginKey];\nfunction loadPlugin(pluginKey, implementation) {\n if (!plugins[pluginKey])\n plugins[pluginKey] = implementation;\n}\n\n// src/core/scope.ts\nvar currentScope;\nvar getCurrentScope = () => currentScope;\nvar createScope = (parent_, immer_) => ({\n drafts_: [],\n parent_,\n immer_,\n // Whenever the modified draft contains a draft from another scope, we\n // need to prevent auto-freezing so the unowned draft can be finalized.\n canAutoFreeze_: true,\n unfinalizedDrafts_: 0,\n handledSet_: /* @__PURE__ */ new Set(),\n processedForPatches_: /* @__PURE__ */ new Set(),\n mapSetPlugin_: isPluginLoaded(PluginMapSet) ? getPlugin(PluginMapSet) : void 0,\n arrayMethodsPlugin_: isPluginLoaded(PluginArrayMethods) ? getPlugin(PluginArrayMethods) : void 0\n});\nfunction usePatchesInScope(scope, patchListener) {\n if (patchListener) {\n scope.patchPlugin_ = getPlugin(PluginPatches);\n scope.patches_ = [];\n scope.inversePatches_ = [];\n scope.patchListener_ = patchListener;\n }\n}\nfunction revokeScope(scope) {\n leaveScope(scope);\n scope.drafts_.forEach(revokeDraft);\n scope.drafts_ = null;\n}\nfunction leaveScope(scope) {\n if (scope === currentScope) {\n currentScope = scope.parent_;\n }\n}\nvar enterScope = (immer2) => currentScope = createScope(currentScope, immer2);\nfunction revokeDraft(draft) {\n const state = draft[DRAFT_STATE];\n if (state.type_ === 0 /* Object */ || state.type_ === 1 /* Array */)\n state.revoke_();\n else\n state.revoked_ = true;\n}\n\n// src/core/finalize.ts\nfunction processResult(result, scope) {\n scope.unfinalizedDrafts_ = scope.drafts_.length;\n const baseDraft = scope.drafts_[0];\n const isReplaced = result !== void 0 && result !== baseDraft;\n if (isReplaced) {\n if (baseDraft[DRAFT_STATE].modified_) {\n revokeScope(scope);\n die(4);\n }\n if (isDraftable(result)) {\n result = finalize(scope, result);\n }\n const { patchPlugin_ } = scope;\n if (patchPlugin_) {\n patchPlugin_.generateReplacementPatches_(\n baseDraft[DRAFT_STATE].base_,\n result,\n scope\n );\n }\n } else {\n result = finalize(scope, baseDraft);\n }\n maybeFreeze(scope, result, true);\n revokeScope(scope);\n if (scope.patches_) {\n scope.patchListener_(scope.patches_, scope.inversePatches_);\n }\n return result !== NOTHING ? result : void 0;\n}\nfunction finalize(rootScope, value) {\n if (isFrozen(value))\n return value;\n const state = value[DRAFT_STATE];\n if (!state) {\n const finalValue = handleValue(value, rootScope.handledSet_, rootScope);\n return finalValue;\n }\n if (!isSameScope(state, rootScope)) {\n return value;\n }\n if (!state.modified_) {\n return state.base_;\n }\n if (!state.finalized_) {\n const { callbacks_ } = state;\n if (callbacks_) {\n while (callbacks_.length > 0) {\n const callback = callbacks_.pop();\n callback(rootScope);\n }\n }\n generatePatchesAndFinalize(state, rootScope);\n }\n return state.copy_;\n}\nfunction maybeFreeze(scope, value, deep = false) {\n if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) {\n freeze(value, deep);\n }\n}\nfunction markStateFinalized(state) {\n state.finalized_ = true;\n state.scope_.unfinalizedDrafts_--;\n}\nvar isSameScope = (state, rootScope) => state.scope_ === rootScope;\nvar EMPTY_LOCATIONS_RESULT = [];\nfunction updateDraftInParent(parent, draftValue, finalizedValue, originalKey) {\n const parentCopy = latest(parent);\n const parentType = parent.type_;\n if (originalKey !== void 0) {\n const currentValue = get(parentCopy, originalKey, parentType);\n if (currentValue === draftValue) {\n set(parentCopy, originalKey, finalizedValue, parentType);\n return;\n }\n }\n if (!parent.draftLocations_) {\n const draftLocations = parent.draftLocations_ = /* @__PURE__ */ new Map();\n each(parentCopy, (key, value) => {\n if (isDraft(value)) {\n const keys = draftLocations.get(value) || [];\n keys.push(key);\n draftLocations.set(value, keys);\n }\n });\n }\n const locations = parent.draftLocations_.get(draftValue) ?? EMPTY_LOCATIONS_RESULT;\n for (const location of locations) {\n set(parentCopy, location, finalizedValue, parentType);\n }\n}\nfunction registerChildFinalizationCallback(parent, child, key) {\n parent.callbacks_.push(function childCleanup(rootScope) {\n const state = child;\n if (!state || !isSameScope(state, rootScope)) {\n return;\n }\n rootScope.mapSetPlugin_?.fixSetContents(state);\n const finalizedValue = getFinalValue(state);\n updateDraftInParent(parent, state.draft_ ?? state, finalizedValue, key);\n generatePatchesAndFinalize(state, rootScope);\n });\n}\nfunction generatePatchesAndFinalize(state, rootScope) {\n const shouldFinalize = state.modified_ && !state.finalized_ && (state.type_ === 3 /* Set */ || state.type_ === 1 /* Array */ && state.allIndicesReassigned_ || (state.assigned_?.size ?? 0) > 0);\n if (shouldFinalize) {\n const { patchPlugin_ } = rootScope;\n if (patchPlugin_) {\n const basePath = patchPlugin_.getPath(state);\n if (basePath) {\n patchPlugin_.generatePatches_(state, basePath, rootScope);\n }\n }\n markStateFinalized(state);\n }\n}\nfunction handleCrossReference(target, key, value) {\n const { scope_ } = target;\n if (isDraft(value)) {\n const state = value[DRAFT_STATE];\n if (isSameScope(state, scope_)) {\n state.callbacks_.push(function crossReferenceCleanup() {\n prepareCopy(target);\n const finalizedValue = getFinalValue(state);\n updateDraftInParent(target, value, finalizedValue, key);\n });\n }\n } else if (isDraftable(value)) {\n target.callbacks_.push(function nestedDraftCleanup() {\n const targetCopy = latest(target);\n if (target.type_ === 3 /* Set */) {\n if (targetCopy.has(value)) {\n handleValue(value, scope_.handledSet_, scope_);\n }\n } else {\n if (get(targetCopy, key, target.type_) === value) {\n if (scope_.drafts_.length > 1 && (target.assigned_.get(key) ?? false) === true && target.copy_) {\n handleValue(\n get(target.copy_, key, target.type_),\n scope_.handledSet_,\n scope_\n );\n }\n }\n }\n });\n }\n}\nfunction handleValue(target, handledSet, rootScope) {\n if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) {\n return target;\n }\n if (isDraft(target) || handledSet.has(target) || !isDraftable(target) || isFrozen(target)) {\n return target;\n }\n handledSet.add(target);\n each(target, (key, value) => {\n if (isDraft(value)) {\n const state = value[DRAFT_STATE];\n if (isSameScope(state, rootScope)) {\n const updatedValue = getFinalValue(state);\n set(target, key, updatedValue, target.type_);\n markStateFinalized(state);\n }\n } else if (isDraftable(value)) {\n handleValue(value, handledSet, rootScope);\n }\n });\n return target;\n}\n\n// src/core/proxy.ts\nfunction createProxyProxy(base, parent) {\n const baseIsArray = isArray(base);\n const state = {\n type_: baseIsArray ? 1 /* Array */ : 0 /* Object */,\n // Track which produce call this is associated with.\n scope_: parent ? parent.scope_ : getCurrentScope(),\n // True for both shallow and deep changes.\n modified_: false,\n // Used during finalization.\n finalized_: false,\n // Track which properties have been assigned (true) or deleted (false).\n // actually instantiated in `prepareCopy()`\n assigned_: void 0,\n // The parent draft state.\n parent_: parent,\n // The base state.\n base_: base,\n // The base proxy.\n draft_: null,\n // set below\n // The base copy with any updated values.\n copy_: null,\n // Called by the `produce` function.\n revoke_: null,\n isManual_: false,\n // `callbacks` actually gets assigned in `createProxy`\n callbacks_: void 0\n };\n let target = state;\n let traps = objectTraps;\n if (baseIsArray) {\n target = [state];\n traps = arrayTraps;\n }\n const { revoke, proxy } = Proxy.revocable(target, traps);\n state.draft_ = proxy;\n state.revoke_ = revoke;\n return [proxy, state];\n}\nvar objectTraps = {\n get(state, prop) {\n if (prop === DRAFT_STATE)\n return state;\n let arrayPlugin = state.scope_.arrayMethodsPlugin_;\n const isArrayWithStringProp = state.type_ === 1 /* Array */ && typeof prop === \"string\";\n if (isArrayWithStringProp) {\n if (arrayPlugin?.isArrayOperationMethod(prop)) {\n return arrayPlugin.createMethodInterceptor(state, prop);\n }\n }\n const source = latest(state);\n if (!has(source, prop, state.type_)) {\n return readPropFromProto(state, source, prop);\n }\n const value = source[prop];\n if (state.finalized_ || !isDraftable(value)) {\n return value;\n }\n if (isArrayWithStringProp && state.operationMethod && arrayPlugin?.isMutatingArrayMethod(\n state.operationMethod\n ) && isArrayIndex(prop)) {\n return value;\n }\n if (value === peek(state.base_, prop) || isRelocatedBaseRef(state, prop, value)) {\n prepareCopy(state);\n const childKey = state.type_ === 1 /* Array */ ? +prop : prop;\n const childDraft = createProxy(state.scope_, value, state, childKey);\n return state.copy_[childKey] = childDraft;\n }\n return value;\n },\n has(state, prop) {\n return prop in latest(state);\n },\n ownKeys(state) {\n return Reflect.ownKeys(latest(state));\n },\n set(state, prop, value) {\n const desc = getDescriptorFromProto(latest(state), prop);\n if (desc?.set) {\n desc.set.call(state.draft_, value);\n return true;\n }\n if (!state.modified_) {\n const current2 = peek(latest(state), prop);\n const currentState = current2?.[DRAFT_STATE];\n if (currentState && currentState.base_ === value) {\n state.copy_[prop] = value;\n state.assigned_.delete(prop);\n return true;\n }\n if (is(value, current2) && (value !== void 0 || has(state.base_, prop, state.type_)))\n return true;\n prepareCopy(state);\n markChanged(state);\n }\n if (state.copy_[prop] === value && // special case: handle new props with value 'undefined'\n (value !== void 0 || has(state.copy_, prop, state.type_)) || // special case: NaN\n Number.isNaN(value) && Number.isNaN(state.copy_[prop]))\n return true;\n state.copy_[prop] = value;\n state.assigned_.set(prop, true);\n handleCrossReference(state, prop, value);\n return true;\n },\n deleteProperty(state, prop) {\n prepareCopy(state);\n if (peek(state.base_, prop) !== void 0 || prop in state.base_) {\n state.assigned_.set(prop, false);\n markChanged(state);\n } else {\n state.assigned_.delete(prop);\n }\n if (state.copy_) {\n delete state.copy_[prop];\n }\n return true;\n },\n // Note: We never coerce `desc.value` into an Immer draft, because we can't make\n // the same guarantee in ES5 mode.\n getOwnPropertyDescriptor(state, prop) {\n const owner = latest(state);\n const desc = Reflect.getOwnPropertyDescriptor(owner, prop);\n if (!desc)\n return desc;\n return {\n [WRITABLE]: true,\n [CONFIGURABLE]: state.type_ !== 1 /* Array */ || prop !== \"length\",\n [ENUMERABLE]: desc[ENUMERABLE],\n [VALUE]: owner[prop]\n };\n },\n defineProperty() {\n die(11);\n },\n getPrototypeOf(state) {\n return getPrototypeOf(state.base_);\n },\n setPrototypeOf() {\n die(12);\n }\n};\nvar arrayTraps = {};\nfor (let key in objectTraps) {\n let fn = objectTraps[key];\n arrayTraps[key] = function() {\n const args = arguments;\n args[0] = args[0][0];\n return fn.apply(this, args);\n };\n}\narrayTraps.deleteProperty = function(state, prop) {\n if (process.env.NODE_ENV !== \"production\" && isNaN(parseInt(prop)))\n die(13);\n return arrayTraps.set.call(this, state, prop, void 0);\n};\narrayTraps.set = function(state, prop, value) {\n if (process.env.NODE_ENV !== \"production\" && prop !== \"length\" && isNaN(parseInt(prop)))\n die(14);\n return objectTraps.set.call(this, state[0], prop, value, state[0]);\n};\nfunction peek(draft, prop) {\n const state = draft[DRAFT_STATE];\n const source = state ? latest(state) : draft;\n return source[prop];\n}\nfunction isRelocatedBaseRef(state, prop, value) {\n if (state.type_ !== 1 /* Array */ || !state.allIndicesReassigned_ || !isDraftable(value) || value[DRAFT_STATE]) {\n return false;\n }\n return state.baseRefs_.has(value);\n}\nfunction readPropFromProto(state, source, prop) {\n const desc = getDescriptorFromProto(source, prop);\n return desc ? VALUE in desc ? desc[VALUE] : (\n // This is a very special case, if the prop is a getter defined by the\n // prototype, we should invoke it with the draft as context!\n desc.get?.call(state.draft_)\n ) : void 0;\n}\nfunction getDescriptorFromProto(source, prop) {\n if (!(prop in source))\n return void 0;\n let proto = getPrototypeOf(source);\n while (proto) {\n const desc = Object.getOwnPropertyDescriptor(proto, prop);\n if (desc)\n return desc;\n proto = getPrototypeOf(proto);\n }\n return void 0;\n}\nfunction markChanged(state) {\n if (!state.modified_) {\n state.modified_ = true;\n if (state.parent_) {\n markChanged(state.parent_);\n }\n }\n}\nfunction prepareCopy(state) {\n if (!state.copy_) {\n state.assigned_ = /* @__PURE__ */ new Map();\n state.copy_ = shallowCopy(\n state.base_,\n state.scope_.immer_.useStrictShallowCopy_\n );\n }\n}\n\n// src/core/immerClass.ts\nvar Immer2 = class {\n constructor(config) {\n this.autoFreeze_ = true;\n this.useStrictShallowCopy_ = false;\n this.useStrictIteration_ = false;\n /**\n * The `produce` function takes a value and a \"recipe function\" (whose\n * return value often depends on the base state). The recipe function is\n * free to mutate its first argument however it wants. All mutations are\n * only ever applied to a __copy__ of the base state.\n *\n * Pass only a function to create a \"curried producer\" which relieves you\n * from passing the recipe function every time.\n *\n * Only plain objects and arrays are made mutable. All other objects are\n * considered uncopyable.\n *\n * Note: This function is __bound__ to its `Immer` instance.\n *\n * @param {any} base - the initial state\n * @param {Function} recipe - function that receives a proxy of the base state as first argument and which can be freely modified\n * @param {Function} patchListener - optional function that will be called with all the patches produced here\n * @returns {any} a new state, or the initial state if nothing was modified\n */\n this.produce = (base, recipe, patchListener) => {\n if (isFunction(base) && !isFunction(recipe)) {\n const defaultBase = recipe;\n recipe = base;\n const self = this;\n return function curriedProduce(base2 = defaultBase, ...args) {\n return self.produce(base2, (draft) => recipe.call(this, draft, ...args));\n };\n }\n if (!isFunction(recipe))\n die(6);\n if (patchListener !== void 0 && !isFunction(patchListener))\n die(7);\n let result;\n if (isDraftable(base)) {\n const scope = enterScope(this);\n const proxy = createProxy(scope, base, void 0);\n let hasError = true;\n try {\n result = recipe(proxy);\n hasError = false;\n } finally {\n if (hasError)\n revokeScope(scope);\n else\n leaveScope(scope);\n }\n usePatchesInScope(scope, patchListener);\n return processResult(result, scope);\n } else if (!base || !isObjectish(base)) {\n result = recipe(base);\n if (result === void 0)\n result = base;\n if (result === NOTHING)\n result = void 0;\n if (this.autoFreeze_)\n freeze(result, true);\n if (patchListener) {\n const p = [];\n const ip = [];\n getPlugin(PluginPatches).generateReplacementPatches_(base, result, {\n patches_: p,\n inversePatches_: ip\n });\n patchListener(p, ip);\n }\n return result;\n } else\n die(1, base);\n };\n this.produceWithPatches = (base, recipe) => {\n if (isFunction(base)) {\n return (state, ...args) => this.produceWithPatches(state, (draft) => base(draft, ...args));\n }\n let patches, inversePatches;\n const result = this.produce(base, recipe, (p, ip) => {\n patches = p;\n inversePatches = ip;\n });\n return [result, patches, inversePatches];\n };\n if (isBoolean(config?.autoFreeze))\n this.setAutoFreeze(config.autoFreeze);\n if (isBoolean(config?.useStrictShallowCopy))\n this.setUseStrictShallowCopy(config.useStrictShallowCopy);\n if (isBoolean(config?.useStrictIteration))\n this.setUseStrictIteration(config.useStrictIteration);\n }\n createDraft(base) {\n if (!isDraftable(base))\n die(8);\n if (isDraft(base))\n base = current(base);\n const scope = enterScope(this);\n const proxy = createProxy(scope, base, void 0);\n proxy[DRAFT_STATE].isManual_ = true;\n leaveScope(scope);\n return proxy;\n }\n finishDraft(draft, patchListener) {\n const state = draft && draft[DRAFT_STATE];\n if (!state || !state.isManual_)\n die(9);\n const { scope_: scope } = state;\n usePatchesInScope(scope, patchListener);\n return processResult(void 0, scope);\n }\n /**\n * Pass true to automatically freeze all copies created by Immer.\n *\n * By default, auto-freezing is enabled.\n */\n setAutoFreeze(value) {\n this.autoFreeze_ = value;\n }\n /**\n * Pass true to enable strict shallow copy.\n *\n * By default, immer does not copy the object descriptors such as getter, setter and non-enumrable properties.\n */\n setUseStrictShallowCopy(value) {\n this.useStrictShallowCopy_ = value;\n }\n /**\n * Pass false to use faster iteration that skips non-enumerable properties\n * but still handles symbols for compatibility.\n *\n * By default, strict iteration is enabled (includes all own properties).\n */\n setUseStrictIteration(value) {\n this.useStrictIteration_ = value;\n }\n shouldUseStrictIteration() {\n return this.useStrictIteration_;\n }\n applyPatches(base, patches) {\n let i;\n for (i = patches.length - 1; i >= 0; i--) {\n const patch = patches[i];\n if (patch.path.length === 0 && patch.op === \"replace\") {\n base = patch.value;\n break;\n }\n }\n if (i > -1) {\n patches = patches.slice(i + 1);\n }\n const applyPatchesImpl = getPlugin(PluginPatches).applyPatches_;\n if (isDraft(base)) {\n return applyPatchesImpl(base, patches);\n }\n return this.produce(\n base,\n (draft) => applyPatchesImpl(draft, patches)\n );\n }\n};\nfunction createProxy(rootScope, value, parent, key) {\n const [draft, state] = isMap(value) ? getPlugin(PluginMapSet).proxyMap_(value, parent) : isSet(value) ? getPlugin(PluginMapSet).proxySet_(value, parent) : createProxyProxy(value, parent);\n const scope = parent?.scope_ ?? getCurrentScope();\n scope.drafts_.push(draft);\n state.callbacks_ = parent?.callbacks_ ?? [];\n state.key_ = key;\n if (parent && key !== void 0) {\n registerChildFinalizationCallback(parent, state, key);\n } else {\n state.callbacks_.push(function rootDraftCleanup(rootScope2) {\n rootScope2.mapSetPlugin_?.fixSetContents(state);\n const { patchPlugin_ } = rootScope2;\n if (state.modified_ && patchPlugin_) {\n patchPlugin_.generatePatches_(state, [], rootScope2);\n }\n });\n }\n return draft;\n}\n\n// src/core/current.ts\nfunction current(value) {\n if (!isDraft(value))\n die(10, value);\n return currentImpl(value);\n}\nfunction currentImpl(value) {\n if (!isDraftable(value) || isFrozen(value))\n return value;\n const state = value[DRAFT_STATE];\n let copy;\n let strict = true;\n if (state) {\n if (!state.modified_)\n return state.base_;\n state.finalized_ = true;\n copy = shallowCopy(value, state.scope_.immer_.useStrictShallowCopy_);\n strict = state.scope_.immer_.shouldUseStrictIteration();\n } else {\n copy = shallowCopy(value, true);\n }\n const source = copy;\n if (isSet(copy))\n copy = /* @__PURE__ */ new Set();\n each(\n source,\n (key, childValue) => {\n set(copy, key, currentImpl(childValue));\n },\n strict\n );\n if (state) {\n state.finalized_ = false;\n }\n return copy;\n}\n\n// src/plugins/patches.ts\nfunction enablePatches() {\n const errorOffset = 16;\n if (process.env.NODE_ENV !== \"production\") {\n errors.push(\n 'Sets cannot have \"replace\" patches.',\n function(op) {\n return \"Unsupported patch operation: \" + op;\n },\n function(path) {\n return \"Cannot apply patch, path doesn't resolve: \" + path;\n },\n \"Patching reserved attributes like __proto__, prototype and constructor is not allowed\"\n );\n }\n function getPath(state, path = []) {\n if (state.key_ !== void 0) {\n const parentCopy = state.parent_.copy_ ?? state.parent_.base_;\n const proxyDraft = getProxyDraft(get(parentCopy, state.key_));\n const valueAtKey = get(parentCopy, state.key_);\n if (valueAtKey === void 0) {\n return null;\n }\n if (valueAtKey !== state.draft_ && valueAtKey !== state.copy_) {\n return null;\n }\n if (proxyDraft != null && proxyDraft.base_ !== state.base_) {\n return null;\n }\n const isSet2 = state.parent_.type_ === 3 /* Set */;\n let key;\n if (isSet2) {\n const setParent = state.parent_;\n key = Array.from(setParent.drafts_.keys()).indexOf(state.key_);\n } else {\n key = state.key_;\n }\n if (!(isSet2 && parentCopy.size > key || has(parentCopy, key))) {\n return null;\n }\n if (state.parent_.type_ === 1 /* Array */ && state.parent_.allIndicesReassigned_) {\n return null;\n }\n path.push(key);\n }\n if (state.parent_) {\n return getPath(state.parent_, path);\n }\n path.reverse();\n try {\n resolvePath(state.copy_, path);\n } catch (e) {\n return null;\n }\n return path;\n }\n function resolvePath(base, path) {\n let current2 = base;\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i];\n current2 = get(current2, key);\n if (!isObjectish(current2) || current2 === null) {\n throw new Error(`Cannot resolve path at '${path.join(\"/\")}'`);\n }\n }\n return current2;\n }\n const REPLACE = \"replace\";\n const ADD = \"add\";\n const REMOVE = \"remove\";\n function generatePatches_(state, basePath, scope) {\n if (state.scope_.processedForPatches_.has(state)) {\n return;\n }\n state.scope_.processedForPatches_.add(state);\n const { patches_, inversePatches_ } = scope;\n switch (state.type_) {\n case 0 /* Object */:\n case 2 /* Map */:\n return generatePatchesFromAssigned(\n state,\n basePath,\n patches_,\n inversePatches_\n );\n case 1 /* Array */:\n return generateArrayPatches(\n state,\n basePath,\n patches_,\n inversePatches_\n );\n case 3 /* Set */:\n return generateSetPatches(\n state,\n basePath,\n patches_,\n inversePatches_\n );\n }\n }\n function generateArrayPatches(state, basePath, patches, inversePatches) {\n let { base_, assigned_ } = state;\n let copy_ = state.copy_;\n if (copy_.length < base_.length) {\n ;\n [base_, copy_] = [copy_, base_];\n [patches, inversePatches] = [inversePatches, patches];\n }\n const allReassigned = state.allIndicesReassigned_ === true;\n for (let i = 0; i < base_.length; i++) {\n const copiedItem = copy_[i];\n const baseItem = base_[i];\n const isAssigned = allReassigned || assigned_?.get(i.toString());\n if (isAssigned && copiedItem !== baseItem) {\n const childState = copiedItem?.[DRAFT_STATE];\n if (childState && childState.modified_) {\n continue;\n }\n const path = basePath.concat([i]);\n patches.push({\n op: REPLACE,\n path,\n // Need to maybe clone it, as it can in fact be the original value\n // due to the base/copy inversion at the start of this function\n value: clonePatchValueIfNeeded(copiedItem)\n });\n inversePatches.push({\n op: REPLACE,\n path,\n value: clonePatchValueIfNeeded(baseItem)\n });\n }\n }\n for (let i = base_.length; i < copy_.length; i++) {\n const path = basePath.concat([i]);\n patches.push({\n op: ADD,\n path,\n // Need to maybe clone it, as it can in fact be the original value\n // due to the base/copy inversion at the start of this function\n value: clonePatchValueIfNeeded(copy_[i])\n });\n }\n for (let i = copy_.length - 1; base_.length <= i; --i) {\n const path = basePath.concat([i]);\n inversePatches.push({\n op: REMOVE,\n path\n });\n }\n }\n function generatePatchesFromAssigned(state, basePath, patches, inversePatches) {\n const { base_, copy_, type_ } = state;\n each(state.assigned_, (key, assignedValue) => {\n const origValue = get(base_, key, type_);\n const value = get(copy_, key, type_);\n const op = !assignedValue ? REMOVE : has(base_, key) ? REPLACE : ADD;\n if (origValue === value && op === REPLACE)\n return;\n const path = basePath.concat(key);\n patches.push(\n op === REMOVE ? { op, path } : { op, path, value: clonePatchValueIfNeeded(value) }\n );\n inversePatches.push(\n op === ADD ? { op: REMOVE, path } : op === REMOVE ? { op: ADD, path, value: clonePatchValueIfNeeded(origValue) } : { op: REPLACE, path, value: clonePatchValueIfNeeded(origValue) }\n );\n });\n }\n function generateSetPatches(state, basePath, patches, inversePatches) {\n let { base_, copy_ } = state;\n let i = 0;\n base_.forEach((value) => {\n if (!copy_.has(value)) {\n const path = basePath.concat([i]);\n patches.push({\n op: REMOVE,\n path,\n value\n });\n inversePatches.unshift({\n op: ADD,\n path,\n value\n });\n }\n i++;\n });\n i = 0;\n copy_.forEach((value) => {\n if (!base_.has(value)) {\n const path = basePath.concat([i]);\n patches.push({\n op: ADD,\n path,\n value\n });\n inversePatches.unshift({\n op: REMOVE,\n path,\n value\n });\n }\n i++;\n });\n }\n function generateReplacementPatches_(baseValue, replacement, scope) {\n const { patches_, inversePatches_ } = scope;\n patches_.push({\n op: REPLACE,\n path: [],\n value: replacement === NOTHING ? void 0 : replacement\n });\n inversePatches_.push({\n op: REPLACE,\n path: [],\n value: baseValue\n });\n }\n function applyPatches_(draft, patches) {\n patches.forEach((patch) => {\n const { path, op } = patch;\n let base = draft;\n for (let i = 0; i < path.length - 1; i++) {\n const parentType = getArchtype(base);\n let p = path[i];\n if (typeof p !== \"string\" && typeof p !== \"number\") {\n p = \"\" + p;\n }\n if ((parentType === 0 /* Object */ || parentType === 1 /* Array */) && (p === \"__proto__\" || p === CONSTRUCTOR))\n die(errorOffset + 3);\n if (isFunction(base) && p === PROTOTYPE)\n die(errorOffset + 3);\n base = get(base, p);\n if (base === null || !isObjectish(base))\n die(errorOffset + 2, path.join(\"/\"));\n }\n const type = getArchtype(base);\n const value = deepClonePatchValue(patch.value);\n const key = path[path.length - 1];\n switch (op) {\n case REPLACE:\n switch (type) {\n case 2 /* Map */:\n return base.set(key, value);\n case 3 /* Set */:\n die(errorOffset);\n default:\n return base[key] = value;\n }\n case ADD:\n switch (type) {\n case 1 /* Array */:\n return key === \"-\" ? base.push(value) : base.splice(key, 0, value);\n case 2 /* Map */:\n return base.set(key, value);\n case 3 /* Set */:\n return base.add(value);\n default:\n return base[key] = value;\n }\n case REMOVE:\n switch (type) {\n case 1 /* Array */:\n return base.splice(key, 1);\n case 2 /* Map */:\n return base.delete(key);\n case 3 /* Set */:\n return base.delete(patch.value);\n default:\n return delete base[key];\n }\n default:\n die(errorOffset + 1, op);\n }\n });\n return draft;\n }\n function deepClonePatchValue(obj) {\n if (!isDraftable(obj))\n return obj;\n if (isArray(obj))\n return obj.map(deepClonePatchValue);\n if (isMap(obj))\n return new Map(\n Array.from(obj.entries()).map(([k, v]) => [k, deepClonePatchValue(v)])\n );\n if (isSet(obj))\n return new Set(Array.from(obj).map(deepClonePatchValue));\n const cloned = Object.create(getPrototypeOf(obj));\n for (const key in obj)\n cloned[key] = deepClonePatchValue(obj[key]);\n if (has(obj, DRAFTABLE))\n cloned[DRAFTABLE] = obj[DRAFTABLE];\n return cloned;\n }\n function clonePatchValueIfNeeded(obj) {\n if (isDraft(obj)) {\n return deepClonePatchValue(obj);\n } else\n return obj;\n }\n loadPlugin(PluginPatches, {\n applyPatches_,\n generatePatches_,\n generateReplacementPatches_,\n getPath\n });\n}\n\n// src/plugins/mapset.ts\nvar _globalIterator = globalThis.Iterator;\nvar hasIteratorFrom = typeof _globalIterator?.from === \"function\";\nfunction enableMapSet() {\n class DraftMap extends Map {\n constructor(target, parent) {\n super();\n this[DRAFT_STATE] = {\n type_: 2 /* Map */,\n parent_: parent,\n scope_: parent ? parent.scope_ : getCurrentScope(),\n modified_: false,\n finalized_: false,\n copy_: void 0,\n assigned_: void 0,\n base_: target,\n draft_: this,\n isManual_: false,\n revoked_: false,\n callbacks_: []\n };\n }\n get size() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n return latest(state).size;\n }\n has(key) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n return latest(state).has(key);\n }\n set(key, value) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (!latest(state).has(key) || latest(state).get(key) !== value) {\n prepareMapCopy(state);\n markChanged(state);\n state.assigned_.set(key, true);\n state.copy_.set(key, value);\n state.assigned_.set(key, true);\n handleCrossReference(state, key, value);\n }\n return this;\n }\n delete(key) {\n if (!this.has(key)) {\n return false;\n }\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareMapCopy(state);\n markChanged(state);\n if (state.base_.has(key)) {\n state.assigned_.set(key, false);\n } else {\n state.assigned_.delete(key);\n }\n state.copy_.delete(key);\n return true;\n }\n clear() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (latest(state).size) {\n prepareMapCopy(state);\n markChanged(state);\n state.assigned_ = /* @__PURE__ */ new Map();\n each(state.base_, (key) => {\n state.assigned_.set(key, false);\n });\n state.copy_.clear();\n }\n }\n forEach(cb, thisArg) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareMapCopy(state);\n latest(state).forEach((_value, key, _map) => {\n cb.call(thisArg, this.get(key), key, this);\n });\n }\n get(key) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n const value = latest(state).get(key);\n if (state.finalized_ || !isDraftable(value)) {\n return value;\n }\n if (value !== state.base_.get(key)) {\n return value;\n }\n const draft = createProxy(state.scope_, value, state, key);\n prepareMapCopy(state);\n state.copy_.set(key, draft);\n return draft;\n }\n keys() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareMapCopy(state);\n return latest(state).keys();\n }\n values() {\n const iterator = this.keys();\n return iteratorFrom({\n next: () => {\n const r = iterator.next();\n if (r.done)\n return r;\n const value = this.get(r.value);\n return {\n done: false,\n value\n };\n }\n });\n }\n entries() {\n const iterator = this.keys();\n return iteratorFrom({\n next: () => {\n const r = iterator.next();\n if (r.done)\n return r;\n const value = this.get(r.value);\n return {\n done: false,\n value: [r.value, value]\n };\n }\n });\n }\n [(DRAFT_STATE, Symbol.iterator)]() {\n return this.entries();\n }\n }\n function iteratorFrom(iterable) {\n if (hasIteratorFrom) {\n return _globalIterator.from(iterable);\n }\n const iterator = {\n ...iterable,\n [Symbol.iterator]: () => iterator\n };\n return iterator;\n }\n function proxyMap_(target, parent) {\n const map = new DraftMap(target, parent);\n return [map, map[DRAFT_STATE]];\n }\n function prepareMapCopy(state) {\n if (!state.copy_) {\n state.assigned_ = /* @__PURE__ */ new Map();\n state.copy_ = new Map(state.base_);\n }\n }\n class DraftSet extends Set {\n constructor(target, parent) {\n super();\n this[DRAFT_STATE] = {\n type_: 3 /* Set */,\n parent_: parent,\n scope_: parent ? parent.scope_ : getCurrentScope(),\n modified_: false,\n finalized_: false,\n copy_: void 0,\n base_: target,\n draft_: this,\n drafts_: /* @__PURE__ */ new Map(),\n revoked_: false,\n isManual_: false,\n assigned_: void 0,\n callbacks_: []\n };\n }\n get size() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n return latest(state).size;\n }\n has(value) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (!state.copy_) {\n return state.base_.has(value);\n }\n if (state.copy_.has(value))\n return true;\n if (state.drafts_.has(value) && state.copy_.has(state.drafts_.get(value)))\n return true;\n return false;\n }\n add(value) {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (!this.has(value)) {\n prepareSetCopy(state);\n markChanged(state);\n state.copy_.add(value);\n handleCrossReference(state, value, value);\n }\n return this;\n }\n delete(value) {\n if (!this.has(value)) {\n return false;\n }\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareSetCopy(state);\n markChanged(state);\n return state.copy_.delete(value) || (state.drafts_.has(value) ? state.copy_.delete(state.drafts_.get(value)) : (\n /* istanbul ignore next */\n false\n ));\n }\n clear() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n if (latest(state).size) {\n prepareSetCopy(state);\n markChanged(state);\n state.copy_.clear();\n }\n }\n values() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareSetCopy(state);\n return state.copy_.values();\n }\n entries() {\n const state = this[DRAFT_STATE];\n assertUnrevoked(state);\n prepareSetCopy(state);\n return state.copy_.entries();\n }\n keys() {\n return this.values();\n }\n [(DRAFT_STATE, Symbol.iterator)]() {\n return this.values();\n }\n forEach(cb, thisArg) {\n const iterator = this.values();\n let result = iterator.next();\n while (!result.done) {\n cb.call(thisArg, result.value, result.value, this);\n result = iterator.next();\n }\n }\n }\n function proxySet_(target, parent) {\n const set2 = new DraftSet(target, parent);\n return [set2, set2[DRAFT_STATE]];\n }\n function prepareSetCopy(state) {\n if (!state.copy_) {\n state.copy_ = /* @__PURE__ */ new Set();\n state.base_.forEach((value) => {\n if (isDraftable(value)) {\n const draft = createProxy(state.scope_, value, state, value);\n state.drafts_.set(value, draft);\n state.copy_.add(draft);\n } else {\n state.copy_.add(value);\n }\n });\n }\n }\n function assertUnrevoked(state) {\n if (state.revoked_)\n die(3, JSON.stringify(latest(state)));\n }\n function fixSetContents(target) {\n if (target.type_ === 3 /* Set */ && target.copy_) {\n const copy = new Set(target.copy_);\n target.copy_.clear();\n copy.forEach((value) => {\n target.copy_.add(getValue(value));\n });\n }\n }\n loadPlugin(PluginMapSet, { proxyMap_, proxySet_, fixSetContents });\n}\n\n// src/plugins/arrayMethods.ts\nfunction enableArrayMethods() {\n const SHIFTING_METHODS = /* @__PURE__ */ new Set([\"shift\", \"unshift\"]);\n const QUEUE_METHODS = /* @__PURE__ */ new Set([\"push\", \"pop\"]);\n const RESULT_RETURNING_METHODS = /* @__PURE__ */ new Set([\n ...QUEUE_METHODS,\n ...SHIFTING_METHODS\n ]);\n const REORDERING_METHODS = /* @__PURE__ */ new Set([\"reverse\", \"sort\"]);\n const MUTATING_METHODS = /* @__PURE__ */ new Set([\n ...RESULT_RETURNING_METHODS,\n ...REORDERING_METHODS,\n \"splice\"\n ]);\n const FIND_METHODS = /* @__PURE__ */ new Set([\"find\", \"findLast\"]);\n const NON_MUTATING_METHODS = /* @__PURE__ */ new Set([\n \"filter\",\n \"slice\",\n \"concat\",\n \"flat\",\n ...FIND_METHODS,\n \"findIndex\",\n \"findLastIndex\",\n \"some\",\n \"every\",\n \"indexOf\",\n \"lastIndexOf\",\n \"includes\",\n \"join\",\n \"toString\",\n \"toLocaleString\"\n ]);\n function isMutatingArrayMethod(method) {\n return MUTATING_METHODS.has(method);\n }\n function isNonMutatingArrayMethod(method) {\n return NON_MUTATING_METHODS.has(method);\n }\n function isArrayOperationMethod(method) {\n return isMutatingArrayMethod(method) || isNonMutatingArrayMethod(method);\n }\n function enterOperation(state, method) {\n state.operationMethod = method;\n }\n function exitOperation(state) {\n state.operationMethod = void 0;\n }\n function executeArrayMethod(state, operation, markLength = true) {\n prepareCopy(state);\n const result = operation();\n markArrayChanged(state, markLength);\n return result;\n }\n function markArrayChanged(state, markLength = true) {\n markChanged(state);\n if (markLength)\n state.assigned_.set(\"length\", true);\n }\n function markAllIndicesReassigned(state) {\n state.allIndicesReassigned_ = true;\n state.baseRefs_ = new Set(state.base_);\n }\n function normalizeSliceIndex(index, length) {\n if (index < 0) {\n return Math.max(length + index, 0);\n }\n return Math.min(index, length);\n }\n function handleInsertedValues(state, startIndex, values) {\n for (let i = 0; i < values.length; i++) {\n const index = \"\" + (startIndex + i);\n state.assigned_.set(index, true);\n handleCrossReference(state, index, values[i]);\n }\n }\n function handleSimpleOperation(state, method, args) {\n const isInsert = method === \"push\" || method === \"unshift\";\n if (isInsert ? args.length === 0 : latest(state).length === 0) {\n return isInsert ? latest(state).length : void 0;\n }\n prepareCopy(state);\n const lengthBefore = state.copy_.length;\n const result = state.copy_[method](...args);\n markArrayChanged(state);\n if (SHIFTING_METHODS.has(method)) {\n markAllIndicesReassigned(state);\n }\n if (method === \"push\") {\n handleInsertedValues(state, lengthBefore, args);\n } else if (method === \"unshift\") {\n handleInsertedValues(state, 0, args);\n }\n return RESULT_RETURNING_METHODS.has(method) ? result : state.draft_;\n }\n function handleReorderingOperation(state, method, args) {\n if (latest(state).length <= 1)\n return state.draft_;\n return executeArrayMethod(\n state,\n () => {\n ;\n state.copy_[method](...args);\n markAllIndicesReassigned(state);\n return state.draft_;\n },\n false\n );\n }\n function createMethodInterceptor(state, originalMethod) {\n return function interceptedMethod(...args) {\n const method = originalMethod;\n enterOperation(state, method);\n try {\n if (isMutatingArrayMethod(method)) {\n if (RESULT_RETURNING_METHODS.has(method)) {\n return handleSimpleOperation(state, method, args);\n }\n if (REORDERING_METHODS.has(method)) {\n return handleReorderingOperation(state, method, args);\n }\n if (method === \"splice\") {\n const insertCount = args.length > 2 ? args.length - 2 : 0;\n if (insertCount === 0) {\n const length = latest(state).length;\n if (args.length === 0 || normalizeSliceIndex(args[0] ?? 0, length) === length || args.length > 1 && !(args[1] >= 1)) {\n return [];\n }\n }\n prepareCopy(state);\n const lengthBefore = state.copy_.length;\n const res = state.copy_.splice(\n ...args\n );\n if (res.length === 0 && insertCount === 0) {\n return res;\n }\n markArrayChanged(state);\n markAllIndicesReassigned(state);\n if (insertCount > 0) {\n const startIndex = normalizeSliceIndex(args[0] ?? 0, lengthBefore);\n handleInsertedValues(state, startIndex, args.slice(2));\n }\n return res;\n }\n } else {\n return handleNonMutatingOperation(state, method, args);\n }\n } finally {\n exitOperation(state);\n }\n };\n }\n function handleNonMutatingOperation(state, method, args) {\n const source = latest(state);\n if (method === \"filter\") {\n const predicate = args[0];\n const thisArg = args[1];\n const result = [];\n const length = source.length;\n for (let i = 0; i < length; i++) {\n if (predicate.call(thisArg, source[i], i, source)) {\n result.push(state.draft_[i]);\n }\n }\n return result;\n }\n if (FIND_METHODS.has(method)) {\n const predicate = args[0];\n const thisArg = args[1];\n const length = source.length;\n const isForward = method === \"find\";\n const step = isForward ? 1 : -1;\n const start = isForward ? 0 : length - 1;\n for (let i = start; i >= 0 && i < length; i += step) {\n if (predicate.call(thisArg, source[i], i, source)) {\n return state.draft_[i];\n }\n }\n return void 0;\n }\n if (method === \"slice\") {\n const rawStart = args[0] ?? 0;\n const rawEnd = args[1] ?? source.length;\n const start = normalizeSliceIndex(rawStart, source.length);\n const end = normalizeSliceIndex(rawEnd, source.length);\n const result = [];\n for (let i = start; i < end; i++) {\n result.push(state.draft_[i]);\n }\n return result;\n }\n return source[method](...args);\n }\n loadPlugin(PluginArrayMethods, {\n createMethodInterceptor,\n isArrayOperationMethod,\n isMutatingArrayMethod\n });\n}\n\n// src/immer.ts\nvar immer = new Immer2();\nvar produce = immer.produce;\nvar produceWithPatches = /* @__PURE__ */ immer.produceWithPatches.bind(immer);\nvar setAutoFreeze = /* @__PURE__ */ immer.setAutoFreeze.bind(immer);\nvar setUseStrictShallowCopy = /* @__PURE__ */ immer.setUseStrictShallowCopy.bind(immer);\nvar setUseStrictIteration = /* @__PURE__ */ immer.setUseStrictIteration.bind(immer);\nvar applyPatches = /* @__PURE__ */ immer.applyPatches.bind(immer);\nvar createDraft = /* @__PURE__ */ immer.createDraft.bind(immer);\nvar finishDraft = /* @__PURE__ */ immer.finishDraft.bind(immer);\nvar castDraft = (value) => value;\nvar castImmutable = (value) => value;\nexport {\n Immer2 as Immer,\n applyPatches,\n castDraft,\n castImmutable,\n createDraft,\n current,\n enableArrayMethods,\n enableMapSet,\n enablePatches,\n finishDraft,\n freeze,\n DRAFTABLE as immerable,\n isDraft,\n isDraftable,\n isModified,\n NOTHING as nothing,\n original,\n produce,\n produceWithPatches,\n setAutoFreeze,\n setUseStrictIteration,\n setUseStrictShallowCopy\n};\n//# sourceMappingURL=immer.mjs.map","import { produce } from 'immer';\n\nconst immerImpl = (initializer) => (set, get, store) => {\n store.setState = (updater, replace, ...args) => {\n const nextState = typeof updater === \"function\" ? produce(updater) : updater;\n return set(nextState, replace, ...args);\n };\n return initializer(store.setState, get, store);\n};\nconst immer = immerImpl;\n\nexport { immer };\n","/**\r\n * UI 状态:面板尺寸、缩放、对话框开关。\r\n *\r\n * 与编辑数据无关的一切交互状态都放这里,避免 editor-store 变成上帝对象。\r\n *\r\n * ⚠️ 使用 vanilla store 工厂而非模块级单例:同一页面挂两个工作台时,\r\n * 面板宽度、缩放、对话框都必须各算各的。React 侧通过 `useUiStore` 订阅。\r\n */\r\n\r\nimport { createStore, type StoreApi } from 'zustand/vanilla'\r\nimport { immer } from 'zustand/middleware/immer'\r\nimport { DEFAULT_PX_PER_SECOND, MAX_PX_PER_SECOND, MIN_PX_PER_SECOND } from '@/lib/constants'\r\n\r\nexport type Route = 'editor' | 'lab'\r\n\r\nexport interface UiState {\r\n route: Route\r\n /** 左侧素材面板宽度(像素) */\r\n leftWidth: number\r\n /** 右侧属性面板宽度(像素) */\r\n rightWidth: number\r\n /** 底部时间轴高度(像素) */\r\n timelineHeight: number\r\n /** 时间轴横向缩放(每秒像素) */\r\n pxPerSecond: number\r\n /** 时间轴横向滚动位置(像素) */\r\n scrollLeftPx: number\r\n /** 当前打开的对话框 */\r\n dialog: 'export' | 'mediaMeta' | 'projectSettings' | null\r\n dialogAssetId: string | null\r\n /** 正在加载的素材数量,用于骨架屏 */\r\n importingCount: number\r\n\r\n setRoute: (route: Route) => void\r\n setLeftWidth: (width: number) => void\r\n setRightWidth: (width: number) => void\r\n setTimelineHeight: (height: number) => void\r\n setPxPerSecond: (value: number) => void\r\n setScrollLeftPx: (value: number) => void\r\n openDialog: (dialog: UiState['dialog'], assetId?: string | null) => void\r\n closeDialog: () => void\r\n beginImport: () => void\r\n endImport: () => void\r\n}\r\n\r\nfunction clampPxPerSecond(value: number): number {\r\n return Math.max(MIN_PX_PER_SECOND, Math.min(MAX_PX_PER_SECOND, value))\r\n}\r\n\r\nexport type UiStore = StoreApi<UiState>\r\n\r\n/** 每个工作台实例一份 UI 状态 */\r\nexport function createUiStore(): UiStore {\r\n return createStore<UiState>()(\r\n immer((set) => ({\r\n route: 'editor',\r\n leftWidth: 320,\r\n rightWidth: 300,\r\n timelineHeight: 280,\r\n pxPerSecond: DEFAULT_PX_PER_SECOND,\r\n scrollLeftPx: 0,\r\n dialog: null,\r\n dialogAssetId: null,\r\n importingCount: 0,\r\n\r\n setRoute: (route) =>\r\n set((draft) => {\r\n draft.route = route\r\n }),\r\n\r\n setLeftWidth: (width) =>\r\n set((draft) => {\r\n draft.leftWidth = Math.max(240, Math.min(520, width))\r\n }),\r\n\r\n setRightWidth: (width) =>\r\n set((draft) => {\r\n draft.rightWidth = Math.max(240, Math.min(480, width))\r\n }),\r\n\r\n setTimelineHeight: (height) =>\r\n set((draft) => {\r\n draft.timelineHeight = Math.max(180, Math.min(560, height))\r\n }),\r\n\r\n setPxPerSecond: (value) =>\r\n set((draft) => {\r\n draft.pxPerSecond = clampPxPerSecond(value)\r\n }),\r\n\r\n setScrollLeftPx: (value) =>\r\n set((draft) => {\r\n draft.scrollLeftPx = Math.max(0, value)\r\n }),\r\n\r\n openDialog: (dialog, assetId = null) =>\r\n set((draft) => {\r\n draft.dialog = dialog\r\n draft.dialogAssetId = assetId\r\n }),\r\n\r\n closeDialog: () =>\r\n set((draft) => {\r\n draft.dialog = null\r\n draft.dialogAssetId = null\r\n }),\r\n\r\n beginImport: () =>\r\n set((draft) => {\r\n draft.importingCount += 1\r\n }),\r\n\r\n endImport: () =>\r\n set((draft) => {\r\n draft.importingCount = Math.max(0, draft.importingCount - 1)\r\n }),\r\n })),\r\n )\r\n}\r\n","/**\n * 主题解析:把 `theme=\"inherit\"` 真正接到宿主的深色约定上。\n *\n * 背景(为什么要这个文件):\n * 库内所有暗色变体(`dark:bg-...`)都由 `data-cw-theme=\"dark\"` 驱动,而 `inherit`\n * 模式下根节点**不写**这个属性 —— 于是 `.cw-root` 的亮色默认值生效,\n * `inherit` 实际等价于「强制浅色」,与文档承诺的「完全跟随宿主」名不副实。\n *\n * 这里读取宿主最通用的约定 `<html class=\"dark\">`,用 MutationObserver 持续跟随,\n * 折算成库自己的 `data-cw-theme`。库仍然只有一套主题机制,宿主不需要改任何代码。\n *\n * 为什么不用 `prefers-color-scheme`:宿主设计系统通常有「跟随系统 / 手动」两种\n * 策略,最终都落在 `<html class=\"dark\">` 上;直接读 class 才与宿主界面一致。\n */\n\nimport { useSyncExternalStore } from 'react'\n\nexport type ResolvedTheme = 'light' | 'dark'\n\n/** 读取宿主 `<html>` 上是否有 `dark` 类;无 DOM 环境(SSR)返回 false */\nfunction getHostDark(): boolean {\n if (typeof document === 'undefined') return false\n return document.documentElement.classList.contains('dark')\n}\n\n/** 订阅宿主根节点 class 变化 */\nfunction subscribeHostDark(onStoreChange: () => void): () => void {\n if (typeof document === 'undefined') return () => undefined\n const observer = new MutationObserver(onStoreChange)\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n return () => observer.disconnect()\n}\n\n/**\n * 持续跟随宿主 `<html class=\"dark\">`。\n *\n * 用 `useSyncExternalStore` 而不是 effect + state:快照是纯布尔值,\n * 不会因为 React 的批处理时序漏掉变化,也不会在 StrictMode 双调用下抖动。\n */\nexport function useHostDarkClass(): boolean {\n return useSyncExternalStore(subscribeHostDark, getHostDark, () => false)\n}\n\n/** 显式主题原样返回;`inherit` 折算为宿主当前的亮/暗 */\nexport function resolveTheme(\n theme: 'light' | 'dark' | 'inherit',\n hostDark: boolean,\n): ResolvedTheme {\n return theme === 'inherit' ? (hostDark ? 'dark' : 'light') : theme\n}\n","/**\n * 主题 token 注入接口。\n *\n * 宿主换肤不该依赖「自己写一份 CSS 并祈祷引入顺序 / 特异性正确」。`tokens` 通过\n * 根节点的 **inline style** 写入 `--cw-*` 变量:\n * - inline 声明的优先级高于任何样式表规则(包括 `[data-cw-theme=\"dark\"]` 的 token 块),\n * 因此宿主不需要处理 CSS 引入顺序;\n * - 变量仍挂在 `.cw-root` 上(不是 `:root`),不会污染宿主页面的同名变量。\n *\n * 用法:\n * ```tsx\n * <CutWorkbenchProvider theme=\"dark\" tokens={{ primary: '#4d6bfe', radius: '8px' }}>\n * ```\n *\n * 需要运行时换肤但不走 React 的宿主,可以用 {@link CUT_THEME_TOKEN_NAMES}\n * 拿到全部变量名,自己写 CSS 或 `style.setProperty()`。\n */\n\nimport type { CSSProperties } from 'react'\n\n/**\n * 所有可覆盖的 token。\n *\n * 值必须是合法的 CSS 颜色 / 长度(例如 `'#4d6bfe'`、`'rgba(0,0,0,.6)'`、`'8px'`、\n * `\"Segoe UI, system-ui, sans-serif\"`)。非法值会被浏览器丢弃,回退到内置的亮/暗默认值。\n */\nexport interface CutThemeTokens {\n // —— 基础色板(与 shadcn 命名一致)——\n background?: string\n foreground?: string\n card?: string\n cardForeground?: string\n popover?: string\n popoverForeground?: string\n primary?: string\n primaryForeground?: string\n secondary?: string\n secondaryForeground?: string\n muted?: string\n mutedForeground?: string\n accent?: string\n accentForeground?: string\n destructive?: string\n destructiveForeground?: string\n /** 警示色(源文件缺失、规格不一致等) */\n warning?: string\n warningForeground?: string\n /** 成功色(能力探测通过等) */\n success?: string\n border?: string\n input?: string\n ring?: string\n // —— 形状与字体 ——\n radius?: string\n fontSans?: string\n fontMono?: string\n // —— 编辑器专属 ——\n timelineBg?: string\n timelineTrack?: string\n timelineGrid?: string\n clipVideo?: string\n clipAudio?: string\n clipImage?: string\n clipText?: string\n clipSubtitle?: string\n playhead?: string\n /** 预览舞台 / Lab 预览区的底色(跟随主题) */\n stageBg?: string\n /** 对话框遮罩(永远是半透明黑,与主题无关) */\n overlay?: string\n /** 压在图片/视频上的角标底色(永远深底,与主题无关) */\n badgeScrim?: string\n /** 角标文字色(永远浅字) */\n badgeScrimForeground?: string\n}\n\n/** token key → `--cw-kebab-case` */\nfunction toCssVarName(key: string): string {\n return `--cw-${key.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`)}`\n}\n\n/**\n * 全部可注入的 token 变量名(含 `--cw-` 前缀)。\n *\n * 供宿主自检(例如校验设计令牌是否齐全)或非 React 场景下手写 CSS。\n */\nexport const CUT_THEME_TOKEN_NAMES: readonly string[] = Object.freeze([\n 'background',\n 'foreground',\n 'card',\n 'cardForeground',\n 'popover',\n 'popoverForeground',\n 'primary',\n 'primaryForeground',\n 'secondary',\n 'secondaryForeground',\n 'muted',\n 'mutedForeground',\n 'accent',\n 'accentForeground',\n 'destructive',\n 'destructiveForeground',\n 'warning',\n 'warningForeground',\n 'success',\n 'border',\n 'input',\n 'ring',\n 'radius',\n 'fontSans',\n 'fontMono',\n 'timelineBg',\n 'timelineTrack',\n 'timelineGrid',\n 'clipVideo',\n 'clipAudio',\n 'clipImage',\n 'clipText',\n 'clipSubtitle',\n 'playhead',\n 'stageBg',\n 'overlay',\n 'badgeScrim',\n 'badgeScrimForeground',\n].map(toCssVarName))\n\n/**\n * 把 token 对象转换成可直接展开到 `style` 的 CSS 变量集合。\n *\n * 空值 / 非字符串会被跳过(例如宿主从 JSON 配置里读到了 `undefined`),\n * 这样未传的 token 保持内置默认值,不会被空字符串覆盖成无效值。\n */\nexport function tokensToCssVars(tokens?: CutThemeTokens): CSSProperties {\n if (tokens == null) return {}\n const style: Record<string, string> = {}\n for (const [key, value] of Object.entries(tokens)) {\n if (typeof value !== 'string' || value.trim().length === 0) continue\n style[toCssVarName(key)] = value\n }\n return style as CSSProperties\n}\n","/**\n * 工作台 Provider:UI 状态 + 主题 + Portal 容器。\n *\n * 与 `CutEditorProvider`(数据与引擎)分开:\n * - `CutEditorProvider` 由 `@cut-workbench/react` 提供,决定「编辑哪个工程」;\n * - `CutWorkbenchProvider` 决定「这一次挂载的布局、主题和宿主桥」。\n *\n * 这样宿主可以在切换主题/布局时重建 UI,而不必重建编辑器(反之亦然)。\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport { useCutEditor } from '@cut-workbench/react'\nimport type { HostBridge } from '@cut-workbench/core'\nimport { createDefaultBridge } from './default-bridge'\nimport { TooltipProvider } from './components/ui/tooltip'\nimport {\n DEFAULT_CONFIG,\n WorkbenchContext,\n type WorkbenchConfig,\n type WorkbenchContextValue,\n} from './context'\nimport { PortalContainerProvider } from './portal-container'\nimport { createUiStore } from './store/ui-store'\nimport { resolveTheme, useHostDarkClass } from './theme'\nimport { tokensToCssVars, type CutThemeTokens } from './theme-tokens'\nimport { setDefaultBridge } from './lib/notify'\n\nexport type { WorkbenchTheme, WorkbenchConfig } from './context'\nexport { useWorkbench, useUi, useUiStoreApi } from './context'\n\nexport interface CutWorkbenchProviderProps {\n children: ReactNode\n /** 宿主桥;不传则使用浏览器内置实现 */\n bridge?: HostBridge\n /**\n * 主题:\n * - `light` / `dark`:固定主题,不理会宿主;\n * - `inherit`:跟随宿主 `<html class=\"dark\">`(MutationObserver 实时同步),\n * 折算成根节点的 `data-cw-theme`,因此库内 `dark:` 变体同样生效。\n */\n theme?: 'light' | 'dark' | 'inherit'\n /**\n * 覆盖任意 `--cw-*` token(key 不带 `--cw-` 前缀)。\n * 通过根节点 inline style 应用,不依赖宿主的 CSS 引入顺序与特异性。\n */\n tokens?: CutThemeTokens\n /** 功能开关(只合并顶层字段) */\n config?: Partial<Omit<WorkbenchConfig, 'features'>> & {\n features?: Partial<WorkbenchConfig['features']>\n }\n className?: string\n}\n\n/**\n * 工作台根 Provider。\n *\n * 同时负责:\n * 1. 创建本次挂载的 UI store(面板尺寸、缩放、对话框);\n * 2. 提供 Radix portal 的挂载点(样式作用域与 z-index 都依赖它);\n * 3. 设置 `data-cw-theme`,让库内 token 生效。\n */\nexport function CutWorkbenchProvider({\n children,\n bridge,\n theme = 'dark',\n tokens,\n config,\n className,\n}: CutWorkbenchProviderProps) {\n // 必须有编辑器才能确定「这个 UI 属于哪个工程」,顺带在缺 Provider 时尽早报错\n useCutEditor()\n\n const [ui] = useState(() => createUiStore())\n const resolvedBridge = useMemo(() => bridge ?? createDefaultBridge(), [bridge])\n\n // 让非 hook 路径(reportError 等)也能尊重宿主的 notify。\n // 卸载时恢复成「未注册」,避免已销毁的实例继续接管后续提示。\n useEffect(() => {\n setDefaultBridge(resolvedBridge)\n return () => setDefaultBridge(null)\n }, [resolvedBridge])\n\n const mergedConfig = useMemo<WorkbenchConfig>(\n () => ({\n ...DEFAULT_CONFIG,\n ...config,\n features: { ...DEFAULT_CONFIG.features, ...(config?.features ?? {}) },\n }),\n [config],\n )\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)\n const rootRef = useRef<HTMLDivElement | null>(null)\n\n // callback ref:挂载/卸载都走同一条路径,不需要额外 effect\n const attachRoot = useCallback((node: HTMLDivElement | null) => {\n rootRef.current = node\n setPortalContainer(node)\n }, [])\n\n // `inherit` 读宿主 `<html class=\"dark\">`,折算成库自己的 data-cw-theme。\n // 显式主题下订阅依然存在,但快照不变、不会触发渲染(一个根节点一个 observer)。\n const hostDark = useHostDarkClass()\n const resolvedTheme = resolveTheme(theme, hostDark)\n\n // token 注入:inline style 优先级高于任何样式表(含暗色 token 块),\n // 因此宿主不需要关心 CSS 引入顺序,也不需要提高选择器特异性。\n const tokenStyle = useMemo(() => tokensToCssVars(tokens), [tokens])\n\n const value = useMemo<WorkbenchContextValue>(\n () => ({\n ui,\n bridge: resolvedBridge,\n config: mergedConfig,\n theme: { mode: theme, resolved: resolvedTheme },\n portalContainer,\n }),\n [ui, resolvedBridge, mergedConfig, theme, resolvedTheme, portalContainer],\n )\n\n return (\n <WorkbenchContext.Provider value={value}>\n {/* 让 `components/ui/` 里的 Portal 组件在裸用时也能找到挂载点 */}\n <PortalContainerProvider container={portalContainer}>\n {/*\n * 根节点必须是**真实盒子**,不能用 `display: contents`。\n *\n * `contents` 会让元素自己不生成盒子,于是它的 background-color 完全不渲染、\n * 高度也变成 auto —— 工作台撑不满宿主给的高度,内部三栏的间距全塌。\n * 代价是它成为布局中的一个层级,所以这里显式 `height: 100%` + `min-height: 0`,\n * 让它在「父元素有确定高度」和「父元素只有 min-height」两种宿主下都能撑满。\n *\n * 关于 `dark` class 的双写:库内所有暗色样式都走 `data-cw-theme`,不依赖它;\n * 保留是为了兼容「宿主用 `.cw-root.dark` 之类的选择器做钩子」的场景,\n * 且 `inherit` 模式下它也表达了「宿主当前是深色」,顺手给宿主一个统一标记。\n */}\n <div\n ref={attachRoot}\n className={['cw-root', resolvedTheme === 'dark' ? 'dark' : null, className]\n .filter(Boolean)\n .join(' ')}\n data-cw-theme={resolvedTheme}\n data-cw-theme-mode={theme}\n data-cw-root=\"\"\n style={tokenStyle}\n >\n <TooltipProvider delayDuration={300}>{children}</TooltipProvider>\n </div>\n </PortalContainerProvider>\n </WorkbenchContext.Provider>\n )\n}\n","/**\r\n * 统一接入入口:`initCutWorkbench(container, options)`。\r\n *\r\n * 面向三类接入方:\r\n * - **非 React 项目 / 微前端**:只要一个容器和一个函数调用;\r\n * - **Electron 渲染进程**:传入 `bridge` 把「下载」换成「保存到指定路径」;\r\n * - **script 标签直接引入**:UMD 产物暴露同一个函数。\r\n *\r\n * 设计要点:\r\n * - **同步返回句柄**。内部渲染是异步的,但接入方写代码应该是线性的;\r\n * 真正的就绪通过 `on('ready')` 或 `whenReady()` 通知。\r\n * - **dispose 幂等且彻底**:卸载 React 树、释放引擎、关闭 AudioContext。\r\n * - **不缓存全局实例**。同一页面调用两次得到两个互不干扰的工作台。\r\n */\r\n\r\nimport { createRoot, type Root } from 'react-dom/client'\r\nimport { createElement } from 'react'\r\nimport {\r\n createEditor,\r\n type CutEditorHandle,\r\n type CutEventMap,\r\n} from '@cut-workbench/core/engine'\r\nimport {\r\n createIndexedDbStorage,\r\n createMemoryStorage,\r\n deserializeProject,\r\n type HostBridge,\r\n type Project,\r\n type SerializedProject,\r\n type StoragePort,\r\n} from '@cut-workbench/core'\r\nimport { CutEditorProvider } from '@cut-workbench/react'\r\nimport { CutWorkbench, type CutWorkbenchToolbarOptions } from './CutWorkbench'\r\nimport { createDefaultBridge } from './default-bridge'\r\nimport { CutWorkbenchProvider } from './provider'\r\nimport type { WorkbenchConfig } from './context'\r\nimport type { CutThemeTokens } from './theme-tokens'\r\n\r\nexport interface InitCutWorkbenchOptions {\r\n /** 宿主桥;不传则用浏览器内置实现 */\r\n bridge?: HostBridge\r\n /** 工程持久化;不传用 IndexedDB(命名为 cut-workbench) */\r\n storage?: StoragePort\r\n /** 持久化命名空间(多项目 / 多实例隔离);默认 'default' */\r\n namespace?: string\r\n /** 初始工程 */\r\n project?: Project | SerializedProject\r\n /** 初始素材文件 */\r\n assets?: File[]\r\n /** 主题:`inherit` 跟随宿主 `<html class=\"dark\">` */\r\n theme?: 'light' | 'dark' | 'inherit'\r\n /** 覆盖任意 `--cw-*` token(key 不带 `--cw-` 前缀),透传给 Provider */\r\n tokens?: CutThemeTokens\r\n /** 功能开关 */\r\n config?: Partial<Omit<WorkbenchConfig, 'features'>> & {\r\n features?: Partial<WorkbenchConfig['features']>\r\n }\r\n /** 是否强制能力探测阻断页 */\r\n enforceCapability?: boolean\r\n /** 顶栏的局部自定义(品牌 / 工程名 / 状态 / 追加动作 / 文案) */\r\n toolbar?: CutWorkbenchToolbarOptions\r\n /** 容器 className(控制高度等) */\r\n className?: string\r\n /** 是否在初始化后自动保存(每次结构性变更后节流保存) */\r\n autoSave?: boolean\r\n autoSaveIntervalMs?: number\r\n /** 就绪回调 */\r\n onReady?: (instance: CutWorkbenchInstance) => void\r\n /** 错误回调 */\r\n onError?: (error: unknown) => void\r\n}\r\n\r\nexport interface CutWorkbenchInstance {\r\n readonly editor: CutEditorHandle\r\n readonly container: HTMLElement\r\n readonly bridge: HostBridge\r\n\r\n /** 等待 React 树挂载完成 */\r\n whenReady(): Promise<void>\r\n /** 卸载 React 树(保留工程数据与编辑器) */\r\n unmount(): void\r\n /** 彻底销毁(引擎资源一并释放);幂等 */\r\n dispose(): Promise<void>\r\n\r\n // —— 工程 ——\r\n getProject(): Project\r\n getAssets(): ReturnType<CutEditorHandle['getAssets']>\r\n serialize(): SerializedProject\r\n loadProject(input: SerializedProject | Project): Promise<void>\r\n saveProject(): Promise<{ ok: boolean; error?: string }>\r\n\r\n // —— 素材 ——\r\n addFiles(files: File[]): Promise<string[]>\r\n\r\n // —— 播放 ——\r\n play(): void\r\n pause(): void\r\n toggle(): void\r\n seek(us: number): void\r\n getPlayhead(): number\r\n\r\n // —— 编辑 ——\r\n undo(): void\r\n redo(): void\r\n\r\n // —— 事件 ——\r\n on<K extends keyof CutEventMap>(type: K, handler: (payload: CutEventMap[K]) => void): () => void\r\n}\r\n\r\nfunction resolveContainer(container: HTMLElement | string): HTMLElement {\r\n if (typeof container !== 'string') return container\r\n const el = document.querySelector(container)\r\n if (el == null) throw new Error(`[cut-workbench] 找不到容器元素:${container}`)\r\n return el as HTMLElement\r\n}\r\n\r\n/**\r\n * 在指定容器里初始化一个工作台。\r\n *\r\n * @example 非 React 项目\r\n * ```js\r\n * const wb = initCutWorkbench('#app', { theme: 'dark' })\r\n * wb.on('export:done', ({ blob }) => saveSomewhere(blob))\r\n * ```\r\n *\r\n * @example Electron 渲染进程\r\n * ```js\r\n * const wb = initCutWorkbench(container, {\r\n * bridge: createElectronBridge(window.cutHost),\r\n * })\r\n * ```\r\n */\r\nexport function initCutWorkbench(\r\n container: HTMLElement | string,\r\n options: InitCutWorkbenchOptions = {},\r\n): CutWorkbenchInstance {\r\n const host = resolveContainer(container)\r\n const bridge = options.bridge ?? createDefaultBridge()\r\n\r\n // 有 bridge 提供的 storage 优先;否则按浏览器能力选 IndexedDB 或内存\r\n const storage =\r\n options.storage ??\r\n (typeof indexedDB !== 'undefined'\r\n ? createIndexedDbStorage({ dbName: 'cut-workbench', version: 1 })\r\n : createMemoryStorage())\r\n const namespace = options.namespace ?? 'default'\r\n\r\n const editor = createEditor({\r\n bridge,\r\n storage,\r\n namespace,\r\n autoLoad: false,\r\n })\r\n\r\n const errors: unknown[] = []\r\n const reportError = (err: unknown) => {\r\n errors.push(err)\r\n if (options.onError != null) options.onError(err)\r\n else console.error('[cut-workbench]', err)\r\n }\r\n\r\n // 引擎里的异步错误统一走这里,避免 unhandled rejection 静默丢失\r\n editor.events.on('error', ({ error }) => reportError(error))\r\n\r\n let root: Root | null = createRoot(host)\r\n // 显式标注类型:直接在 Promise executor 里赋值会被 TS 收窄为 never\r\n let readyResolve: (() => void) | undefined\r\n const readyPromise = new Promise<void>((resolve) => {\r\n readyResolve = resolve\r\n })\r\n let disposed = false\r\n let autoSaveTimer: number | null = null\r\n\r\n function renderTree() {\r\n root!.render(\r\n createElement(\r\n CutEditorProvider,\r\n { editor } as never,\r\n createElement(\r\n CutWorkbenchProvider,\r\n {\r\n bridge,\r\n theme: options.theme ?? 'dark',\r\n tokens: options.tokens,\r\n config: options.config,\r\n } as never,\r\n createElement(CutWorkbench, {\r\n className: options.className ?? 'h-full w-full',\r\n enforceCapability: options.enforceCapability ?? true,\r\n toolbar: options.toolbar,\r\n onSaveProject: () => void instance.saveProject(),\r\n onOpenProject: async () => {\r\n const saved = await storage.load(namespace)\r\n if (saved == null) return\r\n const { project, assets, missingAssetIds } = deserializeProject(saved)\r\n editor.setProject(project, assets, missingAssetIds)\r\n },\r\n }),\r\n ),\r\n ),\r\n )\r\n }\r\n\r\n function scheduleAutoSave() {\r\n if (options.autoSave !== true || autoSaveTimer != null) return\r\n autoSaveTimer = window.setTimeout(() => {\r\n autoSaveTimer = null\r\n void saveProject().catch(reportError)\r\n }, options.autoSaveIntervalMs ?? 2000)\r\n }\r\n\r\n async function saveProject() {\r\n return storage.save(namespace, editor.serialize())\r\n }\r\n\r\n const unsubscribeAutoSave =\r\n options.autoSave === true\r\n ? editor.events.on('project:changed', () => scheduleAutoSave())\r\n : () => undefined\r\n\r\n const instance: CutWorkbenchInstance = {\r\n editor,\r\n container: host,\r\n bridge,\r\n\r\n whenReady: () => readyPromise,\r\n\r\n unmount() {\r\n if (root == null) return\r\n root.unmount()\r\n root = null\r\n },\r\n\r\n async dispose() {\r\n if (disposed) return\r\n disposed = true\r\n unsubscribeAutoSave()\r\n if (autoSaveTimer != null) window.clearTimeout(autoSaveTimer)\r\n if (options.autoSave === true) {\r\n // 关闭前落一次盘,避免最后的编辑丢失\r\n try {\r\n await saveProject()\r\n } catch (err) {\r\n reportError(err)\r\n }\r\n }\r\n instance.unmount()\r\n await editor.dispose()\r\n },\r\n\r\n getProject: () => editor.getProject(),\r\n getAssets: () => editor.getAssets(),\r\n serialize: () => editor.serialize(),\r\n\r\n async loadProject(input) {\r\n await editor.load(input)\r\n if (options.autoSave === true) await saveProject()\r\n },\r\n\r\n saveProject,\r\n\r\n async addFiles(files) {\r\n // 引擎是懒加载的:只有真正导入素材时才把 WebAV 拉进来,\r\n // 避免初始化时在非 Chromium 环境触发 OPFS 访问。\r\n const engine = await import('@cut-workbench/core/engine')\r\n const ids: string[] = []\r\n for (const file of files) {\r\n try {\r\n const result = await engine.importFile(editor.runtime.clips, file)\r\n editor.store.getState().addAsset(result.asset)\r\n ids.push(result.asset.id)\r\n } catch (err) {\r\n reportError(err)\r\n }\r\n }\r\n return ids\r\n },\r\n\r\n play: () => editor.play(),\r\n pause: () => editor.pause(),\r\n toggle: () => editor.toggle(),\r\n seek: (us) => editor.seek(us),\r\n getPlayhead: () => editor.getPlayhead(),\r\n\r\n undo: () => editor.undo(),\r\n redo: () => editor.redo(),\r\n\r\n on: (type, handler) => editor.events.on(type, handler),\r\n }\r\n\r\n // 初始工程 / 素材\r\n if (options.project != null) {\r\n void editor.load(options.project)\r\n }\r\n\r\n renderTree()\r\n\r\n // 先执行一次初始导入再报告 ready,避免接入方拿到句柄时素材还没进 store\r\n void (async () => {\r\n try {\r\n if (options.assets != null && options.assets.length > 0) {\r\n await instance.addFiles(options.assets)\r\n }\r\n } catch (err) {\r\n reportError(err)\r\n } finally {\r\n readyResolve?.()\r\n options.onReady?.(instance)\r\n }\r\n })()\r\n\r\n return instance\r\n}\r\n\r\nexport type { CutEventMap }\r\n","/**\n * 原子组件的样式根。\n *\n * 为什么需要:库的 CSS 全部作用域在 `.cw-root` 下(为了不污染宿主)。原子组件\n * 脱离 `<CutWorkbench>` 使用时,如果没有 `.cw-root` 祖先,所有 utility 都会失效 ——\n * 组件能渲染但完全没有样式(片段没有色块、按钮没有圆角)。\n *\n * 用法:\n * ```tsx\n * <AtomsRoot theme=\"dark\" tokens={{ clipVideo: '#4d6bfe' }}>\n * <ClipBlock {...props} />\n * </AtomsRoot>\n * ```\n *\n * `<CutWorkbench>` 内部已经自带 `.cw-root`,不需要再包一层。\n *\n * 它只提供「样式作用域 + 主题变量 + Portal 挂载点 + Tooltip 兜底」,不创建编辑器、\n * 不接管布局,因此可以放心地包在任何位置。\n */\n\nimport { useCallback, useMemo, useState, type CSSProperties, type ReactNode } from 'react'\nimport { TooltipProvider } from './components/ui/tooltip'\nimport { PortalContainerProvider } from './portal-container'\nimport { resolveTheme, useHostDarkClass } from './theme'\nimport { tokensToCssVars, type CutThemeTokens } from './theme-tokens'\n\nexport interface AtomsRootProps {\n children: ReactNode\n /**\n * 主题:\n * - `light` / `dark`:固定主题;\n * - `inherit`:跟随宿主 `<html class=\"dark\">`(MutationObserver 实时同步)。\n */\n theme?: 'light' | 'dark' | 'inherit'\n /** 覆盖任意 `--cw-*` token(key 不带 `--cw-` 前缀) */\n tokens?: CutThemeTokens\n className?: string\n /** 透传给根节点的 style(常用于给个高度);与 `tokens` 同时存在时以 tokens 为准 */\n style?: CSSProperties\n}\n\n/**\n * 原子组件的样式与主题容器。\n *\n * 与 `<CutWorkbenchProvider>` 的区别:后者提供编辑器 UI 状态(面板尺寸、对话框),\n * 原子组件不需要这些。这里只做「让样式生效」这一件事。\n *\n * 但 Portal 挂载点必须同样具备:原子组件里的 Tooltip / Popover / Select\n * 也会往 portal 里渲染内容,挂到 `document.body` 就会丢掉 `.cw-root` 作用域\n * (表现为「下拉菜单没有背景、出现在视口外」)。\n */\nexport function AtomsRoot({\n children,\n theme = 'dark',\n tokens,\n className,\n style,\n}: AtomsRootProps) {\n const hostDark = useHostDarkClass()\n const resolvedTheme = resolveTheme(theme, hostDark)\n const tokenStyle = useMemo(() => tokensToCssVars(tokens), [tokens])\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)\n const attachRoot = useCallback((node: HTMLDivElement | null) => setPortalContainer(node), [])\n\n const classes = ['cw-root', resolvedTheme === 'dark' ? 'dark' : null, className]\n .filter(Boolean)\n .join(' ')\n\n return (\n <PortalContainerProvider container={portalContainer}>\n <div\n ref={attachRoot}\n className={classes}\n data-cw-theme={resolvedTheme}\n data-cw-theme-mode={theme}\n data-cw-atoms-root=\"\"\n // tokens 的优先级高于宿主的 style(同一条属性上后者覆盖前者),\n // 这样「用 style 给高度 + 用 tokens 换肤」可以同时生效\n style={{ ...style, ...tokenStyle }}\n >\n <TooltipProvider delayDuration={300}>{children}</TooltipProvider>\n </div>\n </PortalContainerProvider>\n )\n}\n"],"ignoreList":[81,82],"mappings":"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"}