---
url: /wave-agent/specs/ui/markdown-links.md
description: webview 中链接的解析与点击行为：消息 markdown、行内代码内的 URL、围栏代码块内的 URL、bash 命令输出中的 URL
---

# 功能规格说明：Webview 链接解析

**创建日期**：2026-08-21

**最近更新**：2026-09-21（围栏代码块内的裸 URL 也提升为可点击链接，推翻此前「仅行内代码」的范围）

> 本规格覆盖 VS Code / JetBrains / Desktop 共享 webview（`packages/webview`）中链接的解析与点击行为，包括消息 markdown 渲染与 bash 命令输出。链接点击后的宿主路由（desktop 的 localhost 预览面板分流）见 [桌面端原型预览](../desktop/desktop-preview.md)，此处不重复。

## 用户场景与测试 *（必填）*

### 用户故事：行内代码中的链接可点击（优先级：P1）

作为用户，我希望助手文本 markdown 中位于行内代码（反引号）里的 http(s) 链接也能像普通链接一样被解析并点击打开，以便 agent 用代码样式展示 URL 时仍可直接访问，无需手动复制。

**为什么是这个优先级**：agent 输出 URL 时常以反引号包裹（避免被 markdown 误处理），当前这类 URL 渲染为纯文本代码、不可点击，是高频打断阅读的场景。

**独立测试**：渲染包含 `` `https://example.com/a/b` `` 的助手 markdown 文本，验证其保留代码样式、同时渲染为可点击链接，点击行为与普通链接一致。

**验收场景**：

1. **假设**助手文本含行内代码 `` `https://example.com/a/b` ``，**当**渲染时，**则**保留代码样式，同时渲染为可点击链接；点击行为与普通链接一致——desktop 上 localhost 链接在预览面板打开、其余链接用系统浏览器，IDE 上按原生链接处理。
2. **假设**行内代码内容不是合法 http(s) 链接（如 `` `var x = 1` ``、`` `www.example.com` ``（无协议）、相对路径 `` `/foo` ``），**当**渲染时，**则**保持纯代码样式，不可点击。
3. **假设**行内代码内是 `[text](url)` 形式的 markdown 链接语法原文，**当**渲染时，**则**保持代码原文展示，不按链接语法解析——仅提升裸 URL。
4. **假设**行内代码 URL 首尾带常见标点（如 `` `https://example.com.` `` 或 `` `（https://example.com）` ``），**当**渲染时，**则**链接可点击，且首尾标点不进入链接目标。
5. **假设**行内代码内容为 `javascript:` 等非 http(s) 协议，**当**渲染时，**则**不得生成链接。

***

### 用户故事：围栏代码块中的 URL 可点击（优先级：P1）

作为用户，我希望助手在围栏代码块（\`\`\`）里打印的 http(s) 地址（本地服务/预览地址、文档或 PR 链接等）也能像正文里的链接一样点击打开，以便 agent 用代码块展示地址（避免被 markdown 误处理、也便于整段复制）时我仍能直接点开，不必手动复制到浏览器。

**为什么是这个优先级**：agent 常把服务地址单独放进代码块（起本地服务后的 `http://127.0.0.1:8097/`、文档链接），这是点开频率最高的形态之一；此前这类地址渲染为纯文本、完全不可点击（用户 2026-09-21 报告），与行内代码通道的既有能力不一致。

**独立测试**：渲染正文为一个只含 `http://127.0.0.1:8097/` 一行的围栏代码块，验证该地址渲染为可点击链接、代码块其余原文（换行与缩进）不变，点击行为与消息文本链接一致。

**验收场景**：

1. **假设**代码块内一行是裸 http(s) URL（如 `http://127.0.0.1:8097/`、`https://example.com/docs`），**当**渲染时，**则**该地址渲染为可点击链接，代码块其余内容（换行、缩进、语言类名）保持原样；点击行为与消息文本链接一致——desktop 上 localhost 链接在预览面板打开、其余链接用系统浏览器，IDE 上按原生链接处理。
2. **假设** URL 出现在代码行内部（如 `curl http://127.0.0.1:3000/health`、`"url": "https://example.com/api"`），**当**渲染时，**则**仅 URL 部分可点击，两侧引号与其余代码文本保持原样、不进入链接目标。
3. **假设**代码块内 URL 首尾带标点或后紧跟全角括号注释（如 `http://127.0.0.1:8097/（本地预览）`），**当**渲染时，**则**链接目标止于标点之前，标点及其后注释文本照常可见（与正文同一终止规则）。
4. **假设**代码块内容是 `javascript:`、`file:` 等非 http(s) 协议或 HTML 片段（`<div>&</div>`），**当**渲染时，**则**不生成链接，文本完整转义后按原文展示。
5. **假设**代码块内不含 URL（`npm run build`、`var x = 1`），**当**渲染时，**则**保持原文，不生成链接；其中文件路径部分仍按 [文件路径链接](file-path-links.md) 规则可点击。

***

### 用户故事：裸 URL 后紧跟全角标点时链接正确终止（优先级：P1）

作为用户，我希望消息文本里的裸 URL 后面紧贴中文标点或全角括号时（如 `https://github.com/netease-lcap/wave-agent/pull/2217（commit 说明`、`https://example.com/b（中文说明）后`），链接目标止于标点之前、其后的文字作为正文正常显示，以便中文写作习惯下（URL 后直接接括号注释、不空格）链接依旧指向正确地址，注释文字也不会被吞掉或丢失。

