You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

108 lines
5.2 KiB

"use client";
import { useI18n } from "@/i18n";
import { getNodeOutputMediaType, SINGLE_INPUT_HANDLE_ID } from "@/utils/nodeHandles";
import type { EaseCurveNodeData, VideoStitchNodeData, WorkflowEdge, WorkflowNode } from "@/types";
interface ComposerProcessBodyProps {
node: WorkflowNode | null | undefined;
nodes: WorkflowNode[];
edges: WorkflowEdge[];
videoStitchData: VideoStitchNodeData | null;
easeCurveData: EaseCurveNodeData | null;
}
function getNodeDisplayTitle(node: WorkflowNode | undefined): string {
if (!node) return "Unknown";
const data = node.data as { customTitle?: unknown; inputPrompt?: unknown };
if (typeof data.customTitle === "string" && data.customTitle.trim()) return data.customTitle.trim();
if (typeof data.inputPrompt === "string" && data.inputPrompt.trim()) return data.inputPrompt.trim().slice(0, 32);
return node.type;
}
function nodeHasVideoOutput(node: WorkflowNode | undefined): boolean {
if (!node) return false;
const data = node.data as { outputVideo?: unknown; video?: unknown };
return Boolean(data.outputVideo || data.video);
}
function buildBezierPreviewPath(handles: [number, number, number, number]): string {
const [x1, y1, x2, y2] = handles;
return `M 8 92 C ${8 + x1 * 84} ${92 - y1 * 84}, ${8 + x2 * 84} ${92 - y2 * 84}, 92 8`;
}
function formatBezierHandles(handles: [number, number, number, number]): string {
return handles.map((value) => value.toFixed(2)).join(", ");
}
export function ComposerProcessBody({
node,
nodes,
edges,
videoStitchData,
easeCurveData,
}: ComposerProcessBodyProps) {
const { t } = useI18n();
if (node?.type === "videoStitch") {
const stitchEdges = edges.filter((edge) => {
if (edge.target !== node.id || edge.targetHandle !== SINGLE_INPUT_HANDLE_ID) return false;
const sourceNode = nodes.find((candidate) => candidate.id === edge.source);
return sourceNode ? getNodeOutputMediaType(sourceNode) === "video" : false;
});
return (
<div className="grid gap-3 md:grid-cols-[1fr_180px]">
<div className="min-w-0 rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-1)] p-3">
<div className="text-xs font-medium text-[var(--text-secondary)]">{t("composer.videoStitchInputs")}</div>
<div className="nowheel mt-3 grid max-h-28 grid-cols-2 gap-2 overflow-y-auto pr-1">
{stitchEdges.map((edge, index) => {
const sourceNode = nodes.find((candidate) => candidate.id === edge.source);
return (
<div key={edge.id} className="flex min-w-0 items-center gap-2 rounded-md border border-[var(--border-subtle)] bg-[var(--surface-2)] px-2 py-2">
<span className={`h-2 w-2 shrink-0 rounded-full ${nodeHasVideoOutput(sourceNode) ? "bg-[var(--handle-color)]" : "bg-[var(--text-disabled)]"}`} />
<div className="min-w-0">
<div className="truncate text-xs text-[var(--text-secondary)]">{t("composer.videoStitchClip", { index: index + 1 })}</div>
<div className="truncate text-[11px] text-[var(--text-muted)]">{getNodeDisplayTitle(sourceNode)}</div>
</div>
</div>
);
})}
</div>
</div>
<div className="rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-1)] p-3 text-xs text-[var(--text-muted)]">
{videoStitchData?.outputVideo ? t("composer.videoStitchOutputReady") : t("composer.videoStitchNoOutput")}
</div>
</div>
);
}
if (node?.type === "easeCurve") {
const handles = easeCurveData?.bezierHandles ?? [0.42, 0, 0.58, 1];
return (
<div className="grid gap-3 md:grid-cols-[1fr_180px]">
<div className="min-w-0 rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-1)] p-3">
<div className="text-xs font-medium text-[var(--text-secondary)]">{t("composer.easeCurveSettings")}</div>
<div className="mt-3 grid gap-3 md:grid-cols-[120px_1fr]">
<div className="rounded-md border border-[var(--border-subtle)] bg-[var(--surface-1)] p-2">
<svg viewBox="0 0 100 100" className="h-24 w-full">
<path d="M 8 92 L 92 8" stroke="rgb(64 64 64)" strokeWidth="2" strokeDasharray="5 5" fill="none" />
<path d={buildBezierPreviewPath(handles)} stroke="rgb(190 242 100)" strokeWidth="4" fill="none" strokeLinecap="round" />
</svg>
</div>
<div className="min-w-0 space-y-2 text-xs text-[var(--text-secondary)]">
<div className="rounded-md border border-[var(--border-subtle)] bg-[var(--surface-2)] px-2 py-2">{easeCurveData?.easingPreset ?? t("composer.easeCurveCustom")}</div>
<div className="rounded-md border border-[var(--border-subtle)] bg-[var(--surface-2)] px-2 py-2 font-mono text-[11px]">{formatBezierHandles(handles)}</div>
</div>
</div>
</div>
<div className="rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-1)] p-3 text-xs text-[var(--text-muted)]">
{easeCurveData?.outputVideo ? t("composer.easeCurveOutputReady") : t("composer.easeCurveNoOutput")}
</div>
</div>
);
}
return null;
}