auto-save 2026-05-12 19:36 (~3)

This commit is contained in:
2026-05-12 19:36:53 +08:00
parent ecef98842f
commit 902c3ed936
3 changed files with 56 additions and 62 deletions

View File

@@ -251,6 +251,13 @@
"message": "auto-save 2026-05-12 19:25 (~4)",
"hash": "c159668",
"files_changed": 4
},
{
"ts": "2026-05-12T19:31:21+08:00",
"type": "commit",
"message": "auto-save 2026-05-12 19:31 (~2)",
"hash": "ecef988",
"files_changed": 2
}
]
}

View File

@@ -297,7 +297,7 @@ export function Dashboard({ data }: Props) {
</>
)}
{/* ---- Download — Kanban ---- */}
{/* ---- Download — 只显示元数据,视频播放器移到 Keyframe section ---- */}
{key === "download" && (
!hasVideo ? (
<KanbanCard tone="orange" tags={["yt-dlp"]} title={isDownloading ? "下载中…" : "等待提交"}>
@@ -305,20 +305,19 @@ export function Dashboard({ data }: Props) {
</KanbanCard>
) : (
<>
<KanbanCard tone="orange" tags={["视频源"]} title={job!.url.startsWith("upload://") ? "本地上传" : "yt-dlp 下载"}>
<video
ref={videoRef}
src={videoUrl(job!.id)}
controls
onTimeUpdate={(e) => setVideoT((e.target as HTMLVideoElement).currentTime)}
className="block w-full bg-black rounded-md mt-1"
/>
<KanbanCard tone="orange" tags={["视频源", job!.url.startsWith("upload://") ? "上传" : "yt-dlp"]} title={job!.url.startsWith("upload://") ? "本地上传" : "TK 下载"}>
<div className="text-[11px] text-[var(--text-soft)] truncate font-mono mt-1">
{job!.url.startsWith("upload://") ? `📎 ${job!.url.slice(9)}` : `🔗 ${job!.url}`}
</div>
</KanbanCard>
<KanbanCard tone="amber" tags={["元数据"]} title="视频信息">
<div className="grid grid-cols-3 gap-3 text-[11px] font-mono">
<div><div className="text-[var(--text-faint)] text-[9.5px]"></div><div className="text-[var(--text-strong)] text-[12.5px] mt-0.5">{job!.width}×{job!.height}</div></div>
<div><div className="text-[var(--text-faint)] text-[9.5px]"></div><div className="text-[var(--text-strong)] text-[12.5px] mt-0.5">{job!.duration.toFixed(1)}s</div></div>
<div><div className="text-[var(--text-faint)] text-[9.5px]"></div><div className="text-[var(--text-strong)] text-[12.5px] mt-0.5">{job!.url.startsWith("upload://") ? "上传" : "TK"}</div></div>
<div><div className="text-[var(--text-faint)] text-[9.5px]"></div><div className="text-[var(--text-strong)] text-[12.5px] mt-0.5">~9MB</div></div>
</div>
<div className="kanban-meta">
/
</div>
</KanbanCard>
</>
@@ -340,25 +339,40 @@ export function Dashboard({ data }: Props) {
</>
)}
{/* ---- Keyframe — Kanban 卡片 ---- */}
{/* ---- Keyframe — Kanban 卡片(视频播放器 + 加帧 + 缩略图都在这里) ---- */}
{key === "keyframe" && (
<div className="space-y-3">
{hasVideo && job && (
<KanbanCard tone="green" tags={["手动加帧"]} title="从视频任意时间点抽 1 张">
<button
type="button"
disabled={addingFrame}
onClick={async () => {
const t = videoRef.current?.currentTime ?? videoT
setAddingFrame(true)
try { await data.onAddManualFrame(t) } finally { setAddingFrame(false) }
}}
className="mt-1 w-full text-[12px] py-1.5 rounded-md bg-emerald-500 hover:bg-emerald-400 text-white disabled:opacity-50 inline-flex items-center justify-center gap-1.5"
>
{addingFrame ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plus className="h-3.5 w-3.5" />}
{addingFrame ? "抽帧中…" : `${videoT.toFixed(1)}s 加为关键帧`}
</button>
</KanbanCard>
<>
<KanbanCard tone="violet" tags={["视频"]} title="拖时间轴选帧">
<video
ref={videoRef}
src={videoUrl(job.id)}
controls
onTimeUpdate={(e) => setVideoT((e.target as HTMLVideoElement).currentTime)}
className="block w-full bg-black rounded-md mt-1"
/>
<div className="kanban-meta">
<span className="font-mono"> {videoT.toFixed(2)}s</span>
<span className="ml-auto">{job.width}×{job.height} · {job.duration.toFixed(1)}s</span>
</div>
</KanbanCard>
<KanbanCard tone="green" tags={["手动加帧"]} title="把当前时间点抽为关键帧">
<button
type="button"
disabled={addingFrame}
onClick={async () => {
const t = videoRef.current?.currentTime ?? videoT
setAddingFrame(true)
try { await data.onAddManualFrame(t) } finally { setAddingFrame(false) }
}}
className="mt-1 w-full text-[12.5px] py-2 rounded-md bg-emerald-500 hover:bg-emerald-400 text-white disabled:opacity-50 inline-flex items-center justify-center gap-1.5 font-medium"
>
{addingFrame ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plus className="h-3.5 w-3.5" />}
{addingFrame ? "抽帧中…" : `+ 把 ${videoT.toFixed(1)}s 加为关键帧`}
</button>
</KanbanCard>
</>
)}
{!hasFrames ? (
<KanbanCard tone="pink" tags={["分镜"]} title="等待解析后抽取">

View File

@@ -128,46 +128,19 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
</>
)}
{/* 已下载:内嵌视频 + 解析按钮 */}
{/* 已下载:仅元数据 + 解析按钮(视频播放器和加帧 controls 在左侧看板 Keyframe section */}
{hasVideo && job && (
<>
<video
ref={videoRef}
src={videoUrl(job.id)}
controls
onTimeUpdate={(e) => setVideoT((e.target as HTMLVideoElement).currentTime)}
className="w-full aspect-video rounded-md bg-black border border-black/10 dark:border-white/10"
/>
<div className="mt-2 flex items-center justify-between text-[10.5px] font-mono text-[var(--text-faint)]">
<span>{job.width}×{job.height} · {job.duration.toFixed(1)}s</span>
<span className="truncate ml-2 max-w-[120px]">
{job.url.startsWith("upload://") ? `📎 ${job.url.slice(9)}` : "🔗"}
</span>
<div className="rounded-md bg-black/30 border border-black/10 dark:border-white/10 px-3 py-2.5">
<div className="flex items-center justify-between text-[10.5px] font-mono text-[var(--text-faint)] mb-1">
<span></span>
<span>{job.url.startsWith("upload://") ? "📎 上传" : "🔗 链接"}</span>
</div>
<div className="text-[var(--text-strong)] text-[13px] font-mono">
{job.width}×{job.height} · {job.duration.toFixed(1)}s
</div>
</div>
{/* 手动拖加帧(已抽过帧才出现) */}
{hasFrames && (
<button
type="button"
disabled={addingFrame}
onClick={async (e) => {
e.stopPropagation()
const t = videoRef.current?.currentTime ?? 0
setAddingFrame(true)
try {
await d.onAddManualFrame(t)
} finally {
setAddingFrame(false)
}
}}
className="mt-2 w-full text-[11.5px] py-2 rounded-md border border-dashed border-emerald-400/40 bg-emerald-400/5 hover:bg-emerald-400/10 text-emerald-300 dark:text-emerald-300 disabled:opacity-50 flex items-center justify-center gap-1.5"
title="把视频当前播放时间点的画面加为新关键帧"
>
{addingFrame ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plus className="h-3.5 w-3.5" />}
{addingFrame ? "抽帧中…" : `+ 把 ${videoT.toFixed(1)}s 这一帧加为关键帧`}
</button>
)}
<button
type="button"
disabled={isAnalyzing || d.analyzing}