Agent 引用来源外部链接在应用内弹出空标签页¶
状态:已修复(外部链接不再渲染为 <a target="_blank">,改为按钮并通过 opener 插件打开系统浏览器)
影响面:Agent 面板 / 独立 Agent 窗口底部的“引用来源”列表中的外部 HTTP(S) 链接
相关代码:
src/components/ai-elements/sources.tsx—Source组件:无href的 action-only 来源渲染为<button>src/components/agent/chat-transcript.tsx— 外部 source 不再传href,只保留onClicksrc/components/shell/right-sidebar.tsx/src/components/shell/feature-window-root.tsx—handleAgentOpenSource通过@tauri-apps/plugin-opener打开外部 URL
1. 问题现象¶
在 Agent 回复底部展开“引用来源”后:
- 如果来源里包含外部网络链接(如
https://arxiv.org/abs/...); - 点击该链接;
- 外部浏览器没有打开,应用内部反而弹出一个空白标签页 / Webview 窗口。
GitHub Issue: #358
2. 根因¶
2.1 Source 把外部链接渲染成 <a target="_blank">¶
src/components/agent/chat-transcript.tsx 原来对外部 source 同时传了 href 和 onClick:
<Source
title={s}
href={isHttp ? s : undefined}
onClick={onOpenSource ? () => onOpenSource(s) : undefined}
/>
Source 组件检测到 href 是 http(s):// 时,会设置 target="_blank":
const isExternal = Boolean(href && /^https?:\/\//i.test(href));
<a
href={href}
target={isExternal ? "_blank" : undefined}
onClick={(event) => {
if (onClick) {
event.preventDefault();
onClick(event);
}
}}
>
2.2 Tauri WebView 对 _blank 的默认行为¶
Tauri 2 的 WebView 默认会把 target="_blank" 链接当成“新建 Webview 窗口”请求处理。虽然 JS 中调用了 event.preventDefault(),但在 Tauri 层面 _blank 的窗口创建行为并不能被前端稳定地完全阻止(参见 Tauri 维护者说明)。
2.3 window.open fallback 也在应用内打开¶
handleAgentOpenSource 对 HTTP(S) 链接调用 @tauri-apps/plugin-opener 的 openUrl();一旦 openUrl() 失败,catch 分支里的 window.open(url, "_blank") 在 Tauri WebView 中同样会在应用内部打开窗口,而不是系统浏览器。
3. 解决方案¶
3.1 外部来源不再让 <a> 带 href¶
在 chat-transcript.tsx 中,外部 source 只保留 onClick,不传 href:
<Source
title={s}
href={isHttp ? undefined : s}
onClick={onOpenSource ? () => onOpenSource(s) : undefined}
/>
3.2 Source 组件对 action-only 来源渲染为 <button>¶
当 onClick 存在但 href 不存在时,Source 渲染为 <button type="button">,彻底避免 Tauri 捕获到任何 target="_blank" 行为:
if (onClick && !href) {
return (
<button type="button" onClick={onClick}>
{content}
</button>
);
}
本地 Vault 路径仍保留 <a href> 语义(只是点击被劫持到 onOpenSource,用于在 workspace 中打开论文或文件)。
3.3 打开逻辑不变¶
点击后仍走 handleAgentOpenSource → openUrl(),在系统浏览器中打开外部链接。
4. 验收建议¶
- 让 Agent 做一个调研任务,使其回复底部的 Sources 出现外部 HTTP(S) 链接。
- 点击该外部链接:应在系统默认浏览器中打开对应网页。
- 应用内部不应再弹出空白标签页 / Webview 窗口。
- 本地 Vault 路径来源(如
papers/xxx/NOTES.md)仍可正常在 workspace 中打开。 pnpm run typecheck通过。
5. 边界¶
- 该修复只影响 Agent 回复底部的
Sources列表;inline citation(InlineCitationSource)本身已渲染为<button>,不存在_blank问题。 - 本地路径仍使用
<a>标签,但不带target="_blank",因此不会触发 Tauri 新建内部窗口。 - 若未来需要恢复“右键复制外部链接地址”或“中键在新浏览器标签页打开”等原生链接行为,需要改为全局 Rust 层拦截
_blank(方案 B),而非在前端保留<a target="_blank">。