From 48a9fb0f6d7445f7004f1bf84a9608b4e7b52116 Mon Sep 17 00:00:00 2001 From: ssurui Date: Thu, 3 Sep 2026 15:34:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20TTS=20append()=20=E9=AB=98=E9=A2=91?= =?UTF-8?q?=E5=B0=8F=E5=A2=9E=E9=87=8F=E4=B8=8B=E4=B8=A2=E5=AD=97=E3=80=81?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=A1=86=20max-height=20=E5=85=9C=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 真机复测发现两个问题: 1. 语音输出改成流式后实测只播一个孤零零的句号,正文完全没读出来。 根因:TtsPlayer.append() 每次调用都用 this.buffer = pending 直接 覆盖缓冲区,而不是累加。LLM 流式返回的增量通常很小(经常几个字一 个 chunk),大部分时候 processBuffer() 没能凑出完整句子就被下一次 append() 覆盖掉,只有 flush() 时残留的最后一小段才被朗读。改成 this.buffer += pending,累加未处理完的内容,不再丢字。真机验证: 同一次问答里生成过程中(回复结束前)已经出现 4 条完整正文的 speak 调用,不再是只有收尾的空句号。 2. 输入框 rows="1" + min-height:0 没能完全消除跟按钮行的高度差 (真机截图像素测量仍有约10px gap),补充 max-height:96px 硬性封顶, 重启(不是 HMR 热更)后截图确认对齐。 Co-Authored-By: Claude Sonnet 5 --- src/renderer/src/components/ChatPanel.vue | 2 +- src/renderer/src/utils/tts.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/components/ChatPanel.vue b/src/renderer/src/components/ChatPanel.vue index 4eef92d..832a0c7 100644 --- a/src/renderer/src/components/ChatPanel.vue +++ b/src/renderer/src/components/ChatPanel.vue @@ -869,7 +869,7 @@ onUnmounted(() => { .q-input { width: 100%; box-sizing: border-box; - height: 96px; min-height: 0; + height: 96px; min-height: 0; max-height: 96px; border: 2px solid var(--line-2); border-radius: var(--radius); padding: 16px 20px; font-family: inherit; font-size: 20px; line-height: 1.5; resize: none; diff --git a/src/renderer/src/utils/tts.ts b/src/renderer/src/utils/tts.ts index b9c21f0..5f29ce7 100644 --- a/src/renderer/src/utils/tts.ts +++ b/src/renderer/src/utils/tts.ts @@ -152,9 +152,12 @@ export class TtsPlayer { const pending = this.computePendingDelta(plain) if (!pending) return - // 直接用当前未朗读的纯文本替换缓冲区,避免旧 buffer 因格式被二次清洗而变得陈旧/重复 + // pending 是相对 lastSpokenPlain 的增量(已排除重复),累加进缓冲区而不是替换—— + // 替换会丢失上一次 append() 里 processBuffer() 还没来得及提取成句子的剩余文本 + // (比如队列满了暂停提取,或者还没遇到句尾标点),高频小增量调用下会导致正文 + // 大量丢字,只有 flush() 时残留的最后一小段才会被朗读。 this.lastSpokenPlain = plain - this.buffer = pending + this.buffer += pending this.recordAppend(pending.length) this.processBuffer() }