新增: 待发箱抽屉与对话体验前端批

- 待发箱抽屉: ChatInput ⏰ 入口 + OutboxDrawer + useAiOutbox IPC 封装
- U2 进度语义三层: 内容层(最近动作) + 静默三档降速脉冲, 永不自动判死
- 流式心跳自愈: 竞态窗口旧任务迟到完成事件误收敛的自愈判定(949 前端兜底)
- 953 截断提示: 末条气泡渲染截断标记 + 继续输出按钮, 事件契约透传
This commit is contained in:
lxy
2026-10-11 07:38:00 +08:00
parent 31510d3555
commit 3818a63522
22 changed files with 1151 additions and 19 deletions
+38
View File
@@ -98,6 +98,44 @@ export function registerMainReattachListener(handler: () => Promise<void>): void
})()
}
// ── P1(2026-10-10)分离窗口「去设置」跨窗直达:分离窗无 Settings 路由,原实现仅 toast
// 提示用户「去主窗口设置」,链路断裂(分离窗长时间工作场景下改配置须手动找窗口)。
// 现走事件广播(对齐 REATTACH_EVENT 模式):分离窗 emit → 主窗口 listen → 显示/聚焦
// 主窗 + router.push('/settings')。
const SETTINGS_EVENT = 'ai-open-settings'
/** 发起方(分离窗)侧:广播「打开设置」事件,主窗口消费 */
export async function emitOpenSettings(): Promise<void> {
try {
const { emit } = await import('@tauri-apps/api/event')
await emit(SETTINGS_EVENT, {})
} catch (e) {
console.error('[AI] 跨窗打开设置事件发送失败:', e)
}
}
/** 承接方(主窗口)侧:注册「打开设置」监听。handler 由 AiChat 注入(显示主窗 + 导航) */
let _settingsListenerRegistered = false
export function registerMainOpenSettingsListener(handler: () => Promise<void>): void {
if (_settingsListenerRegistered) return
_settingsListenerRegistered = 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>>(SETTINGS_EVENT, async () => {
await handler()
})
} catch (e) {
console.error('[AI] 主窗口设置监听注册失败:', e)
_settingsListenerRegistered = false
}
})()
}
/** 承接方(分离窗)侧:注册跨窗切会话监听,切换完成后聚焦本窗口(点击发生在主窗口,
* 焦点须跟到承接会话的窗口,否则用户感知「点了没反应」)。
* handler 由 useAiConversations 注入:切会话逻辑归会话域,避免本模块反向依赖造成模块环。 */