优化视频创作

This commit is contained in:
Azmat@qq.com
2026-09-15 15:17:05 +08:00
parent 99a126d19b
commit 7098245a4a
21 changed files with 262 additions and 220 deletions
+51 -55
View File
@@ -399,17 +399,6 @@ function collectSessionAssets(
});
}
if (message.kind === "prompt_file") {
const title = String(message.payload?.title || "视频生成Prompt.md");
const body = String(message.payload?.body || "");
push({
key: `prompt:${message.id || title}`,
kind: "document",
title,
promptTitle: title,
promptBody: body || "暂无内容",
});
}
}
return out;
@@ -512,7 +501,7 @@ function PlanCard({ payload }: { payload: Record<string, unknown> }) {
<section className="omni-video-plan-card">
<header className="omni-video-plan-head">
<strong>视频最终方案</strong>
<span>请确认后再出 Prompt</span>
<span>确认后进入出片参数核对</span>
</header>
<section className="omni-plan-section">
<strong>卖点做减法</strong>
@@ -555,9 +544,9 @@ function PlanCard({ payload }: { payload: Record<string, unknown> }) {
</span>
) : null}
<span className="omni-plan-final-input">
最终输入:
出片准备:
<b>
Prompt.md + <em className="omni-plan-point-body">{String(payload.ref_count ?? 0)}</em> 组参考素材
已整合生成参数与 <em className="omni-plan-point-body">{String(payload.ref_count ?? 0)}</em> 组参考素材
</b>
</span>
</div>
@@ -565,30 +554,21 @@ function PlanCard({ payload }: { payload: Record<string, unknown> }) {
);
}
function PromptFileCard({
payload,
onView,
}: {
payload: Record<string, unknown>;
onView: () => void;
}) {
function PromptFileCard({ payload }: { payload: Record<string, unknown> }) {
return (
<section className="omni-prompt-file-card">
<span className="omni-prompt-file-icon">
<FileText />
</span>
<div className="omni-prompt-file-copy">
<strong>{String(payload.title || "视频生成Prompt.md")}</strong>
<small>已整合策略、镜头、口播、节奏与 {String(payload.ref_count ?? 0)} 组参考素材</small>
<strong>出片指令已准备好</strong>
<small>已根据方案、参数和 {String(payload.ref_count ?? 0)} 组参考素材在后台整理完成</small>
</div>
<button type="button" onClick={onView}>
查看 Prompt
</button>
</section>
);
}
/** 视频 5 步闸门:策略/方案/Prompt 下方的「按这个继续 / 我想改」。
/** 视频阶段闸门:策略/方案下方的「按这个继续 / 我想改」。
* 「我想改」在卡片内展开反馈输入,不再依赖底部 composer。 */
function StepConfirmCard({
message,
@@ -606,14 +586,14 @@ function StepConfirmCard({
const answers = (message.payload.answers as Record<string, string> | undefined) || {};
const action = String(answers.step_action || "");
const stepLabel =
step === "strategy" ? "创作策略" : step === "plan" ? "视频方案" : step === "prompt" ? "出片 Prompt" : "这一步";
step === "strategy" ? "创作策略" : step === "plan" ? "视频方案" : step === "prompt" ? "出片指令" : "这一步";
const placeholder =
step === "strategy"
? "说说你想怎么改策略…"
: step === "plan"
? "说说你想怎么改方案…"
: step === "prompt"
? "说说你想怎么改 Prompt…"
? "说说你想怎么改出片细节…"
: "说说你想怎么改…";
const [revising, setRevising] = useState(false);
const [feedback, setFeedback] = useState("");
@@ -781,21 +761,6 @@ function ElicitCard({
);
}
if (interaction === "chat") {
return (
<div className="omni-chat-row agent">
<span className="omni-chat-avatar">
<Sparkles />
</span>
<div className="omni-chat-bubble">
<p className="omni-chat-text">
{message.text || fields[0]?.label || "这项你想怎么定?"}
</p>
</div>
</div>
);
}
const toggleMulti = (key: string, value: string) => {
setAnswers((prev) => {
const current = Array.isArray(prev[key]) ? (prev[key] as string[]) : [];
@@ -828,12 +793,12 @@ function ElicitCard({
<div className="omni-chat-bubble omni-gate-bubble">
<p className="omni-chat-text">{message.text || gateLabel}</p>
{!submitted ? (
<div className="omni-gate-actions">
{(gateField?.options || []).map((option) => (
<div className="omni-gate-actions" aria-label="商品选择操作">
{(gateField?.options || []).map((option, index) => (
<button
type="button"
key={option.value}
className={option.value === "send" ? "primary" : ""}
className={index === 0 ? "primary" : ""}
disabled={disabled}
onClick={() => {
if (!gateField?.key) return;
@@ -854,6 +819,32 @@ function ElicitCard({
);
}
if (interaction === "chat") {
const question = message.text || fields[0]?.label || "这项你想怎么定?";
return (
<div className="omni-chat-row agent">
<span className="omni-chat-avatar">
<Sparkles />
</span>
<div className="omni-chat-stack">
<div className="omni-chat-bubble">
<p className="omni-chat-text">{question}</p>
</div>
{!submitted ? (
<div className="omni-reply-guide" aria-label="下一步引导">
<p><span>下一步</span>直接在下方输入你的想法,回答这一项就行。</p>
<div className="omni-reply-guide-options">
<button type="button" className="primary" onClick={() => document.getElementById("omniSessionPrompt")?.focus()}>
现在填写
</button>
</div>
</div>
) : null}
</div>
</div>
);
}
return (
<div className="omni-elicit-slot">
<section className={`omni-elicit-card${submitted ? " is-submitted" : ""}`}>
@@ -1051,11 +1042,15 @@ function ConfirmCard({
);
}
function ResultCard({ payload }: { payload: Record<string, unknown> }) {
function ResultCard({
payload,
}: {
payload: Record<string, unknown>;
}) {
const assets = (payload.assets as Array<Record<string, string>> | undefined) || [];
const first = assets[0] || {};
const meta = [payload.model, payload.resolution, payload.ratio].filter(Boolean).join(" · ");
const isVideo = first.type === "video";
const isGeneratedVideo = first.type === "video";
const [preview, setPreview] = useState<{ src: string; kind: "image" | "video"; name: string } | null>(null);
return (
@@ -1100,7 +1095,7 @@ function ResultCard({ payload }: { payload: Record<string, unknown> }) {
</div>
<div className="omni-result-info">
<div>
<strong>{isVideo ? "视频已生成" : assets.length > 1 ? `已生成 ${assets.length} 张图` : "图片已生成"}</strong>
<strong>{isGeneratedVideo ? "视频已生成" : assets.length > 1 ? `已生成 ${assets.length} 张图` : "图片已生成"}</strong>
<small>{meta}</small>
</div>
</div>
@@ -2178,10 +2173,6 @@ export function OmniSessionPage({
<PromptFileCard
key={message.clientKey || message.id}
payload={message.payload}
onView={() => setPromptView({
title: String(message.payload.title || "视频生成Prompt.md"),
body: String(message.payload.body || "暂无内容"),
})}
/>
);
case "confirm": {
@@ -2213,7 +2204,12 @@ export function OmniSessionPage({
case "generating":
return <ProcessCard key={message.clientKey || message.id} payload={message.payload} />;
case "result":
return <ResultCard key={message.clientKey || message.id} payload={message.payload} />;
return (
<ResultCard
key={message.clientKey || message.id}
payload={message.payload}
/>
);
default: {
const refs = message.refs || [];
const body = stripMentionText(message.text, refs);