**为什么是这个优先级**：中文文本里 URL 后紧跟「（说明）」而中间不打空格是高频写法；当前实现只在空白分隔 token 的**尾部**剥离标点，URL 后的全角括号及其后正文会被整体吞进链接目标（点击打开带 `%EF%BC%88` 的错误地址），或在 bash 输出通道整段注释被丢弃。

**独立测试**：渲染含 `https://example.com/b（中文说明）后` 的助手消息，验证链接目标为 `https://example.com/b`、`（中文说明）后` 作为正文可见。

**验收场景**：

1. **假设**消息文本为 `https://github.com/netease-lcap/wave-agent/pull/2217（commit 说明`（全角开括号后直接接正文、中间无空白），**当**渲染时，**则**链接目标止于 `/pull/2217`，`（commit 说明` 作为正文显示。
2. **假设**消息文本为 `https://example.com/b（中文说明）后`，**当**渲染时，**则**链接目标为 `https://example.com/b`，`（中文说明）后` 整体作为正文显示（既不进链接目标，也不被丢弃）。
3. **假设** URL 后紧贴其他全角标点（如 `https://example.com，见说明`、`访问 https://example.com。`），**当**渲染时，**则**链接目标止于标点之前，标点及其后文本保持可见。
4. **假设** URL 自身含合法字符（`_`、`#frag`、查询串中的中文如 `?q=你好`、成对 ASCII 括号如 `/foo(bar)`），**当**渲染时，**则**链接目标完整保留，不受本规则影响。
5. **假设** bash 命令输出中的 URL 后紧跟全角括号（如 `Server at http://localhost:8000（说明）`），**当**渲染时，**则**与消息文本同规则：链接目标止于全角标点之前，标点及其后文本保持可见。

***

## 用户故事：bash 命令输出中的 URL 可点击（优先级：P1）

作为用户，我希望 bash 命令输出（`.bash-command-output`）中的 http(s) URL 被解析为可点击链接，且点击行为与消息文本链接一致（desktop 上 localhost 链接在预览面板打开、其余链接用系统浏览器，IDE 上按原生链接处理），以便运行本地服务或查看命令日志时直接点击访问，无需手动复制。

**为什么是这个优先级**：运行 `python -m http.server`、`npm run dev` 等命令时，输出中的 `http://localhost:8000/` 类地址是高频操作目标；当前 bash 输出为纯文本、不可点击，用户只能复制粘贴。

**独立测试**：渲染包含裸 URL（如 `Server running at http://localhost:8000/`）的 bash tool result，验证 URL 渲染为可点击链接、其余文本保持原样，点击行为与消息文本链接一致。

**验收场景**：

1. **假设** bash 命令输出含裸 http(s) URL（如 `Server started at http://localhost:8000/`），**当**渲染时，**则** URL 渲染为可点击链接，其余文本保持原样；点击行为与消息文本链接一致——desktop 上 localhost 链接在预览面板打开、其余链接用系统浏览器，IDE 上按原生链接处理。
2. **假设** URL 首尾带标点（如 `visit https://example.com.` 或 `（https://example.com）`），**当**渲染时，**则**链接可点击，且首尾标点（含中文标点）不进入链接目标；URL 后紧跟括号注释（如 `见 https://example.com（帮助）`）时注释文本保持可见。
3. **假设**输出含 `javascript:`、`file:` 等非 http(s) 协议、`<script>` 等 HTML 片段或 `&`、`<` 等需转义字符，**当**渲染时，**则**不生成链接，非 URL 文本经 HTML 转义后按原文展示，无注入风险。
4. **假设**输出为纯文本无 URL（如错误信息），**当**渲染时，**则**保持原样展示，渲染路径无行为变化。
5. **假设**命令输出为多行且部分行含 URL，**当**渲染时，**则**仅含 URL 的行内生成链接，其余行保持原样，换行结构不变。

***

## 边界情况

* **bash 命令输入行（`.bash-command-input`）里的 URL 怎么办？** 不处理——仅链接化输出（result），命令本身保持代码样式原文。命令是 agent 执行的代码而非用户阅读的地址。
* **其他工具 result（`.result-raw`、`.lsp-output` 等）里的 URL 怎么办？** 不在本次范围内，保持纯文本；如后续需要可复用同一链接化函数扩展。
* **行内代码内含多个 URL（如 `` `https://a.com https://b.com` ``）怎么办？** 内容整体不是单个 URL，保持代码原文、不生成链接，避免歧义。
* **围栏代码块中的 URL 怎么办？** 提升为可点击链接（用户 2026-09-21 口径，推翻此前「仅行内代码、代码块保持原文」的范围）：代码块内按空白分词，逐 token 切出其中的裸 http(s) URL 提升为链接，token 内其余部分保持代码原文（含文件路径识别，见 [文件路径链接](file-path-links.md)）；mermaid 代码块渲染为图表，不参与。与行内代码通道的差别只在粒度——行内代码要求整串就是一个 URL，代码块是按 token 切分后逐段提升。
* **URL 内部含全角标点怎么办？** 一律按终止符处理——全角标点在 URL 中本就应百分号编码，中文语境下它更像是正文分隔符；因此把 URL 候选截断在第一个全角标点之前。ASCII 括号不受影响（成对是 URL 内容，如 `/wiki/Hello_(disambiguation)`）。
* **推理（reasoning）文本块中的行内代码怎么办？** 与助手文本一致——共用同一 markdown 渲染路径，行为自然对齐。
* **安全如何保证？** 仅 http(s) 协议被提升为链接，`javascript:` 等危险协议不生成链接；非 URL 文本在注入前完整 HTML 转义；渲染结果仍经 DOMPurify 过滤。
