重构: aichat 双轨状态机收口 + AiCompressed 事件拆分
- generating bool + CONV_STATE_ENABLED 开关双轨退役,ConvState enum 单一真相源 - can_accept_request 接入 chat 域入口(覆盖 Stopping 竞态,严谨于 is_active) - AiCompressed 拆 AiManualCompressed/AiAutoCompressed(治自动压缩误触桌面toast+刷新) - convStates/getConvState 下沉 aiShared.ts 破循环依赖
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { state } from '@/stores/ai'
|
||||
import { nextMsgId } from './aiShared'
|
||||
import { nextMsgId, getConvState, convStates } from './aiShared'
|
||||
import { setStreaming } from './streamingGuard'
|
||||
import { persistUiState } from './useAiPanel'
|
||||
|
||||
@@ -52,18 +52,24 @@ async function detachPanel(convId?: string) {
|
||||
}
|
||||
// 快照当前生成态,供分离窗口接管(保持正在进行的对话)
|
||||
// F-09:用 per-conv key 写入(多会话各自快照互不覆盖)
|
||||
if (state.streaming && targetConv && state.generatingConvs.has(targetConv)) {
|
||||
localStorage.setItem(genKey(targetConv), targetConv)
|
||||
localStorage.setItem(textKey(targetConv), state.currentText)
|
||||
// 批4 双轨收口:读 getConvState(enum 真相源)派生,替代旧 generatingConvs.has。
|
||||
// 桥接语义:has(convId)=true 等价于 conv_state∈{generating,stopping,compressed}(非终止三态)。
|
||||
if (state.streaming && targetConv) {
|
||||
const cs = getConvState(targetConv)
|
||||
const gen = cs === 'generating' || cs === 'stopping' || cs === 'compressed'
|
||||
if (gen) {
|
||||
localStorage.setItem(genKey(targetConv), targetConv)
|
||||
localStorage.setItem(textKey(targetConv), state.currentText)
|
||||
}
|
||||
}
|
||||
// 主窗口 state 与分离窗口 state 独立(各自 webview 独立 JS context,
|
||||
// stores/ai.ts 模块级单例仅在同 webview 内共享),清主窗口 state 不影响分离窗口生成态。
|
||||
// 分离窗口接管生成后,主窗口不应再持该会话生成态残留(防重开面板时 UI 显生成中幽灵/进度条)。
|
||||
// F-09:仅从 Set 移除该 conv(其他会话可能仍在生成),不全局清空。
|
||||
// F-09:仅从 convStates 移除该 conv(其他会话可能仍在生成),不全局清空。
|
||||
// watchdog 在主窗口 AiChat 卸载(App.vue v-if detached)→ stopListener → clearStreamWatchdog 时已清,
|
||||
// 此处仅清内存 state 视觉残留;localStorage 快照保留供 resumeInDetached 恢复。
|
||||
setStreaming(false, { convId: targetConv || null, reason: 'detachPanel-clear-main' })
|
||||
if (targetConv) state.generatingConvs.delete(targetConv)
|
||||
if (targetConv) convStates.delete(targetConv)
|
||||
const sep = targetConv ? `?conv=${encodeURIComponent(targetConv)}` : ''
|
||||
const url = window.location.origin + window.location.pathname + '#/ai-detached' + sep
|
||||
const win = new WebviewWindow(label, {
|
||||
@@ -135,7 +141,7 @@ function cleanupAllDetachedSnapshots(): void {
|
||||
* 1. state.currentText = df-ai-text-${convId}(已生成文本)
|
||||
* 2. push 占位 assistant 气泡(content='')
|
||||
* 3. state.streaming = true
|
||||
* 4. state.generatingConvs.add(df-ai-gen)
|
||||
* 4. convStates.set(df-ai-gen, 'generating')(批4 双轨收口:替代旧 generatingConvs.add)
|
||||
*
|
||||
* 幂等:若 state.streaming 已是 true(已恢复过/正在生成),直接返回不重复 push。
|
||||
* 这对主面板刷新场景至关重要:detachPanel 时主窗口 state 已被清 streaming=false
|
||||
@@ -194,7 +200,9 @@ export async function restoreGeneratingState(opts?: { fromMainPanel?: boolean; c
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
setStreaming(true, { convId: gen, reason: 'restoreGeneratingState' })
|
||||
state.generatingConvs.add(gen)
|
||||
// 批4 双轨收口:恢复生成中态写 convStates(enum 真相源),替代旧 generatingConvs.add。
|
||||
// setStreaming 联动已退役(批4),streaming 单值与 convStates 项并行写无冲突。
|
||||
convStates.set(gen, 'generating')
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user