优化: AI Chat 体验全面升级(图标/审批/目标提取/窗口管理)

- AI 入口图标:灯泡→星花 sparkles+发光,与灵感入口区分
- 目标提取根本性改造:用户消息规则→工具调用推理(infer_goal_from_tool_calls)
  - Vec<String>→Vec<GoalEntry>(text+status),active→completed 状态流转
  - 多条/轮提取,system_prompt 仅注入 active 目标
  - 代码拆分到 helpers.rs(解决 brace 嵌套致 pub(crate) 不可见)
- 审批修复:patch_file 全档位自动放行,批量审批防抖,乐观更新竞态保护
- 审批通知浮层:全局右上角 ApprovalOverlay,窗口遮挡时可见
- 设置导入导出移除:价值低占用空间,跨设备同步建议复制 SQLite
- 分离窗口默认置顶+置顶状态持久化(刷新后保持)
- header 菜单聚合:清空/压缩上下文收入 ... popout
- LLM 文字重叠防御:delta 重复检测+丢弃
- 置顶图标:星→大头针 pushpin
- 文档/注释更新:过期 Vec<String>/chat.rs 提取描述修正
This commit is contained in:
lxy
2026-07-03 13:07:03 +08:00
parent aa0701a2ea
commit 0dfaeb0f5b
26 changed files with 929 additions and 753 deletions
+24 -1
View File
@@ -42,6 +42,9 @@ let _unlistenApprovalClear: (() => void) | null = null
// (两回调写同一 state.currentText → 流式文字双倍)
let _startPromise: Promise<void> | null = null
// 上一次 AiTextDelta 的内容(单窗口内 LLM 重复 delta 防御用,见 handleStreamingEvent)
let _lastDelta = ''
const appSettings = useAppSettingsStore()
// B-260616-17: 看门狗不重置的事件集合(审批等待/完成/错误由各自 case 内 clear)。
@@ -205,6 +208,7 @@ export function flushCurrentText() {
// 自清把 flush 语义收敛为"回填并归零",消除对调用方清空顺序的依赖。各调用方后续的
// currentText='' 对已清空值幂等,无副作用。
state.currentText = ''
_lastDelta = '' // 同步重置 delta 跟踪,避免新轮首个 delta 误命中上一轮末 delta 重复检测
}
/** token 用量展示开关(读 appSettings,与 Settings.vue 共享 key `df-show-token-usage`) */
@@ -274,9 +278,28 @@ function removePendingDirAuth(id: string): void {
/** streaming 域:流式文本累积/新轮/心跳/重试/max 轮暂停 */
function handleStreamingEvent(event: AiChatEvent): boolean {
switch (event.type) {
case 'AiTextDelta':
case 'AiTextDelta': {
// BUG-260624-01 / UX-260617-12 消息重叠防御:
// 历史多次报「LLM 回复文字重复」(如「好好,,现在现在我对我对」单字双重叠加)。
// 已排除的根因:后端 stream_recv.rs emit 增量 chunk.delta(非累积)、
// startListener 幂等+_startPromise 并发去重、streamingBlocks 块级 memo。
// 最可能残留路径:① 主窗口 AI 面板与 ai-detached 分离窗口同时打开,两 webview 独立 JS 上下文
// 各自 listen 全局广播事件 → 各自 currentText += delta(用户感知「双倍」)。
// 桌面双窗口场景下难以在单窗口内根治,需后端定向 emit_to(label) 重构。
// ② LLM provider(GLM anthropic_compat 等)在特定情况下返重复 delta。
// 防御:连续两次 delta 完全相同(且非空)时丢弃第二次 + console.warn,留下诊断证据。
// 副作用极低:正常 LLM 流不会连发完全相同 delta(空格/单字符除外,已 len>1 守卫)。
if (event.delta && _lastDelta === event.delta && event.delta.length > 1) {
// df-ai-trace-delta 开关控制(默认开,生产可关)
if (appSettings.get('df-ai-trace-delta', true)) {
console.warn('[AiTextDelta] 重复 delta 已丢弃(疑似双窗口 listener 或 provider 异常):', JSON.stringify(event.delta))
}
return true
}
_lastDelta = event.delta
state.currentText += event.delta
return true
}
case 'AiAgentRound': {
// Agent 循环新一轮:保存当前文本到上一条 assistant 消息,新建空 assistant 消息