优化视频创作
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user