auto-save 2026-05-14 04:26 (~5)
This commit is contained in:
@@ -10,7 +10,7 @@ import { type NodeProps, useReactFlow } from "@xyflow/react"
|
||||
import {
|
||||
Link2, Upload, Download, Scissors, Image as ImageIcon,
|
||||
Mic, Languages, FileEdit, Film, FileVideo, Loader2, Plus, X, LayoutGrid, Maximize2,
|
||||
Copy, Trash2, Move, PanelLeft, PanelRight, PanelBottom, ChevronLeft, ChevronRight,
|
||||
Copy, Trash2, Move, PanelLeft, PanelRight, PanelBottom, ChevronLeft, ChevronRight, SlidersHorizontal,
|
||||
} from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
import { NodeShell, type NodeStatus, type NodeKind } from "./node-shell"
|
||||
@@ -124,7 +124,7 @@ function clamp(value: number, min: number, max: number) {
|
||||
return Math.max(min, Math.min(max, value))
|
||||
}
|
||||
|
||||
const THUMBNAIL_HEIGHT = 176
|
||||
const THUMBNAIL_HEIGHT = 192
|
||||
const FLOATING_PANEL_EDGE_INSET = 8
|
||||
const FRAME_TARGET_OPTIONS: Array<{ value: FrameExtractTarget; label: string; hint: string }> = [
|
||||
{ value: "balanced", label: "综合关键帧", hint: "清晰、去重、变化、时间覆盖" },
|
||||
@@ -439,6 +439,7 @@ function FrameExtractQuickBar({
|
||||
}) {
|
||||
const option = FRAME_TARGET_OPTIONS.find((item) => item.value === target) ?? FRAME_TARGET_OPTIONS[0]
|
||||
const qualityOption = FRAME_QUALITY_OPTIONS.find((item) => item.value === quality) ?? FRAME_QUALITY_OPTIONS[1]
|
||||
const [settingsOpen, setSettingsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -446,39 +447,16 @@ function FrameExtractQuickBar({
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<select
|
||||
value={target}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onTargetChange(e.target.value as FrameExtractTarget)}
|
||||
className="h-7 w-full cursor-pointer rounded-md border border-white/12 bg-white/[0.08] px-1.5 text-[10px] font-semibold text-white outline-none transition focus:ring-2 focus:ring-violet-300/70 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
aria-label="选择自动抽帧目标"
|
||||
title={option.hint}
|
||||
>
|
||||
{FRAME_TARGET_OPTIONS.map((item) => (
|
||||
<option key={item.value} value={item.value}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="mt-1 flex gap-1">
|
||||
<div className="flex gap-1">
|
||||
<select
|
||||
value={count}
|
||||
value={target}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onCountChange(Number(e.target.value))}
|
||||
className="h-7 min-w-0 flex-1 cursor-pointer rounded-md border border-white/12 bg-white/[0.08] px-1.5 text-[10px] font-bold text-white outline-none transition focus:ring-2 focus:ring-violet-300/70 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
aria-label="选择抽帧张数"
|
||||
onChange={(e) => onTargetChange(e.target.value as FrameExtractTarget)}
|
||||
className="h-8 min-w-0 flex-1 cursor-pointer rounded-md border border-white/12 bg-white/[0.08] px-2 text-[10.5px] font-semibold text-white outline-none transition focus:ring-2 focus:ring-violet-300/70 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
aria-label="选择自动抽帧目标"
|
||||
title={option.hint}
|
||||
>
|
||||
{FRAME_COUNT_OPTIONS.map((item) => (
|
||||
<option key={item} value={item}>{item} 张</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={quality}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onQualityChange(e.target.value as FrameExtractQuality)}
|
||||
title={qualityOption.hint}
|
||||
className="h-7 min-w-0 flex-1 cursor-pointer rounded-md border border-white/12 bg-white/[0.08] px-1.5 text-[10px] font-bold text-white outline-none transition focus:ring-2 focus:ring-violet-300/70 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
aria-label="选择抽帧精度"
|
||||
>
|
||||
{FRAME_QUALITY_OPTIONS.map((item) => (
|
||||
{FRAME_TARGET_OPTIONS.map((item) => (
|
||||
<option key={item.value} value={item.value}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -487,12 +465,50 @@ function FrameExtractQuickBar({
|
||||
disabled={disabled}
|
||||
onClick={onAnalyze}
|
||||
title={hasFrames ? "追加自动抽帧" : "自动抽帧"}
|
||||
className="inline-flex h-7 shrink-0 items-center justify-center gap-1 rounded-md bg-gradient-to-r from-indigo-500 to-violet-500 px-2 text-[10.5px] font-bold text-white shadow-lg shadow-violet-950/35 transition hover:opacity-95 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
className="inline-flex h-8 shrink-0 items-center justify-center gap-1 rounded-md bg-gradient-to-r from-indigo-500 to-violet-500 px-2.5 text-[10.5px] font-bold text-white shadow-lg shadow-violet-950/35 transition hover:opacity-95 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
>
|
||||
{running ? <Loader2 className="h-3 w-3 animate-spin" /> : <Scissors className="h-3 w-3" />}
|
||||
{running ? "抽取" : hasFrames ? "追加" : "抽帧"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={() => setSettingsOpen((open) => !open)}
|
||||
title={`设置 · ${count} 张 · ${qualityOption.label}`}
|
||||
className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-white/12 transition disabled:cursor-not-allowed disabled:opacity-45 ${
|
||||
settingsOpen ? "bg-white text-violet-700" : "bg-white/[0.06] text-white/75 hover:bg-white/[0.12] hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<SlidersHorizontal className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
{settingsOpen && (
|
||||
<div className="mt-1.5 flex gap-1">
|
||||
<select
|
||||
value={count}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onCountChange(Number(e.target.value))}
|
||||
className="h-7 min-w-0 flex-1 cursor-pointer rounded-md border border-white/12 bg-white/[0.08] px-1.5 text-[10px] font-bold text-white outline-none transition focus:ring-2 focus:ring-violet-300/70 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
aria-label="选择抽帧张数"
|
||||
>
|
||||
{FRAME_COUNT_OPTIONS.map((item) => (
|
||||
<option key={item} value={item}>{item} 张</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={quality}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onQualityChange(e.target.value as FrameExtractQuality)}
|
||||
title={qualityOption.hint}
|
||||
className="h-7 min-w-0 flex-1 cursor-pointer rounded-md border border-white/12 bg-white/[0.08] px-1.5 text-[10px] font-bold text-white outline-none transition focus:ring-2 focus:ring-violet-300/70 disabled:cursor-not-allowed disabled:opacity-45"
|
||||
aria-label="选择抽帧精度"
|
||||
>
|
||||
{FRAME_QUALITY_OPTIONS.map((item) => (
|
||||
<option key={item.value} value={item.value}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -553,7 +569,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
|
||||
const toolWidth = Math.max(148, thumbNaturalWidth)
|
||||
const target = d.frameTargets[j.id] ?? "balanced"
|
||||
const count = d.frameCounts[j.id] ?? 5
|
||||
const quality = d.frameQualities[j.id] ?? "accurate"
|
||||
const quality = d.frameQualities[j.id] ?? "ultra"
|
||||
const jHasFrames = j.frames.length > 0
|
||||
const jRunning = ["splitting", "transcribing"].includes(j.status)
|
||||
return (
|
||||
@@ -576,7 +592,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
|
||||
onAnalyze={() => d.onAnalyzeJob(j.id, { mode: jHasFrames ? "append" : "replace" })}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-[72px] rounded-lg border border-white/10 bg-white/[0.03]" />
|
||||
<div className="h-[44px] rounded-lg border border-white/10 bg-white/[0.03]" />
|
||||
)}
|
||||
<div
|
||||
className={`relative self-center rounded-md overflow-visible border shadow-lg transition hover:-translate-y-0.5 ${
|
||||
|
||||
Reference in New Issue
Block a user