fix(ui): preserve scroll position across comment add/edit/delete#1
Merged
Conversation
The review pane re-anchors the viewport on a surviving diff row whenever the file list changes (comment mutations and external `.dunk/comments.json` reloads), so inserting or removing inline comment cards no longer pushes the code under the user's eye out of frame. Reuses the existing layout/wrap anchor restoration scaffolding: a single useLayoutEffect now triggers on `files` identity change too, falls back to a survivable diff row when the natural inline-note anchor disappears, and clamps the previous scrollTop into the new content extent as a last resort. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These four tests asserted on the first frame captured after `settleDiffPane` (two renderOnce calls with a microtask between them). On Windows test runners the OpenTUI render+settle cycle can take more iterations than that budget, so the scroll reveal hadn't landed when the frame was captured, producing flaky `(fail)` runs that re-ran clean. The flake surfaced once the PR-only Windows workflow began running. Each test now polls via the existing `waitForFrame` helper until the scroll-settled predicate matches (8 attempts), keeping the original assertions intact and the passing-path cost unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Convert "keeps a selected wrapped hunk fully visible" to waitForFrame so the reveal poll absorbs Windows OpenTUI settle variance, matching the three sibling tests this branch already hardened. - Bump the default `jj` test timeout to 30s. Windows process spawning runs ~70× slower than Linux (5500ms vs 73ms in CI), pushing the invalid-revset test past Bun's 5s default. Skips the still-passing variants undisturbed. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Context
Adding, editing, or deleting a comment in the review pane currently lets the diff content shift under the user's eye — inserting an inline comment card above the viewport pushes code down by the card's height, and deleting one pulls everything up. Watcher-triggered reloads of
.dunk/comments.jsonhave the same problem.What was changed
useLayoutEffectinDiffPaneto also fire onfilesidentity changes (previousFilesRef.current !== files). One source of truth for viewport preservation across every relayout-causing input.inline-note:${annotation.id}, so an in-place comment edit still resolves cleanly and keeps the reader's position inside the card.DiffSectionRowBoundscarries akind("diff-row" | "inline-note") so anchor pickers can prefer survivable rows.findViewportRowAnchoraccepts{ preferDiffRows?: boolean };resolveViewportRowAnchorTopnow returnsnumber | nullso callers can apply a clamp fallback instead of scrolling to file body top.Validation
bun run typecheck,bun test(334 pass),bun run test:integration(21 pass — full existing PTY suite, including the layout/wrap scroll-preservation tests that exercise the same scaffolding this change extends),bun run lint,bun run format.viewportAnchor.test.tscovering:preferDiffRowsrow-picking, inline-note anchor surviving an in-place edit (with explicit height-bump assertion), inline-note anchor returningnullafter deletion, diff-row fallback resolving cleanly after deletion, and missing-file resolver behavior.Expert review
Design was validated before implementation via
expert-letterto Codex (.expert/letter-20260511T104143Z-*.md). The unified-effect approach,preferDiffRowsbias, nullable resolver contract, and clamp fallback are all from that response.Out of scope
VisibleAgentNoteid-format mapping fromDiffPane. The same string format is built in two places, but the existing site is unrelated to this change.ApptoDiffPane. Codex explicitly advised against App-level plumbing here; thefilesidentity trigger is the intended design and also benefits filter/reload paths.🤖 Generated with Claude Code