Skip to content

feat(core): scroll-synced split preview via sourceMap export (ROADMAP #13)#116

Open
zaizailxm wants to merge 2 commits into
floatboatai:mainfrom
zaizailxm:feat/core-preview-sync-scroll
Open

feat(core): scroll-synced split preview via sourceMap export (ROADMAP #13)#116
zaizailxm wants to merge 2 commits into
floatboatai:mainfrom
zaizailxm:feat/core-preview-sync-scroll

Conversation

@zaizailxm

Copy link
Copy Markdown

Summary / 摘要

Scroll-synced split preview (ROADMAP #13): exportHTML({ sourceMap: true }) stamps blocks with source offsets, new EditorAPI scroll-anchor APIs, a createScrollSync() bidirectional sync engine in core, and a split-preview reference integration in the electron demo.

Motivation / 背景与动机

Inline live preview hides the rendered form of the exact block the cursor is in, and users who edit in pure source mode have no rendered view at all. A split preview needs scroll sync to be usable — naive percentage sync drifts badly when content renders at a different height than its source (code fences, tables). This PR implements offset-anchored sync as the roadmap requests.

Changes / 变更内容

  • packages/core:
    • exportHTML(options?) — optional { sourceMap: true } stamps block elements with data-nexus-from / data-nexus-to source offsets (new rehypeSourceMap). Descends into list/blockquote containers so outline-style documents get per-item anchors. Default output is byte-identical to before.
    • EditorAPI: new scroll event; getScrollAnchor() / scrollToAnchor(offset) with sub-line interpolation over CM6 line blocks.
    • scroll-sync.ts: createScrollSync({ editor, preview }) — bidirectional sync. Pure mapping functions linearly interpolate inside anchors and across gaps; a driver lock (150 ms sliding window) suppresses programmatic-scroll feedback loops; handlers are rAF-coalesced; hidden previews are skipped; anchors cache invalidates on refresh() / window resize.
    • themeToCssVars(theme) exported so host UI outside the editor DOM can apply the same theme variables.
  • apps/electron-demo:
    • preview-pane.ts — reference integration: toolbar ◨ toggle + persisted "Split preview" setting; re-renders on debounced change events and explicitly after silent file loads (which by design bypass the change pipeline).
  • docs/ROADMAP.md / docs/ROADMAP.zh.md: feat(slash): add priority sort and result limit to slash commands #13 → done. packages/core/README.md: new "Scroll sync & source-mapped HTML export" section.

Testing / 测试

  • pnpm test passes — 559 tests / 37 files (44 new)
  • Affected packages build (pnpm build), pnpm -r exec tsc --noEmit clean
  • New vitest cases: mapping math incl. inverse round-trips and full-range monotonicity sweeps; negative overscroll and zero-height anchors; source-map granularity (top-level, nested lists, blockquotes, inline exclusion); sync engine (both directions, echo suppression, driver handback, hidden-pane guard, destroy); preview pane lifecycle incl. the silent-load refresh path
  • Manual verification in the packaged Electron app (driven via playwright-core): typing updates, bidirectional wheel scroll, toggle, dark theme + restart persistence, 400-section document (801 anchors, editor at ratio 1.0 → preview follows to 0.987), and jump across a 40-line code fence lands editor/preview targets within 13px of each other — the case percentage-based sync fails.

Compliance / 合规自检

  • CLA signed — will sign when the bot prompts
  • AI disclosure: this PR was developed with substantial AI assistance (Claude Code), under my direction and review — the choice of roadmap item, the anchor-interpolation approach, API shape, and trade-offs were reviewed by me and I can explain and defend them. Disclosed per GOVERNANCE.md §6.2; the evaluation task this PR was prepared for explicitly permits unrestricted AI tool use.
  • New dependencies: none (uses the existing remark/rehype/unified pipeline)
  • No build artifacts committed
  • No secrets committed

Checklist / 自检清单

Screenshots / Recordings · 截图或录屏 (UI changes)

(见下方附图:分屏打开、双向滚动同步对齐)
split-preview-open
split-preview-scroll-sync
split-preview-fence-alignment

zaizailxm and others added 2 commits July 3, 2026 14:20
…loatboatai#13)

Core:
- exportHTML({ sourceMap: true }) stamps blocks with data-nexus-from/to
  source offsets (rehypeSourceMap); descends into list/blockquote
  containers so outline-style documents get per-item anchors. Default
  exportHTML() output is byte-identical to before.
- EditorAPI: "scroll" event, getScrollAnchor() / scrollToAnchor(offset)
  with sub-line interpolation over CM6 line blocks.
- createScrollSync(): bidirectional editor<->preview sync. Linear
  interpolation between source-mapped anchors keeps unevenly rendered
  content (code fences, tables) aligned; a driver lock with a 150ms
  window suppresses programmatic-scroll feedback loops; rAF-coalesced
  handlers; hidden previews are skipped entirely.
- themeToCssVars() exported so host UI outside the editor DOM can apply
  the same theme variables instead of hand-copying names.

Electron demo:
- Split preview pane (toolbar toggle + persisted setting), rendered via
  exportHTML sourceMap and wired through createScrollSync. Re-rendered
  on debounced change events and explicitly after silent file loads,
  which bypass the change pipeline.

Tests: 38 new (mapping math incl. inverse round-trips, source-map
granularity, feedback-loop suppression, hidden-pane guard, preview pane
lifecycle). Verified end-to-end in the packaged Electron app: typing,
bidirectional wheel scroll, live update, toggle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…export

Full-range monotonicity sweeps for both mapping directions (no NaN, no
regressions across anchor gaps), negative scrollTop (elastic overscroll),
zero-height anchors, and empty-document behavior for exportHTML sourceMap
and the scroll-anchor APIs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Jul 3, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@redreamality redreamality left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR #116.

This is a substantial implementation of ROADMAP #13: scroll-synced split preview using source-offset anchors rather than naive percentage syncing. The scope matches the roadmap entry, and the PR correctly documents that OpenSpec is not required for this item.

Validation performed in an isolated checkout:

pnpm exec tsc --noEmit -p packages/core/tsconfig.json
pnpm exec tsc --noEmit -p apps/electron-demo/tsconfig.json
pnpm exec tsc --noEmit -p packages/react/tsconfig.json
pnpm exec tsc --noEmit -p packages/vue/tsconfig.json
pnpm --filter @floatboat/nexus-core test -- scroll-sync export-html-source-map --reporter=verbose
pnpm --filter @floatboat/nexus-core test -- --runInBand

These checks passed.

Review notes:

  • CLA is passing.
  • exportHTML({ sourceMap: true }) preserves the default non-source-map behavior while stamping rendered blocks with source offsets for sync consumers.
  • createScrollSync() has sensible bidirectional mapping primitives, rAF-coalescing, cached anchor invalidation, hidden-preview short-circuiting, and teardown of editor/DOM/window listeners.
  • The demo integration keeps the preview hidden by default, re-renders only when needed, refreshes after silent file loads, applies shared theme CSS variables, and destroys listeners cleanly.
  • Tests cover source-map generation, mapping edge cases, editor scroll APIs, and preview pane lifecycle behavior.

One local environment note: pnpm --filter @floatboat/nexus-electron-demo test -- preview-pane --reporter=verbose could not start because Rollup's macOS optional native package failed to load under this local environment/code-signature setup. This is the same Rollup optional-dependency/native loading issue seen in prior reviews and is not evidence of a PR code failure; the electron-demo TypeScript check passed, and the preview-pane test source itself was reviewed.

Conclusion: approved from code-review perspective.

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.

3 participants