优化: 聊天体验批与Web规范合规
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { state } from '@/stores/ai'
|
||||
import { useAppSettingsStore } from '@/stores/appSettings'
|
||||
import { nextMsgId, getConvState, convStates } from './aiShared'
|
||||
import type { MessageId } from '@/api/types'
|
||||
import { setStreaming } from './streamingGuard'
|
||||
@@ -58,6 +59,45 @@ export async function emitDetachedOpenConversation(convId: string): Promise<void
|
||||
}
|
||||
}
|
||||
|
||||
// ── V11b(2026-10-05)跨窗收回分离窗:reattachPanel 的 UI 入口 ──
|
||||
// 病灶:reattachPanel(关闭全部分离窗+回主面板)全仓零 UI 调用——分离窗内「关闭」按钮走
|
||||
// closeDetachedWindow(仅关窗不恢复主面板),用户想收回只能手动重开主面板。主/分离窗是
|
||||
// 独立 JS context,分离窗按钮无法直调主窗 store;走 Tauri 全局事件广播(对齐 OPEN_CONV_EVENT
|
||||
// 模式):分离窗 emit → 主窗(label 判定)监听 → 执行 reattachPanel。
|
||||
const REATTACH_EVENT = 'ai-reattach-panel'
|
||||
|
||||
/** 发起方(分离窗)侧:广播收回事件,主窗口消费 */
|
||||
export async function emitReattachToMain(): Promise<void> {
|
||||
try {
|
||||
const { emit } = await import('@tauri-apps/api/event')
|
||||
await emit(REATTACH_EVENT, {})
|
||||
} catch (e) {
|
||||
console.error('[AI] 跨窗收回事件发送失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
/** 承接方(主窗口)侧:注册收回监听。handler=reattachPanel(由 AiChat 挂载时注入,归窗口域) */
|
||||
let _reattachListenerRegistered = false
|
||||
export function registerMainReattachListener(handler: () => Promise<void>): void {
|
||||
if (_reattachListenerRegistered) return
|
||||
_reattachListenerRegistered = true
|
||||
void (async () => {
|
||||
try {
|
||||
const [{ listen }, { getCurrentWebviewWindow }] = await Promise.all([
|
||||
import('@tauri-apps/api/event'),
|
||||
import('@tauri-apps/api/webviewWindow'),
|
||||
])
|
||||
if (getCurrentWebviewWindow().label !== 'main') return
|
||||
await listen<Record<string, never>>(REATTACH_EVENT, async () => {
|
||||
await handler()
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('[AI] 主窗口收回监听注册失败:', e)
|
||||
_reattachListenerRegistered = false
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
/** 承接方(分离窗)侧:注册跨窗切会话监听,切换完成后聚焦本窗口(点击发生在主窗口,
|
||||
* 焦点须跟到承接会话的窗口,否则用户感知「点了没反应」)。
|
||||
* handler 由 useAiConversations 注入:切会话逻辑归会话域,避免本模块反向依赖造成模块环。 */
|
||||
@@ -114,6 +154,12 @@ async function detachPanel(convId?: string) {
|
||||
localStorage.setItem(textKey(targetConv), state.currentText)
|
||||
}
|
||||
}
|
||||
// V11c: 种子分离窗活跃会话 KV——分离窗 AiChat 挂载同样跑 loadConversations(),
|
||||
// 读按窗口域分流的 'df-ai-active-conv-detached'(独立 JS context 不与主窗共 pinia state),
|
||||
// 不种子则分离窗启动回落列表首条而非主窗正看的会话。目标 conv 为空(无活跃会话)跳过。
|
||||
if (targetConv) {
|
||||
void useAppSettingsStore().set('df-ai-active-conv-detached', targetConv)
|
||||
}
|
||||
// 主窗口 state 与分离窗口 state 独立(各自 webview 独立 JS context,
|
||||
// stores/ai.ts 模块级单例仅在同 webview 内共享),清主窗口 state 不影响分离窗口生成态。
|
||||
// 分离窗口接管生成后,主窗口不应再持该会话生成态残留(防重开面板时 UI 显生成中幽灵/进度条)。
|
||||
@@ -133,7 +179,10 @@ async function detachPanel(convId?: string) {
|
||||
minHeight: 400,
|
||||
center: true,
|
||||
decorations: true,
|
||||
alwaysOnTop: true, // 分离窗口默认置顶(AI 助手常态:用户在主窗口工作时希望 AI 辅助窗口可见)
|
||||
// V11a(2026-10-05):默认不置顶——原 true 会让分离窗盖住其他应用(VSCode/浏览器)妨碍工作;
|
||||
// 用户开过置顶的偏好走 AiChat.vue 的 df-ai-always-on-top KV 恢复链(分离窗挂载时
|
||||
// setAlwaysOnTop(saved)),默认改 false 不影响已开置顶用户,只改变首次/未开者的初始态。
|
||||
alwaysOnTop: false,
|
||||
})
|
||||
win.once('tauri://created', () => {
|
||||
state.panelOpen = false
|
||||
@@ -183,6 +232,8 @@ async function reattachPanel() {
|
||||
state.docked = false
|
||||
state.panelOpen = true
|
||||
persistUiState()
|
||||
// V11c: 清分离窗活跃会话 KV(分离窗已全关,残留无意义;下次 detachPanel 会重新种子)
|
||||
void useAppSettingsStore().remove('df-ai-active-conv-detached')
|
||||
// 时序加固:reattach 关闭分离窗口后,显式停分离窗口残留 listener(分离窗口 close 异步,
|
||||
// onBeforeUnmount stopListener 可能延迟),防主面板重新挂载 startListener 时与旧 listener 重叠竞态。
|
||||
stopListener()
|
||||
|
||||
Reference in New Issue
Block a user