优化: run_command 实时流式 + 审批浮窗修复 + 文档探索闭环(fetch_url+obscura引导+model_fetch兼容+prompt策略+厂商预设)

This commit is contained in:
lxy
2026-08-01 17:58:47 +08:00
parent 0e0c6862ba
commit d664bdc309
25 changed files with 1693 additions and 44 deletions
+59 -7
View File
@@ -117,6 +117,11 @@ function displayName(tc: AiToolCallInfo): string {
let _unlistenUpdate: UnlistenFn | null = null
let _positioned = false
/** 关窗兜底:防止 close() promise 卡住或被拒导致「正在关闭…」永久停留。
* close() 在 Tauri 2.x 触发 closeRequested(非强制),某些情况下原生窗口未及时销毁 →
* Vue 组件保持挂载 + 空态 hint 永久可见。此处超时强制 destroy() 兜底。 */
let _closeFallbackTimer: ReturnType<typeof setTimeout> | null = null
const CLOSE_TIMEOUT_MS = 1500
onMounted(async () => {
// 首屏定位到屏幕右上角(只定位一次,后续保留用户拖动后位置)
@@ -164,8 +169,51 @@ onMounted(async () => {
onBeforeUnmount(() => {
_unlistenUpdate?.()
_unlistenUpdate = null
if (_closeFallbackTimer) {
clearTimeout(_closeFallbackTimer)
_closeFallbackTimer = null
}
})
/** 关窗统一实现:close 先行(优雅),超时/失败兜底 destroy()(强制销毁)。
* 根因修复(BUG3+):原实现仅 await getCurrentWebviewWindow().close(),而 Tauri 2.x 的
* close() 触发 closeRequested(非强制),若原生窗口因任意原因未及时销毁(已知偶发),
* Vue 组件保持挂载 → 浮窗内空态 hint「正在关闭…」永久可见,UI 卡死。
* - 兜底1:close() 失败 → 立即 destroy() 强制销毁
* - 兜底2:close() 1.5s 未完成 → destroy() 强制销毁(超时定时器)
* - 兜底3:destroy() 也失败 → 控制台报错(不再静默吞掉,便于排查) */
async function closeWithFallback(): Promise<void> {
const win = getCurrentWebviewWindow()
// 超时兜底:close 卡住则强制 destroy
_closeFallbackTimer = setTimeout(() => {
_closeFallbackTimer = null
console.warn('[ApprovalPopup] close() 超时,降级 destroy()')
win.destroy().catch((e: unknown) => {
console.error('[ApprovalPopup] destroy() 兜底也失败:', e)
})
}, CLOSE_TIMEOUT_MS)
try {
await win.close()
// close 成功:取消超时定时器(若已触发则 destroy 已并行无害)
if (_closeFallbackTimer) {
clearTimeout(_closeFallbackTimer)
_closeFallbackTimer = null
}
} catch (e) {
// close 失败:取消超时,降级 destroy()
if (_closeFallbackTimer) {
clearTimeout(_closeFallbackTimer)
_closeFallbackTimer = null
}
console.warn('[ApprovalPopup] close() 失败,降级 destroy():', e)
try {
await win.destroy()
} catch (e2) {
console.error('[ApprovalPopup] destroy() 兜底也失败:', e2)
}
}
}
/** 批准:risk 类 approve(true),path 类 authorizeDir('once') */
async function onApprove(tc: AiToolCallInfo) {
if (processingId.value === tc.id) return
@@ -212,12 +260,15 @@ async function onReject(tc: AiToolCallInfo) {
* 误以为卡死。空 = 完成,应即时关浮窗(不等待主窗口 watch closePopup 的跨窗口往返)。 */
async function maybeAutoClose() {
if (approvals.value.length > 0) return
// 先关窗(确保用户立刻看到反馈),再 emit 清主窗口引用(顺序与 onClose 一致)
// 关窗走 closeWithFallback 兜底(close 超时/失败 → destroy 强制销毁),防「正在关闭…」永久
await closeWithFallback()
try {
const { emit } = await import('@tauri-apps/api/event')
// 先关窗(确保用户立刻看到反馈),再 emit 清主窗口引用(顺序与 onClose 一致)
await getCurrentWebviewWindow().close()
await emit(ApprovalPopupEvents.EVT_CLOSED, {})
} catch { /* ignore */ }
} catch (e) {
console.warn('[ApprovalPopup] emit EVT_CLOSED 失败:', e)
}
}
/** 点击「打开审批面板」:emit 主窗口聚焦 + togglePanel */
@@ -233,13 +284,14 @@ async function onActivate() {
* 改为先 close(即时反馈,core:window:allow-close 已授权),再 emit 清主窗口引用;
* 即便 emit 失败,主窗口 win.once('tauri://destroyed') 也会清 _popupWin(双保险)。 */
async function onClose() {
try {
await getCurrentWebviewWindow().close()
} catch { /* ignore */ }
// 关窗走 closeWithFallback 兜底(close 超时/失败 → destroy 强制销毁)
await closeWithFallback()
try {
const { emit } = await import('@tauri-apps/api/event')
await emit(ApprovalPopupEvents.EVT_CLOSED, {})
} catch { /* ignore */ }
} catch (e) {
console.warn('[ApprovalPopup] emit EVT_CLOSED 失败:', e)
}
}
// 拖动样式反馈(实际拖动由 data-tauri-drag-region + Tauri dataDrag 守卫处理)