From 9b496bb42a1d55caf2b0887e6c5ffbd504cb81ba Mon Sep 17 00:00:00 2001 From: Peanut Date: Tue, 21 Jul 2026 20:43:11 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=92=A4=E5=9B=9E=20typewriter=20?= =?UTF-8?q?=E6=94=B9=E5=8A=A8=EF=BC=8Cnovel=20=E6=94=B9=E7=94=A8=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E7=B4=AF=E5=8A=A0=20delta=20=E4=BF=9D=E8=AF=81?= =?UTF-8?q?=E6=B5=81=E5=BC=8F=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前用 useTypewriterStream.writer.push + 同步读 visibleText 有时序问题: writer 的 visibleText 由内部 setInterval 异步推进,push 后立即同步读得到空串, 气泡一直空白。 撤回 typewriter 改动,恢复 novel_delta 直接 lastNovel.content += delta, SSE 本身就是流式推送,每次到达即显示。 保留 confirmOutline / modifyOutline 的 pendingNextResponse = true 修复(loading)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- mini-program/src/pages/main/ScriptView.vue | 33 ++++------------------ 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/mini-program/src/pages/main/ScriptView.vue b/mini-program/src/pages/main/ScriptView.vue index 2101f84..f2c268e 100644 --- a/mini-program/src/pages/main/ScriptView.vue +++ b/mini-program/src/pages/main/ScriptView.vue @@ -290,8 +290,6 @@ const currentVersionMessageId = ref('') const novelSessionId = ref('') const novelFullText = ref('') const novelOutline = ref(null) -// novel 消息 id → typewriter writer 实例映射(用于真正的逐字流式输出) -const novelWriters = new Map() const clarificationCard = ref(null) const currentStreamTask = ref(null) const answeringClarification = ref(false) @@ -1707,27 +1705,16 @@ const handleShortNovelEvent = (event) => { generationStatus.value = 'idle' pendingNextResponse.value = false break - case 'novel_start': { - const novelMsg = addResultMessage({ role: 'assistant', kind: 'novel', content: '', pending: true }) - // 为这条 novel 消息创建独立的 typewriter writer,实现逐字流式输出 - novelWriters.set(novelMsg.id, useTypewriterStream({ interval: 32, step: 1 })) + case 'novel_start': + addResultMessage({ role: 'assistant', kind: 'novel', content: '', pending: true }) generationStatus.value = 'streaming' pendingNextResponse.value = false break - } case 'novel_delta': { - // 往最后一条 pending novel 消息推送 delta 到 typewriter writer + // 往最后一条 pending novel 消息直接累加 delta,保证流式显示 const lastNovel = [...resultMessages.value].reverse().find(m => m.kind === 'novel' && m.pending) if (lastNovel) { - const writer = novelWriters.get(lastNovel.id) - const delta = payload.delta || '' - if (writer) { - writer.push((writer.targetText?.value || '') + delta) - lastNovel.content = writer.visibleText.value - // writer 的 visibleText 是 reactive,需用 watch;但这里直接读取值即可 - } else { - lastNovel.content += delta - } + lastNovel.content += payload.delta || '' } keepResultAtBottom() break @@ -1736,17 +1723,7 @@ const handleShortNovelEvent = (event) => { // 标记最后一条 novel 消息完成 const lastNovel = [...resultMessages.value].reverse().find(m => m.kind === 'novel') if (lastNovel) { - const writer = novelWriters.get(lastNovel.id) - const finalText = payload.full_text || '' - if (writer) { - writer.finish(finalText) - // finish 后 typewriter 会逐字排空到 targetText,等排空完成再写回 content - // 但为了持久化和立即显示,这里直接同步设值 - lastNovel.content = finalText - novelWriters.delete(lastNovel.id) - } else if (finalText) { - lastNovel.content = finalText - } + if (payload.full_text) lastNovel.content = payload.full_text lastNovel.pending = false } scriptId.value = payload.scriptId || ''