优化: 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 -2
View File
@@ -12,6 +12,7 @@
//! 模块级私有:
//! - _pendingApprovalIds:审批按钮防抖守卫(同 useAiSend 原实现)
import { ref } from 'vue'
import { aiApi } from '@/api'
import { state } from '@/stores/ai'
import { t } from '@/i18n/i18n-helpers'
@@ -73,10 +74,13 @@ async function approveToolCall(toolCallId: string, approved: boolean, decision?:
// IPC 成功返回 = 后端已处理(执行工具/拒绝/写入白名单 + emit 事件 + try_continue)。
// 乐观更新 tc.status,不依赖后端事件来复位 loading(后端事件可能在 IPC 返回前后任意时刻到达,
// 且 findToolCall 若因时序找不到 tc 会致 loading 永不复位)。
// approve/authorize 通过 → running(后端正在执行工具);deny/reject → rejected(后端已记审计)。
// CR: 批审批场景下后端可能已完成工具执行并 emit AiToolCallCompleted,此时 tc.status 已为
// 'completed',乐观更新不能回退——故仅在 status!=='completed' 时覆写。
if (tc) {
const isDeny = isPathKind ? (decision === 'deny') : !approved
tc.status = isDeny ? 'rejected' : 'running'
if (tc.status !== 'completed') {
tc.status = isDeny ? 'rejected' : 'running'
}
if (isDeny) {
tc.result = t('ai.aiTool.rejectedHint')
}
@@ -102,6 +106,7 @@ async function approveToolCall(toolCallId: string, approved: boolean, decision?:
}
}
/**
* 批量审批:遍历 pendingApprovals 逐个按 kind 分派调用 approveToolCall(决策:批量逐个,
* 非目录聚合——path 类每条独立决策,不合并同目录)。
@@ -113,9 +118,14 @@ async function approveToolCall(toolCallId: string, approved: boolean, decision?:
* - decision='reject':risk 类 approved=false;path 类 decision='deny'。
* 单条失败不中断批量(已由 approveToolCall 内部回滚该条状态)。
*/
const _batchProcessing = ref(false)
async function batchApprove(decision: 'approve' | 'reject') {
if (_batchProcessing.value) return // 去重:批量进行中禁重复点击
_batchProcessing.value = true
// 快照当前待审批列表(遍历中 state.pendingApprovals 会因事件回调而缩短)
const items = [...state.pendingApprovals]
let successCount = 0
let failCount = 0
for (const p of items) {
try {
if (p.kind === 'path') {
@@ -125,11 +135,23 @@ async function batchApprove(decision: 'approve' | 'reject') {
// risk 类:approve/reject boolean
await approveToolCall(p.id, decision === 'approve')
}
successCount++
} catch {
// 单条失败不中断批量操作(已由 approveToolCall 内部回滚该条状态)
failCount++
// 继续处理剩余项
}
}
_batchProcessing.value = false
// 若有失败项,console.warn 诊断(用户可打开 DevTools 查看具体失败项)
if (failCount > 0) {
console.warn(`[AI] 批量审批完成: ${successCount} 成功, ${failCount} 失败`)
}
}
/** 批量处理中(供按钮禁用 + spinner 展示) */
export function isBatchProcessing(): boolean {
return _batchProcessing.value
}
export function useAiApproval() {