fix: 固定脚本助手思考用时
This commit is contained in:
@@ -391,7 +391,7 @@ type StepNode = { id: string; label: string; done?: boolean; think?: boolean };
|
||||
// 行33 · 进度时间轴(参考主流 AI 应用的流式+推理体感):
|
||||
// 竖线串起每步;当前步状态字「光扫」+ 秒数;generate 步思考态用模型实时思考最新一句做状态字,
|
||||
// 右侧 › 折叠/展开思考全文(默认收起);完成后收成「已完成思考 · 用时 Xs」。非推理模型无思考节点,照常跑步骤。
|
||||
function ProgressTimeline({ steps, reasoning, stream, done, startedAt }: { steps?: StepNode[]; reasoning?: string; stream?: string; done?: boolean; startedAt?: number }) {
|
||||
function ProgressTimeline({ steps, reasoning, stream, done, startedAt, thinkingElapsedSeconds, thinkingDurationReady }: { steps?: StepNode[]; reasoning?: string; stream?: string; done?: boolean; startedAt?: number; thinkingElapsedSeconds?: number; thinkingDurationReady?: boolean }) {
|
||||
const [openThink, setOpenThink] = useState(false);
|
||||
const [now, setNow] = useState(() => Date.now());
|
||||
const bodyRef = useRef<HTMLDivElement>(null);
|
||||
@@ -403,7 +403,9 @@ function ProgressTimeline({ steps, reasoning, stream, done, startedAt }: { steps
|
||||
useEffect(() => { if (openThink && bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [reasoning, openThink]);
|
||||
|
||||
const list = steps ?? [];
|
||||
const elapsed = startedAt ? Math.max(0, Math.round((now - startedAt) / 1000)) : 0;
|
||||
const liveElapsed = startedAt ? Math.max(0, Math.round((now - startedAt) / 1000)) : 0;
|
||||
// “已完成思考”只展示生成分镜阶段冻结下来的时长;历史记录重进页面时不能以当前时间重算。
|
||||
const elapsed = thinkingDurationReady && thinkingElapsedSeconds != null ? thinkingElapsedSeconds : liveElapsed;
|
||||
const activeId = !done ? ([...list].reverse().find((s) => !s.done)?.id ?? null) : null;
|
||||
// 思考流最新一句(按句末标点/换行切)= 动态状态字,不写死
|
||||
const latestThought = (() => {
|
||||
@@ -420,7 +422,9 @@ function ProgressTimeline({ steps, reasoning, stream, done, startedAt }: { steps
|
||||
const hasThink = s.id === "generate" && (reasoning ?? "").length > 0;
|
||||
let text = s.label;
|
||||
if (s.id === "generate") {
|
||||
if (rowDone) text = hasThink ? `已完成思考 · 用时 ${elapsed}s` : (preamble || s.label);
|
||||
if (rowDone) text = hasThink
|
||||
? (thinkingDurationReady && thinkingElapsedSeconds != null ? `已完成思考 · 用时 ${elapsed}s` : "已完成思考")
|
||||
: (preamble || s.label);
|
||||
else if (s.think) text = latestThought || "思考";
|
||||
else text = preamble || s.label;
|
||||
}
|
||||
@@ -1247,8 +1251,9 @@ export function PipelinePage(props: {
|
||||
const chatBodyRef = useRef<HTMLDivElement | null>(null);
|
||||
// 对话记录(本地会话态):生成动作可追溯,不再是「点了按钮、对话区永远空着」
|
||||
// kind=progress:进度提示流(行33),steps 逐条滚动出现,done 后折叠成一行结果
|
||||
type ChatMsg = { id: number; role: "ai" | "user"; text: string; time: string; kind?: "progress"; steps?: StepNode[]; stream?: string; reasoning?: string; done?: boolean; startedAt?: number; auto?: boolean };
|
||||
type ChatMsg = { id: number; role: "ai" | "user"; text: string; time: string; kind?: "progress"; steps?: StepNode[]; stream?: string; reasoning?: string; done?: boolean; startedAt?: number; thinkingElapsedSeconds?: number; thinkingDurationReady?: boolean; auto?: boolean };
|
||||
const nowHm = () => new Date().toTimeString().slice(0, 5);
|
||||
const elapsedSinceStart = (startedAt?: number) => startedAt ? Math.max(0, Math.round((Date.now() - startedAt) / 1000)) : 0;
|
||||
const msgIdRef = useRef(1);
|
||||
const nextMsgId = () => msgIdRef.current++;
|
||||
// 脚本助手记录按项目持久化:退出项目→重进仍能看到历史对话(行36)
|
||||
@@ -1260,7 +1265,13 @@ export function PipelinePage(props: {
|
||||
const saved = JSON.parse(raw) as ChatMsg[];
|
||||
if (Array.isArray(saved) && saved.length) {
|
||||
msgIdRef.current = Math.max(...saved.map((m) => m.id || 0)) + 1;
|
||||
return saved;
|
||||
// 兼容此前已落盘的完成记录:旧数据没有生成完成时刻,无法还原真实时长;
|
||||
// 标为历史记录后仅显示“已完成思考”,不展示被自然时间放大的伪造秒数。
|
||||
return saved.map((msg) => (
|
||||
msg.kind === "progress" && msg.done && msg.steps?.some((step) => step.id === "generate") && !msg.thinkingDurationReady
|
||||
? { ...msg, thinkingDurationReady: false }
|
||||
: msg
|
||||
));
|
||||
}
|
||||
}
|
||||
} catch { /* 解析失败则回退默认 */ }
|
||||
@@ -1434,7 +1445,12 @@ export function PipelinePage(props: {
|
||||
const idx = steps.findIndex((s) => s.id === sid);
|
||||
if (idx === -1) steps.push({ id: sid, label: label ?? sid, done: !running });
|
||||
else steps[idx] = { ...steps[idx], ...(label ? { label } : {}), ...(running ? {} : { done: true }) };
|
||||
return { ...m, steps };
|
||||
// “生成分镜”结束即表示思考结束;实体提取、自检、保存都不再计入该文案的用时。
|
||||
const thinkingElapsedSeconds = sid === "generate" && !running && m.thinkingElapsedSeconds == null
|
||||
? elapsedSinceStart(m.startedAt)
|
||||
: m.thinkingElapsedSeconds;
|
||||
const thinkingDurationReady = sid === "generate" && !running ? true : m.thinkingDurationReady;
|
||||
return { ...m, steps, ...(thinkingElapsedSeconds != null ? { thinkingElapsedSeconds } : {}), ...(thinkingDurationReady != null ? { thinkingDurationReady } : {}) };
|
||||
}));
|
||||
} else if (evt.type === "reasoning" && typeof evt.text === "string") {
|
||||
// 推理模型思考流:逐字累积,并把 generate 步置「思考态」(状态字 = 思考最新一句)
|
||||
@@ -1458,7 +1474,7 @@ export function PipelinePage(props: {
|
||||
} else if (evt.type === "saved") {
|
||||
ok = true;
|
||||
} else if (evt.type === "error") {
|
||||
setChatMsgs((list) => list.map((m) => (m.id === progressId ? { ...m, done: true } : m)));
|
||||
setChatMsgs((list) => list.map((m) => (m.id === progressId ? { ...m, done: true, thinkingElapsedSeconds: m.thinkingElapsedSeconds ?? elapsedSinceStart(m.startedAt), thinkingDurationReady: m.thinkingDurationReady ?? false } : m)));
|
||||
const publicError = isPublicGenerationError(evt.error) ? evt.error : null;
|
||||
if (publicError) {
|
||||
const presentation = presentGenerationError(publicError);
|
||||
@@ -1489,7 +1505,7 @@ export function PipelinePage(props: {
|
||||
setStreamBusy(false);
|
||||
if (targetShotId) { setBusyShot(null); setRewriteShot(null); } else setScriptBusy(false);
|
||||
}
|
||||
setChatMsgs((list) => list.map((m) => (m.id === progressId ? { ...m, done: true } : m)));
|
||||
setChatMsgs((list) => list.map((m) => (m.id === progressId ? { ...m, done: true, thinkingElapsedSeconds: m.thinkingElapsedSeconds ?? elapsedSinceStart(m.startedAt), thinkingDurationReady: m.thinkingDurationReady ?? false } : m)));
|
||||
if (ok) {
|
||||
await onRefreshProject();
|
||||
// 模型写了收尾就用它(真 agent 感);没写则兜底默认句
|
||||
@@ -2777,7 +2793,7 @@ export function PipelinePage(props: {
|
||||
<div className="bubble">
|
||||
{msg.kind === "progress"
|
||||
? <>
|
||||
<ProgressTimeline steps={msg.steps} reasoning={msg.reasoning} stream={msg.stream} done={msg.done} startedAt={msg.startedAt} />
|
||||
<ProgressTimeline steps={msg.steps} reasoning={msg.reasoning} stream={msg.stream} done={msg.done} startedAt={msg.startedAt} thinkingElapsedSeconds={msg.thinkingElapsedSeconds} thinkingDurationReady={msg.thinkingDurationReady} />
|
||||
</>
|
||||
: <CollapsibleText text={msg.text} maxLines={10} />}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user