新增: aichat前端体验四件与队列残留收口

- R4 代码块语言角标:MessageList decorateCodeHtml 捕获 language-xxx 输出 data-lang+CSS ::before 左上角标,hover 显,无语言裸块不渲染
- R6 工具结果路径可复制:ToolResultBody 五处 file-bar 的 path span→button(read_file/list_directory/search_files/grep/patch_file),点击复制完整路径+✓/✕ 1.2s 反馈+execCommand 降级链
- I6 重新生成 UI 入口:MessageItem user 气泡 hover 补重新生成按钮(canRegenerate=isLastUser&&非流式),复用 handleRetry→store.regenerate 链路
- V4 浮窗审批 High 风险二次确认:useToolApproval 导出 isHighRiskTool/highRiskConfirmText,ApprovalPopup.onApprove 前置 ConfirmDialog,数字键守卫不绕确认
- 9-28#5 两处全局清队列残留:useAiEvents/useAiLifecycleEvents 无 convId 的 AiError 分支改保留队列+console.warn(防误伤其他会话排队消息)
- i18n zh/en 补 clickToCopy 键;裸 @ 改 @ 转义(vue-i18n linked-message 前缀吞噬)
This commit is contained in:
lxy
2026-10-05 00:35:03 +08:00
parent 83e86cd6b7
commit c10f0e46f0
11 changed files with 161 additions and 35 deletions
+19 -7
View File
@@ -151,7 +151,7 @@ import { useI18n } from 'vue-i18n'
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { toolDisplayName } from '@/composables/ai/useToolCardHeader'
import { dispatchApprovalIPC } from '@/composables/ai/useToolApproval'
import { dispatchApprovalIPC, isHighRiskTool, highRiskConfirmText } from '@/composables/ai/useToolApproval'
import { ApprovalPopupEvents } from '@/composables/ai/useApprovalPopup'
import { formatWaitTime, waitLevelOf } from '@/utils/time'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
@@ -170,20 +170,24 @@ const { toast, showToast } = useToast()
/** 拖动态(样式反馈) */
const isDragging = ref(false)
// ── M5:path 类 deny 二次确认(独立窗口本地 confirm;拒绝不可逆——工具返 Err、loop 续跑不再重试)──
// 浮窗是独立 WebviewWindow,与主窗口 JS 上下文隔离,不复用 useConfirm 跨窗口 reactive。
// pendingDenyTc 持有「待确认拒绝」的 tc;answerConfirm 据 result 决定续跑 runApproval 或放弃。
const confirmState = reactive<{ visible: boolean; msg: string; tc: AiToolCallInfo | null }>({
// ── M5/V4:浮窗本地二次确认(独立窗口本地 confirm;与主窗口 JS 上下文隔离,不复用 useConfirm)──
// M5:path 类 deny(拒绝不可逆——工具返 Err、loop 续跑不再重试)。
// V4:High 风险工具 risk 批准前置确认(对齐 ToolCard 安全语义:注意力分散场景数字键「1」
// 单键放行 run_command/delete_file 太危险)。action 记录「待确认动作」供 answerConfirm 分派。
const confirmState = reactive<{ visible: boolean; msg: string; tc: AiToolCallInfo | null; action: 'deny' | 'approve' }>({
visible: false,
msg: '',
tc: null,
action: 'deny',
})
function answerConfirm(ok: boolean) {
const tc = confirmState.tc
const action = confirmState.action
confirmState.visible = false
confirmState.tc = null
if (ok && tc) {
void runApproval(tc, false, 'deny')
if (action === 'approve') void runApproval(tc, true)
else void runApproval(tc, false, 'deny')
}
}
@@ -406,8 +410,16 @@ async function onApproveSession(tc: AiToolCallInfo) {
async function onApproveAlways(tc: AiToolCallInfo) {
await runApproval(tc, false, 'always')
}
/** risk 类:批准(approved=true) */
/** risk 类:批准(approved=true)。V4:High 风险工具先二次确认(按钮与数字键 1 同走本入口,
* 确认框打开时数字键守卫生效,不绕确认;拒绝仍按 M5 原逻辑)。 */
async function onApprove(tc: AiToolCallInfo) {
if (isHighRiskTool(tc.name)) {
confirmState.msg = highRiskConfirmText(tc.name)
confirmState.tc = tc
confirmState.action = 'approve'
confirmState.visible = true
return
}
await runApproval(tc, true)
}
/** path 类 deny / risk 类 reject(统一入口,kind 由 dispatchApprovalIPC 据 tc.kind 分派)。