Skip to content

Commit 62f4eaa

Browse files
authored
Merge branch 'quarto-dev:main' into main
2 parents c75db3c + a5fc4c0 commit 62f4eaa

92 files changed

Lines changed: 5997 additions & 212 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 31 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ exclude = [
2424
resolver = "2"
2525

2626
[workspace.package]
27-
version = "0.6.0"
27+
version = "0.7.0"
2828
authors = ["Posit Software, PBC"]
2929
homepage = "https://github.com/posit-dev/quarto-markdown-syntax"
3030
keywords = ["parser"]
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# q2-preview scroll sync (bd-9kzfi)
2+
3+
## Overview
4+
5+
Scroll sync (editor ↔ preview) works for the HTML preview (`MorphIframe`)
6+
but is a no-op for the q2-preview format (`ReactPreview``Q2PreviewIframe`).
7+
Two gaps:
8+
9+
1. **No source-line attributes in the q2-preview DOM.** `MorphIframe` maps
10+
editor line ↔ preview position via `data-loc="fileId:startLine:startCol-endLine:endCol"`
11+
attributes the Rust HTML writer stamps on each element. The q2-preview React
12+
renderer stamps only `data-sid` (and only under attribution). `findElementForLine`
13+
has nothing to match.
14+
2. **No scroll plumbing on the React path.** `ReactPreview` never calls
15+
`useScrollSync`; `Q2PreviewIframe` exposes no `scrollToLine`/`getScrollRatio`
16+
handle and attaches no `scroll`/`click` listeners; `ReactRenderer` threads none.
17+
18+
Per-node line numbers already ride the wire: `renderPageInProject` emits
19+
`include_inline_locations: true`, so every node carries an `l` field
20+
(`{f, b:{o,l,c}, e:{o,l,c}}`). The q2-preview iframe is `allow-same-origin`,
21+
so the parent can reach `contentDocument` directly and reuse `MorphIframe`'s
22+
exact scroll logic. **No Rust change, no new postMessage protocol.**
23+
24+
Scope (confirmed with user): **block-level scroll sync only.** No inline
25+
granularity, no selection mirroring.
26+
27+
## Design
28+
29+
- `data-loc` must land on each leaf's **own** semantic element (no wrapper):
30+
wrappers (even `display:contents` / `position:relative`) break theme CSS
31+
child/adjacency/`:nth-child` selectors and margin collapse — a parity risk
32+
this repo guards hard.
33+
- New helper `dataLocProps(node)` (framework) → `{ 'data-loc'?: string }`,
34+
spread onto each q2-preview block leaf's root element.
35+
- Extract `parseDataLoc` / `findElementForLine` / `isElementVisible` into a
36+
shared `iframe/scrollSyncDom.ts`; `MorphIframe` and `Q2PreviewIframe` both
37+
import them.
38+
- `Q2PreviewIframe` gains a `ref` handle (`scrollToLine`, `getScrollRatio`)
39+
+ `onScroll`/`onClick` props, using direct same-origin `contentDocument`
40+
access (mirrors `MorphIframe`).
41+
- `ReactRenderer` forwards the handle ref + callbacks to `Q2PreviewIframe`
42+
(q2-preview branch only).
43+
- `ReactPreview` wires `useScrollSync` exactly like `Preview.tsx`.
44+
45+
## Work Items
46+
47+
### Phase 1 — data-loc emission (TDD)
48+
- [x] Test: `dataLocProps` returns correct string for node with `l`, `{}` without
49+
- [x] Test: Para/Header/CodeBlock/Div with `l` render `data-loc`; node without `l` has none
50+
- [x] Implement `framework/sourceLoc.ts` + export
51+
- [x] Spread `dataLocProps(node)` into block leaves: Para, Header, CodeBlock
52+
(both paths), BulletList, OrderedList, BlockQuote, Div, HorizontalRule,
53+
RawBlock, Figure, LineBlock, DefinitionList, Table (skipped Plain — Fragment)
54+
55+
### Phase 2 — scroll plumbing (TDD where feasible)
56+
- [x] Test: `findElementForLine` / `parseDataLoc` in shared module (jsdom)
57+
- [x] Extract `iframe/scrollSyncDom.ts`; refactor `MorphIframe` to import
58+
- [x] `Q2PreviewIframe`: ref handle + scroll/click listeners (same-origin)
59+
- [x] `ReactRenderer`: thread ref + onScroll/onClick to Q2PreviewIframe
60+
- [x] `ReactPreview`: wire `useScrollSync`
61+
62+
### Phase 3 — verify
63+
- [x] `npm run build` (production tsc -b + vite) green; WASM untouched (TS-only)
64+
- [x] vitest suites green (preview-renderer 186+192; hub-client 556 unit + 66 integ)
65+
- [x] Added pampa regression test proving real parser output carries `l` on
66+
*block* nodes (`json_location_test::test_json_location_on_block_nodes`),
67+
and jsdom tests proving the real `<Ast>` render stamps `data-loc` from it.
68+
**NOT verified:** live browser scroll interaction (same-origin iframe DOM
69+
access + Monaco cursor events) — needs a running hub + browser session.
70+
- [ ] changelog.md entry (two-commit workflow) — pending commit
71+
- [ ] beads bd-9kzfi close + sync — pending commit

0 commit comments

Comments
 (0)