Skip to content

feat(markdown): 预览态就地编辑(VS Code 式锁 / 笔滑钮) - #27

Merged
xhqing merged 1 commit into
mainfrom
feat/markdown-editor
Nov 6, 2026
Merged

xhqing merged 1 commit into
mainfrom
feat/markdown-editor

Conversation

@xhqing

@xhqing xhqing commented Nov 6, 2026

Copy link
Copy Markdown
Owner

背景

Markdown 面板只有「只读渲染」和「整篇源码」两种形态,想改一句话要切到源码模式里找。目标是在渲染结果里直接改内容,交互参照 VS Code 新版 Markdown Editor:右上角「锁 / 笔」滑动开关,锁定时点击内容只闪一道镜面光泽提醒,解锁后点某一块即可就地编辑。

为什么不是「搬 VS Code 的那套」

  • @vscode/markdown-editor(MIT)在 WebKit 里启动即 ReferenceError: Can't find variable: EditContext —— 它用 Chromium 专有的 EditContext 做文本输入,WKWebView 没有这个 API(本机离屏 WKWebView 实测)。
  • Milkdown(上游 NEXT 分支用的引擎)能在 WebKit 跑,但 getMarkdown() 往返会改写未编辑的块:实测把 | --- | --- | 写成 | -- | -- |、把 $$ 块公式 $$ 变成行内 $…$。
  • 上游 PR Add inline rich Markdown editing manaflow-ai/cmux#12479 是「整篇 contenteditable + DOM→Markdown 序列化」,其 Limits 明写保存会归一化整个文档,且至今 OPEN + CONFLICTING。

做法

渲染后把 marked 的顶层 token 与渲染出的 DOM 子节点并行走一遍,给每个块打上它在源文件里的 [start, end) 区间(并做一次平铺校验,保证区间无空隙、无重叠)。点块 → 该块换成这段原文的浮层编辑器;提交 → 只替换这一段字节。

因此没动过的块一个字节都不会变:表格、代码围栏、frontmatter、手工换行都保真。只读渲染路径本身没有改动,编辑层只在 .editor 模式安装、切回去就卸载。

交互

  • 预览面板头部新增铅笔按钮进入该模式;eye 回只读、doc.plaintext 回整篇源码。
  • 面板右上角锁 / 笔滑钮;锁定态点击内容 = 滑钮闪光泽,不进入编辑。
  • Cmd+S 先提交正在编辑的块再落盘;切换模式前也会先提交。
  • 锁状态记在 markdown.editor.locked,下一个文档沿用(和 VS Code 一样)。

验证

离屏 WKWebView(不抢焦点):

  • README.md:24 块 / 4804 字节;PATCHES.md:71 块 / 13968 字节;CHANGELOG.md 前 120 行:42 块 / 14362 字节——非空白字节全部落在块内、无空隙、无重叠、拼接后与原文件逐字节相同(roundTrip: true)。
  • 点击开块 → textarea 取到该块原文 → 提交后消息里的文档结构完整(空行保留)。
  • 锁定时点击只触发滑钮光泽、不开编辑层。
  • swiftc -parse 通过;lint-pbxproj-test-wiring / config schema / latency guardrails / gitleaks 均通过。

已知边界

  • 一次编辑一个块;块内结构仍写 Markdown(不是 Typora 的表格网格)。
  • 编辑浮层不跟随滚动(v1)。
  • 超大文档(>300 KB)渲染本身较慢,就地编辑会更明显;预览模式不受影响。
  • 完整编译与真机交互由 Release 构建 / CI 兜底(本机只有 CommandLineTools,没有完整 Xcode)。

@xhqing
xhqing enabled auto-merge (squash) November 6, 2026 13:30
…ck toggle

The markdown panel could only be read (rendered preview) or edited as the
whole document (NSTextView source mode). Add a third mode that keeps the
rendered document and lets a block be edited where it is.

Design: the renderer stamps every top-level block with the byte range it
came from, so committing an edit replaces exactly that slice of the file.
Nothing is ever re-serialized from HTML, which means blocks the user did not
touch — tables, code fences, frontmatter, manual line breaks — keep their
original bytes.

VS Code's own Markdown Editor engine could not be reused: @vscode/markdown-editor
constructs an EditContext, a Chromium-only API that WKWebView does not have
(verified: ReferenceError on boot). Milkdown round-trips through remark and
rewrites untouched blocks, so it would need a source map to be safe. Block
ranges over the existing renderer give the same guarantee with no new engine.

The panel gets a pencil button that enters the mode; inside it a lock/pencil
pill (VS Code's design pins to the top-right. Clicking content while locked
only flashes a shine across the pill. Cmd+S flushes the open block first;
switching modes commits before leaving.

Verified offscreen in WKWebView (no focus stealing): block ranges tile the
whole source with no gaps or overlaps and round-trip byte-identically on
README.md, PATCHES.md and a CHANGELOG slice; clicking a block loads its raw
markdown, committing posts the edited document, and the locked pill shines
instead of opening an editor.

Co-Authored-By: pi <noreply@pi.dev>
EOF
)
@xhqing
xhqing force-pushed the feat/markdown-editor branch from 2e07bac to 586ea64 Compare November 6, 2026 13:32
@xhqing
xhqing merged commit 78b57ec into main Nov 6, 2026
1 check passed
@xhqing
xhqing deleted the feat/markdown-editor branch November 6, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant