@cut-workbench/workbench 0.1.0 → 0.2.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/README.md +153 -9
- package/dist/CutWorkbench.d.ts +26 -1
- package/dist/auto.cjs +4 -0
- package/dist/auto.d.ts +19 -0
- package/dist/auto.js +8 -0
- package/dist/components/common/UnsupportedBrowser.d.ts +6 -1
- package/dist/components/layout/AppShell.d.ts +17 -3
- package/dist/components/layout/WorkbenchToolbar.d.ts +39 -2
- package/dist/cut-workbench.css +47 -6
- package/dist/features/lab/index.d.ts +5 -1
- package/dist/format--DhqHMgX.cjs +208 -0
- package/dist/format--DhqHMgX.cjs.map +1 -0
- package/dist/format-CV2aoZZC.js +109 -0
- package/dist/format-CV2aoZZC.js.map +1 -0
- package/dist/index.cjs +500 -1620
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +419 -1518
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +3 -0
- package/dist/lab-disabled.cjs +6 -0
- package/dist/lab-disabled.cjs.map +1 -0
- package/dist/lab-disabled.d.ts +13 -0
- package/dist/lab-disabled.js +5 -0
- package/dist/lab-disabled.js.map +1 -0
- package/dist/lab.cjs +1178 -0
- package/dist/lab.cjs.map +1 -0
- package/dist/lab.d.ts +32 -0
- package/dist/lab.js +1177 -0
- package/dist/lab.js.map +1 -0
- package/dist/umd/cut-workbench.umd.js +1 -1
- package/package.json +84 -67
package/dist/lab.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lab.cjs","names":["Card","cn","CardHeader","FlaskConical","CardTitle","CardDescription","Badge","CardContent","useRef","useCallback","useState","useRef","_Fragment","useState","Loader2","useState","readMp4Info","concatMp4","formatResolution","formatDuration","AlertTriangle","useState","mixAudioIntoMp4","useState","isWebGL2Available","useRef","useCallback","renderChromakeyFrame","createMp4Clip","useState","useRef","parseSrt","createSubtitlePreviewClip","useCallback","renderSubtitleFrame","burnSubtitles","Square","Play","useState","useRef","splitMp4Tracks","Split","formatDuration","useState","extractKeyframes","_Fragment","formatTimecode","useState","useRef","useCallback","createDemoCanvasStream","fixRecordedDuration","Circle","_Fragment","Play","Pause","Square","formatRecorderState","AVRecorder","useState","useRef","FrameCounterClip","useCallback","renderClipFrameToCanvas","exportCustomClip","Square","Play","formatTimecode","FlaskConical"],"sources":["../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"],"sourcesContent":["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"],"mappings":"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"}
|
package/dist/lab.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lab 加载器(`@cut-workbench/workbench/lab` 子入口)。
|
|
3
|
+
*
|
|
4
|
+
* 能力实验室静态引入 `@cut-workbench/core/lab`,而那个子入口静态引入
|
|
5
|
+
* `@webav/av-recorder`(屏幕录制)。它必须与主包隔离,否则关掉 lab 也白关。
|
|
6
|
+
*
|
|
7
|
+
* ## 为什么是子入口,而不是主包里的 `lazy(() => import(...))`
|
|
8
|
+
*
|
|
9
|
+
* 实测(Vite 8 / Rolldown):动态 import 只要出现在模块图里,打包器就会产出
|
|
10
|
+
* 对应 chunk —— 即使所在分支被 `define` 折成死代码、主包里已经看不到任何
|
|
11
|
+
* 引用,那个 chunk 文件**仍然会写进 dist**。因此「运行时开关 + 动态 import」
|
|
12
|
+
* 只能让主包变小,删不掉 av-recorder 的 chunk。
|
|
13
|
+
*
|
|
14
|
+
* 拆分后:主包只保留 `import('@cut-workbench/workbench/lab')`(构建为 external,
|
|
15
|
+
* 自身不产出 chunk),由**宿主**的打包器决定解析到哪个文件。宿主想彻底移除 lab:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* // 宿主 vite.config.ts
|
|
19
|
+
* resolve: {
|
|
20
|
+
* alias: [
|
|
21
|
+
* { find: '@cut-workbench/workbench/lab', replacement: '@cut-workbench/workbench/lab-disabled' },
|
|
22
|
+
* ],
|
|
23
|
+
* },
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* 不配置时行为不变:lab 可用,且依然只在进入 lab 路由时才下载。
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ 这里**不要**再用 `React.lazy` 包一层:懒加载边界在调用方
|
|
29
|
+
* (`CutWorkbench` 对整个子入口做动态 import)。两层 lazy 会让 React 收到
|
|
30
|
+
* 「promise 解析出 promise」,报 `Element type is invalid`。
|
|
31
|
+
*/
|
|
32
|
+
export { LabPage } from './features/lab';
|