Repository navigation
feat(markdown): split scroll sync and review comments on the preview - #394
Merged
Merged
Conversation
Two additions to the markdown source / preview shell. Split-view scroll sync: - every rendered block is stamped with the source lines it came from (`data-src-line` / `data-src-end` from a markdown-it core rule, on the `<pre>` for fences), which gives (line, y) anchors between the editor and the preview - either pane drives the other, interpolating between anchors so tall images and long paragraphs scroll smoothly instead of jumping - anchors are cached until a re-render or a ResizeObserver hit (late images, mermaid) - the echo of a programmatic `scrollTop` write is recognised by the value it lands on, so the panes never feed each other; the pane already on screen leads when split opens, and the first sync waits for `attachHiddenScrollRestore` so a restored position is not undone Review comments on rendered text: - selecting anything in the preview raises a "Comment" pill that opens the same composer as the code side (send now, add to pending, edit, delete) - the comment goes into the same `reviewComments` store as an editor comment, so it also shows beside its source in the editor, follows its code through edits, and ships with the pending batch - the quote is exactly the selection; the line range starts at the innermost block at each end and is narrowed to the source lines the selection spans, matching with whitespace runs elastic since most markdown is hard-wrapped and the rendering joins those lines; when the selection is not in the source as typed (inline markdown), the locator stays the block's lines - in the preview the quoted text itself is highlighted with a `<mark>` (not the CSS Custom Highlight API, which mispaints in WKWebView), the open composer's included; the whole-block stripe is only the fallback when the text cannot be found in the rendering - the composer and card moved out of `reviewCommentsExt` into `commentBoxes.ts` (framework-free DOM, `.tc-comment-*` rules now in index.css) so both surfaces render the same boxes - in the preview they live in slot divs kept after the top-level block the range ends on, surviving innerHTML rebuilds; find, copy and selection skip them, and copy unwraps the quote highlight - documents cannot forge the source-line, slot or highlight markers: our line stamps carry a per-load nonce and `claimDocument()` strips the rest, for the render and each mermaid SVG - task files only; whole-file comments stay in the editor Fixes the comment textarea growing 2px short of its text (border-box height set from `scrollHeight`, which excludes the borders). It overflowed into scrollbars, visible when zoomed, in the editor and diff composers too. It now scrolls only at its height cap. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
…er shows On the macos-14 runner the first programmatic selection after the scroll-sync cases never raised the pill, and the five cases after it cascaded off that. It does not reproduce locally. Re-select while waiting, and on timeout report the selection, active element and window focus so a persistent failure names its cause. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
simion
approved these changes
Oct 12, 2026
simion
left a comment
Owner
There was a problem hiding this comment.
Approved. Nice work on the scroll sync and preview comments, thanks.
simion
added a commit
that referenced
this pull request
Oct 12, 2026
Six preview comment cases failed in a full run and passed alone. The split opens with the editor focused, and a focused CodeMirror writes its caret back into the document whenever its viewport redraws, which collapses a selection made in the preview. The scroll-sync cases leave that viewport settling, so the Comment pill came up and was gone before the next command. A real drag starts with a mousedown that takes focus out of the editor, so the app is fine; the spec was skipping that half of the gesture. selectInParagraph blurs first, and the re-select retry loop goes. Measured both ways and written up in gotchas.md. Refs #394 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Two additions to the markdown source / preview shell.
Split-view scroll sync:
data-src-line/data-src-endfrom a markdown-it core rule, on the<pre>for fences), which gives (line, y) anchors between the editor and the previewscrollTopwrite is recognised by the value it lands on, so the panes never feed each other; the pane already on screen leads when split opens, and the first sync waits forattachHiddenScrollRestoreso a restored position is not undoneReview comments on rendered text:
reviewCommentsstore as an editor comment, so it also shows beside its source in the editor, follows its code through edits, and ships with the pending batch<mark>(not the CSS Custom Highlight API, which mispaints in WKWebView), the open composer's included; the whole-block stripe is only the fallback when the text cannot be found in the renderingreviewCommentsExtintocommentBoxes.ts(framework-free DOM,.tc-comment-*rules now in index.css) so both surfaces render the same boxesclaimDocument()strips the rest, for the render and each mermaid SVGFixes the comment textarea growing 2px short of its text (border-box height set from
scrollHeight, which excludes the borders). It overflowed into scrollbars, visible when zoomed, in the editor and diff composers too. It now scrolls only at its height cap.Screenshots
Selecting text shows a new "comment" button:
Clicking "comment" has the familiar agent comment box:
Not great with a screenshot, but here is the split pane scrolled in the preview, which synced the scroll in the editor: