Skip to content

feat(desktop): Markdown 目录改为 web view 页内的浮动大纲 - #1912

Merged
xiaoyumuxi merged 1 commit into
previewfrom
feat/desktop-markdown-web-outline
Nov 14, 2026
Merged

xiaoyumuxi merged 1 commit into
previewfrom
feat/desktop-markdown-web-outline

Conversation

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

关联 Issue / Linked issue

PR 类型 / Type

  • feat:新功能或用户可见的改进
  • fix:缺陷修复
  • docs:文档、Agent Note、Skill 等规则文件
  • release:稳定版发布准备

改动说明 / What changes

背景 / Why

Markdown 预览的“目录”弹层很难用:条目互相重叠,各级标题也没有缩进,看不出层级。它是 Viva 弹出列表,选中一个标题就关闭,没法一边看目录一边在章节之间跳转,也不知道自己读到了哪一节。

这次参考 Typora 的“大纲”面板:有 web 引擎时,目录由 web view 页面自己画,固定在右上角,按层级缩进,可以折叠,并高亮当前章节。同时修复原生弹出列表的重叠问题。

改动 / Changes

web view 页面的大纲(desktop/internal/webpreview/assets/)

  • markdown.html:新增 <aside id="outline">,包含标题栏和 role="tree" 的列表。
  • markdown.css:右上角浮动面板,宽 min(280px, 100vw - 24px),有圆角和阴影,分深浅两套底色。标题栏用大字,下面加分隔线。每行高 28px,悬停时加底色;当前章节用强调色的浅底和强调色文字;有子标题的行左侧带折叠箭头,展开时旋转 90°;超长标题用省略号截断;键盘焦点显示内描边。
  • markdown.js:
    • 每次渲染后用标题建树,算出层级、父级、同级序号和子节点。每深一级多缩进 16px。
    • 点箭头只折叠或展开,不滚动页面。点其他位置会跳到该标题:页面滚到那一行,并像用户自己滚动一样发出 scrolled,宿主照常把源码带到同一行。
    • 当前章节随页面顶行更新(提前半行算到达)。如果当前章节被折叠起来,就高亮可见的上级。指针不在面板上时,面板会自动滚到当前章节。
    • 键盘:上下方向键、PageUp/PageDown、Home/End 移动焦点;右键展开或进入第一个子标题,左键折叠或回到父级;回车或空格跳转。焦点通过 aria-activedescendant 指向当前行,每行带 aria-level、aria-posinset、aria-setsize、aria-expanded、aria-selected。
    • 虚拟化:行高固定,只创建视口内的行和前后各 8 行。上千个标题时,DOM 里也只有几十个节点。
    • 折叠状态按“父级键 + 层级 + 标题文字 + 同名序号”生成的键保存。编辑后重新渲染,折叠状态保留;已删掉的标题对应的键会被清理。
    • 按 Escape 时,如果开着图片查看器就先关它,否则关闭大纲,并回报 outline{open:false}。
    • 没有标题时,即使处于打开状态也隐藏面板。

宿主与页面的协议

  • 宿主发给页面的消息新增 outline{open},用来打开或关闭大纲。页面 ready 时,宿主按当前按钮状态重发一次,因为页面 ready 之前发出的消息会丢失。
  • 页面发给宿主的消息新增 outline{open},在用户从页面里关闭大纲时发送。宿主只更新按钮状态,不回发。webpreview.Message 新增 Open 字段,并更新了消息说明注释。
  • setup 的文案新增 outline,复用已有词条 desktop.markdown.contents,没有新增 i18n 词条。

目录按钮(desktop/internal/workbench/markdown_preview.go)

  • web 页和原生页共用一个按钮,改为可展开样式(Expandable、Expanded)。打开时用激活底色和正文色图标,关闭时只在悬停时显示底色,并带提示文字。
  • 新增 showContents,统一修改打开状态。web 页会同时收到 outline 消息。没有标题或文件过大时,按钮不显示,大纲也会关闭。
  • web 页不再画 Viva 弹出列表。

原生弹出列表修复(Linux 或 web view 打不开时使用)

  • 根因:PadSides(top, right, bottom, left) 的参数顺序传错,左缩进被传到了其他边,导致各行重叠,也没有缩进。修复后按参数顺序传值。
  • 缩进从最浅的标题级别开始计算:文档的标题如果从二级开始,不会整体多缩进一档。每深一级缩进 12 点(markdownContentsIndent),左右各留 8 点(markdownContentsInset)。

测试

  • 新增 TestMarkdownContentsIndentsHeadingsByLevel,检查原生弹出列表的行不重叠、每深一级多缩进 12 点、同级对齐,以及按钮的展开状态。修复前这个测试会失败:"Usage" at {859 197 36 20} overlaps "Keys" at {859 199 28 20}。
  • 改写 TestMarkdownWebPageRendersCoreHTMLAndScrollsWithTheSource 的目录部分,检查以下几点:
    • 加载时只发一次 outline{open:false}。
    • 点按钮后发出 outline{open:true},宿主不再画标题。
    • 页面回报跳转后,源码跟着滚动,宿主不回发 scroll。
    • 页面回报关闭后,按钮状态同步,宿主不回发。

文档

  • 功能矩阵 markdown-preview-outline:更新了能力描述,证据补上 web 页文件,并改写备注和验收步骤。两端仍是 implemented / pending。
  • Agent Note 2026-11-14-web-view-previews.md 补充了以下内容:
    • “Markdown 目录画在页面里”的决策。
    • 被否方案:web 页也用 Viva 弹出列表。原因是选中后就关闭,而且要高亮当前章节,宿主需要逐帧接收页面滚动位置。
    • 代价:web 页和原生页的目录外观不同。
    • 两个测试的验证说明。

不在本 PR 范围内 / Out of scope

  • Linux 原生页仍是弹出列表,没有做成可折叠、跟随当前章节的大纲。原生页只是后备,这次只修复它的排版。
  • 大纲面板浮在正文上方,和 Typora 一样,不会把正文往左挤。窄分栏里面板最多占到视口宽度减 24px,可以用按钮或 Escape 关闭。

类型专属:feat

多平台对齐

  • 两端都具备:macOS 和 Windows 共用同一套 Viva 代码和页面脚本(WKWebView / WebView2),本 PR 同时实现。Windows 上还没有真机验证,矩阵中 Windows 的验证状态保持 pending。
  • 只实现了 macOS:已为 Windows 提交跟进 Issue:#
  • 只实现了 Windows:已为 macOS 提交跟进 Issue:#
  • 刻意只属于某个平台(功能矩阵中另一端记为 platform-specific),理由:

范围检查 / Scope checks

功能矩阵 / Feature matrix

  • 已更新 shared/platform-feature-matrix/features/markdown-preview-outline.json:能力描述、两端证据(加入 web 页 html/js/css、markdown_web.go、webpreview.go 和两个测试文件)、备注和验收步骤。两端都还没有整机验收,状态保持 implemented / pending。
  • 已运行 ./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh(224 条通过)和 ./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD(通过)
  • 没有提交 .artifacts/ 下生成的矩阵视图
  • [N/A] 没有更新矩阵的理由:已更新

统一日志系统 / Structured logging

N/A:没有新增生命周期里程碑、操作终态、降级或失败路径。大纲的打开、关闭、折叠和跳转都是高频视图交互,不记日志。

界面文案 / i18n

  • 没有新增文案:大纲标题复用已有词条 desktop.markdown.contents,由宿主经 i18n.Shared 取出后通过 setup 传给页面;按钮提示文字也用这个词条。
  • 已运行 node scripts/i18n/verify-app-language-localization.mjs(通过)和 node scripts/i18n/generate-i18n.mjs --check(en-US、zh-CN 均 11125/11125),没有放宽脚本或扩大豁免名单

逻辑归属 / Logic placement

  • 标题本身来自 Core:web 页用 markdown.render 输出中带源码行的标题,原生页用源码标题。这次只新增标题树、折叠和当前章节高亮,都属于视图呈现,且依赖页面逐帧的滚动位置,所以写在页面脚本里,不进 Core。macOS 和 Windows 共用同一份脚本。Agent Note 记录了这一取舍。
  • [N/A] 平台层没有可迁入 Core 的既有共享逻辑被修改
  • 没有按工具名加分支

性能与内存 / Performance and memory

  • 没有新增后台任务、轮询或缓存。大纲只在打开时排版,并按固定行高虚拟化,只创建视口附近的行。当前章节的计算挂在已有的滚动回报帧上,对标题做二分查找。
  • 宿主和页面之间只多了一个布尔值的 outline 消息,标题列表沿用已有的 rendered 消息,没有额外传输。
  • [N/A] 性能基线:没有改动编辑器模块(desktop/internal/editor/**、editorview/**),没有运行 editor-bench.sh。也没有新增运行时或依赖。

设置 / Settings

N/A:没有新增或修改设置项。

插件契约 / Plugin contract

N/A:没有改插件契约、能力版本、插件函数协议或宿主方法。outline 是桌面端和自己内嵌页面之间的消息,不对插件开放。

原计划编号 方法 用途
— — 无

发行包与资源 / Bundle and resources

  • 运行时不写 app bundle 或安装目录:页面资源仍从二进制读取,没有新增运行时资源路径。已运行 ./scripts/desktop/verify/verify-runtime-bundle-immutability.sh(通过)
  • [N/A] 资源复用清单:没有新增下载、解压、生成或缓存资源

无用代码与重构 / Dead code and refactoring

  • 本次改动导致无用的代码已删除:web 页不再声明 Viva 弹出列表,这部分只保留给原生页。
  • [N/A] 本次没有发现之前就存在的无用代码或可重构之处

验证 / Verification

命令或操作 平台 结果
scripts/desktop/desktop-go.sh test ./internal/workbench ./internal/webpreview -count=1(完整两个包,不加过滤) macOS arm64 通过(workbench 147s,webpreview 0.9s)
在修复前的代码上运行 TestMarkdownContentsIndentsHeadingsByLevel macOS arm64 失败:"Usage" at {859 197 36 20} overlaps "Keys" at {859 199 28 20};修复后通过
用无头 Chrome 加载真实的 markdown.html/js/css,由测试脚本模拟宿主发送 setup、render、outline,并通过 CDP 断言(约 100 个标题) macOS Chrome 31 项断言全部通过,覆盖:
• 显示与标题
• 只创建视口附近的行
• 层级与缩进
• 行不重叠
• 箭头折叠且不滚动页面
• 点行跳转并回报 scrolled
• 方向键、回车和 aria-activedescendant
• 滚动时更新当前章节,折叠时高亮上级
• 重新渲染后保留折叠
• 树滚到底部
• Escape 关闭并回报 outline{open:false}
• 无标题时隐藏
浅色、深色主题截图人工检查(1100×760,2x) macOS Chrome 面板在右上角;标题下有分隔线;层级缩进、折叠箭头、当前章节高亮、长标题省略号都正常
node scripts/i18n/verify-app-language-localization.mjs、node scripts/i18n/generate-i18n.mjs --check macOS 通过
./scripts/desktop/verify/verify-runtime-bundle-immutability.sh macOS 通过
./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh、verify-platform-feature-matrix-change.sh origin/preview HEAD macOS 通过
./scripts/agent-notes/verify-agent-notes.sh macOS 222 篇通过
gofmt -l、node --check markdown.js macOS 无输出 / 通过

未能执行的检查和原因:

  • 还没有在整机 Lithe 中验收:在 WKWebView 里点按钮、折叠、跳转,以及焦点在页面内时按 Escape 关闭。页面行为已用 Chrome 验证,宿主消息已用 Go 测试验证。标记 ready 前补做 macOS 整机验收。
  • Windows(WebView2)没有真机,未验证。
  • Linux 原生弹出列表只做了组件测试,没有在 Linux 上运行。
  • 截图来自上面的 Chrome 测试页,无法通过 gh 上传,所以没有附在 PR 中。

提交前确认 / Final checks

  • 分支从最新的 preview(7c036b3b8)创建;目标分支:preview
  • 以 Draft 提交,本地验证通过后才标记 ready
  • 没有无关改动,没有提交 .build/、target/、dist/、.artifacts/ 等产物,没有凭据、个人路径和未脱敏的日志或截图
  • 本次没有启动 Lithe 应用;测试用的无头 Chrome 由脚本启动,结束后已关闭,确认没有残留进程

- web view 页在右上角显示大纲面板:标题栏加分隔线,按层级缩进,
  有子标题的行可折叠,当前章节随页面滚动高亮,点行或方向键加回车跳转;
  固定行高虚拟化,只创建视口附近的行;折叠状态按稳定键保存,重新渲染后保留。
- 宿主与页面新增 outline{open} 消息:目录按钮打开或关闭面板,
  页面内按 Escape 关闭时回报宿主同步按钮。
- 目录按钮改为可展开样式,打开时高亮。
- 修复原生弹出列表 PadSides 参数顺序错误导致的行重叠和缩进缺失,
  按最浅标题级别计算缩进,并补回归测试。
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 14, 2026
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review November 14, 2026 09:29
@xiaoyumuxi
xiaoyumuxi merged commit b582e20 into preview Nov 14, 2026
7 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant