feat(core): add offset-based sync-scroll preview primitives#148
Open
Caldalis wants to merge 1 commit into
Open
feat(core): add offset-based sync-scroll preview primitives#148Caldalis wants to merge 1 commit into
Caldalis wants to merge 1 commit into
Conversation
Implements roadmap floatboatai#13 (markdown live preview sync scroll): three core primitives plus a reference split-preview pane in the electron demo, behind an opt-in toggle (default off). Offset-anchored, not percentage-based - markdown blocks render at wildly different heights, so proportional sync cannot keep two panes aligned. packages/core: - New rAF-throttled scroll event ({ offset, ratio }) computed via lineBlockAtHeight in CM6 content-space; no screen-coordinate math. - New EditorAPI.scrollToOffset(offset, ratio?) as the write-side counterpart, same line-based unit. - New renderPreviewHtml(ast): mdast -> HTML via the existing remark-rehype/rehype-stringify deps (transform-only, no re-parse), tagging top-level blocks with data-offset scroll anchors. - lezer-mdast-adapter: emit spec-shaped mdast for GFM tables (align parsed from the delimiter row instead of hardcoded []) and task-list items (gap text recovered into a paragraph child). Without these the preview rendered cell-less tables and textless task items; other getAst() consumers (e.g. plugin-wordcount silently skipping task-item text) were affected too. Scope rationale in design.md. - Export ScrollPayload type. apps/electron-demo: - New preview-panel.ts: read-only rendered pane, bidirectional block-top scroll sync with a two-frame echo guard; link clicks intercepted (plain click swallowed, Ctrl/Cmd-click opens externally, absolute http(s) URLs only). - Toolbar toggle + splitPreview setting (default off). - togglePanel now takes an explicit show-display value, fixing a latent display="" restore bug also affecting outline/vault/backlinks panels. openspec: add-sync-scroll-preview (proposal, design, specs, tasks). No new runtime dependencies.
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 摘要
Offset-based (not percentage) two-way scroll sync between the editor and a new read-only rendered preview pane: three
small core primitives (
scrollevent,scrollToOffset,renderPreviewHtml) plus a reference implementation in theelectron demo behind an opt-in toggle (default off).
Motivation / 背景与动机
roadmap maintenance rules
openspec/changes/add-sync-scroll-preview(proposal / design / 2 spec deltas / tasks, included inthis PR)
This re-attempts roadmap #13 with the concerns that closed PR #118 designed out from the start:
Single concern — scroll sync only. No keybinding UI. Wiki-link rendering in the preview is an explicit
non-goal: it surfaced a pre-existing
lezer-mdast-adapter.tsquirk, documented in design.md as a standalone follow-upinstead of bundled here. (Two other narrow adapter fixes — table
alignand task-item text — are included becausethe preview functionally depends on them; the scope judgment distinguishing the two cases is in design.md.)
Offset-anchored, not percentage — the editor emits the document offset at the top of the viewport
(
lineBlockAtHeight, CM6 content-space); the preview aligns the nearestdata-offsetblock top. Sub-block ratiointerpolation was deliberately dropped after self-review found the two sides measure "ratio" in incompatible units
(source line vs. AST block) — design.md has the full analysis.
Opt-in — default off; zero default-behavior change.
Changes / 变更内容
packages/core:scrollevent{ offset, ratio }— ViewPlugin onscrollDOM, content-spacelineBlockAtHeight, no screen-coordinatemath; the CM6
EditorViewis still never exposed to hostsEditorAPI.scrollToOffset(offset, ratio?)— write-side counterpart, same line-based unitrenderPreviewHtml(ast)— mdast → HTML via the already-presentremark-rehype+rehype-stringify(transform-only, no re-parse),top-level blocks tagged with
data-offset; exportedScrollPayload; README updatedlezer-mdast-adapter.ts, called out explicitly:adaptTablehardcodedalign: [](andmdast-util-to-hastemits exactlyalign.lengthcells per row, so preview tables rendered as cell-less<tr>shells) and the Task branch lookedfor child nodes where Lezer keeps item text implicit in gaps (task items rendered as bare checkboxes). Fixed at the source rather than papered
over in the preview — the task text is unrecoverable downstream, and other
getAst()consumers (e.g. plugin-wordcount silently skipping task-itemtext) were affected too. Full scope judgment vs. the deliberately-deferred wiki-link adapter bug is in design.md;
exportHTML()is unaffected(it re-parses via remark-parse). Happy to split these into their own PR if preferred.
apps/electron-demo:preview-panel.ts: read-only rendered pane, bidirectional block-top sync, two-frame echo guardelectron/main.tshas nowill-navigateguard, so an unhandledclick would navigate the whole BrowserWindow away); Ctrl/Cmd-click opens externally — same convention as the inline
live-preview link renderer; non-absolute hrefs are a no-op
splitPreviewsetting (default off)togglePanel()restored panels withstyle.display = "", whichdrops the inline
display:flexthese panels rely on. It now takes an explicit show-display value — this also fixesthe same latent bug for the outline/vault/backlinks panels. Rationale in design.md; happy to split this out if you
prefer.
openspec/:add-sync-scroll-preview— proposal, design (decisions + self-review findings), spec deltas foreditor-coreandsync-scroll-preview, tasksTesting / 测试
pnpm testpasses — 535 tests, 20 new acrossscroll-event.test.ts,live-preview-to-html.test.ts(incl. 3 adapter regression tests:table cells + alignment, task text + checkbox + inline formatting, nested blocks under a task),
preview-panel.test.tspnpm build,pnpm build:electron-demo,pnpm typecheckall cleanacross headings, paragraphs, code fences, and tables; no feedback-loop jitter; panel stays off on a fresh profile. These passes surfaced and drove
the fixes for three real-browser-only layout bugs, the window.open relative-href issue, and the empty-table / textless-task adapter bugs
(tasks.md 4.4, 6.9, 6.10).
Compliance / 合规自检
CLA signed
AI disclosure:
AI辅助笔记:
工具:claude code(主要编码助手);AI辅助也用于英文文档编写和代码审查。
AI的使用方式:
代码库理解——在设计工作之前,探索现有的实时预览/
lezer-mdast-adapter/演示面板架构。
代码草拟——我设定了设计
AI根据该设计草拟了代码;
我审查并根据情况修改了代码。
测试草拟——AI帮助编写了我指定行为的测试用例
英文写作——我的工作语言是中文; AI 帮助我规范化了我的设计笔记和想法,并将其整理成英文 README 部分、OpenSpec 提案/设计/规范/任务以及代码注释,此外还处理了一些繁琐和机械性工作
搜索查询,整理资料
由我完成:上述设计决策及其权衡(记录在 design.md 中);所有功能代码的代码审查;所有手动验证
我可以解释并捍卫此 PR 中的每个设计决策。
(节流、销毁清理、数据偏移完整性、链接点击安全性包括 wiki 链接垃圾 href 的情况)
New dependencies: none
No build artifacts committed
No secrets committed
Checklist / 自检清单
live-preview-table.tsnot touched (n/a)add-sync-scroll-preview)Screenshots / Recordings