From ac8dab3855ba65b40ef6853f0d54a56da3e04508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Mon, 15 Jun 2026 08:23:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20CR-25=20=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E7=AA=97=E5=8F=A3=E7=8A=B6=E6=80=81=E6=9C=BA(destroye?= =?UTF-8?q?d=20=E6=81=A2=E5=A4=8D=20panelOpen/=E6=A0=B8=20ai=5Fis=5Fgenera?= =?UTF-8?q?ting/=E4=B8=8D=E8=87=AA=E5=8A=A8=20switch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R3 win.once destroyed 回调补 state.panelOpen=true(X 关分离窗口后主面板自动恢复,无需手动 Ctrl+I);R4 resumeInDetached 先 await invoke('ai_is_generating') 核后端真值,generating=true 才重建生成态(置 streaming+占位气泡),false/Err 清 df-ai-gen 残留不重建(防假气泡+watchdog 130s 假超时);R5 移除 resumeInDetached 自动 switchConversation(避免覆盖 AiSession 单例 activeConversationId 致主窗口期间发消息落错会话,B-22 延伸),分离窗口可看历史列表但切换改显式触发。批9 agent,vue-tsc 0 --- src/composables/ai/useAiWindow.ts | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/composables/ai/useAiWindow.ts b/src/composables/ai/useAiWindow.ts index feacbe1..0f915c8 100644 --- a/src/composables/ai/useAiWindow.ts +++ b/src/composables/ai/useAiWindow.ts @@ -4,13 +4,12 @@ //! - _unlistenMove / _unlistenResize: 主窗口 move/resize 跟随的 unlistener //! //! 耦合: -//! - detachPanel/resumeInDetached 调 useAiConversations.switchConversation -//! - resumeInDetached 调 aiShared.nextMsgId 占位 ai 气泡 +//! - resumeInDetached 调 aiShared.nextMsgId 占位 ai 气泡 + invoke('ai_is_generating') 核对后端真值 //! - reattachPanel/detachPanel 调 useAiPanel.persistUiState +import { invoke } from '@tauri-apps/api/core' import { state } from '../../stores/ai' import { nextMsgId } from './aiShared' -import { switchConversation } from './useAiConversations' import { persistUiState } from './useAiPanel' // ── 主窗口事件跟随 unlistener ── @@ -54,6 +53,8 @@ async function detachPanel() { win.once('tauri://destroyed', () => { state.detached = false state.docked = false + // R3: X 关分离窗口后主面板自动恢复,无需手动 Ctrl+I + state.panelOpen = true stopFollowMain() }) state.detached = true @@ -77,10 +78,26 @@ async function reattachPanel() { /** 分离窗口挂载时接管主窗口当前对话(含生成中态) */ async function resumeInDetached(convId: string | null) { - const id = convId || state.activeConversationId - if (id) await switchConversation(id) + // R5: 不自动 switchConversation —— 分离窗口可看历史列表,但不主动覆盖主窗口 activeConversationId + // (避免主窗口期间发消息落错会话)。若需切换会话,由用户在分离窗口显式操作触发。 + void convId const gen = localStorage.getItem('df-ai-gen') if (gen) { + // R4: 先核对后端 ai_is_generating 真值,generating=true 才重建生成态, + // 避免 df-ai-gen 残留导致假气泡 + 130s 后 watchdog 假超时 + let backendGenerating = false + try { + backendGenerating = await invoke('ai_is_generating') + } catch (e) { + console.error('[AI] resumeInDetached: 核对 ai_is_generating 失败,跳过重建生成态', e) + return + } + if (!backendGenerating) { + // 后端已不再生成,清残留快照,不重建 + localStorage.removeItem('df-ai-gen') + localStorage.removeItem('df-ai-text') + return + } // 接管正在生成的对话:恢复已生成文本并补占位,后续 delta 继续追加 state.currentText = localStorage.getItem('df-ai-text') || '' state.messages.push({