Repository navigation
feat: batch review comments into one prompt for the session - #129
Merged
Merged
Conversation
A diff could hand the session a reference — a file, a line range — but never an instruction, so anything that actually had to change was typed out by hand while looking straight at it. Right-clicking a selection now also offers "Comment on lines", in both review surfaces: the Review dock's working diff and a pull request's Files changed. The note is held against those lines rather than written out. Comments collect at the foot of the panel in the order the diff was read, each removable on its own, and one Send hands the batch over as a single prompt. The batch goes in as a bracketed paste. Newlines are what make the block readable and also what a TUI reads as send — one per line — so the envelope xterm puts around text pasted by hand is what keeps the batch one paste, and unsent: Enter stays the user's. Verified against a real Claude Code, which takes it as "[Pasted text #1 +N lines]". Comments live in a module-level store keyed by what is under review — the checkout's path, or the pull request's URL — in memory only: an anchor is a line number of the diff as it was rendered, and outliving a restart would mean outliving every edit made in between. useInject now answers whether the write landed, so a Send with no session to take it says so and keeps the comments instead of dropping them. Refs #119 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The composer was a modal in the middle of the window: it covered the diff being read and put the note as far from the lines it was about as the screen allows. It is now a CodeMirror block widget that opens under the selection — the anchor as its label, the field, and the two keys that close it. Plain DOM rather than a React portal: the editor owns this node, and eq() keeps the widget alive across unrelated state changes, so what was typed survives. The root is contenteditable=false and every keydown stops there — the editor's content is contenteditable, and without that the note would run through CodeMirror's own input handling. useCodeMirrorView gains dispatchEffects, the seam an extension is driven through from outside, and useDiffEditor holds the submit handler in a ref so the extension set stays built once per document. Refs #119 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
omartelo
added a commit
that referenced
this pull request
Jul 30, 2026
#119 landed while this was open, and it had built the same thing twice over: a CodeMirror block widget that opens a gap between two lines of a diff, so a comment is written where it is aimed. Two implementations of one idea is one too many, so the merge keeps this branch's and moves #129's feature onto it. What was kept from each: - The gap is codemirror-threads.ts. It holds any number of slots per editor, keyed, and React fills each through a portal — which is what a thread needs, since it renders replies, a reply box and a resolve action. codemirror-comment.ts held exactly one composer per editor and built its DOM by hand; deleted. - The composer is now a slot like the rest, and the shared CommentBox renders it. It keeps #129's Enter-to-file, behind submitOnEnter: a note taken while reading is one line and reaching for a modifier is the slower half of writing it, while a review comment is prose that a stray Enter would file half of. - The batch bar, the review-comments store and the bracketed-paste compose are #129's, untouched. The two destinations now sit side by side in the diff's context menu, and this is where #119's open question gets its answer: they are named for where the note goes — "Comment for the session" and "Comment on the pull request" — because once a box is open the two look identical, and picking the wrong one is a note that lands where nobody looks for it. The dock's working diff has no pull request behind it and so offers only the first. Consequences worth naming: - The composer hangs off the *document* line the selection ended on rather than a resolved new-file line, which is #129's anchoring and the better one: a selection ending on a deleted line still has a place to open. reviewSlots no longer resolves it at all. - FileDiff's onComment became onSessionComment, and its review prop carries the GitHub half. ReviewPanel follows. - useCodeMirrorView exposes the live view; dispatchEffects went with the widget that needed it. Verified over CDP against a real pull request: the menu offers both items on one selection, the GitHub box files a pending comment (Submit review 2), and the session box files its note on a bare Enter into the batch bar. No new console errors.
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.
Closes #119.
A diff could hand the session a reference — a file, a line range — but never an instruction, so anything that actually had to change was typed out by hand while looking straight at it. This adds the missing half: read the diff, leave line comments, hand them over as one turn.
What it does
Right-clicking a selection now also offers Comment on lines 12-30…, in both review surfaces — the Review dock's working diff and a pull request's Files changed. A box opens between the lines themselves, carrying the anchor as its label;
⏎files the note,⇧⏎breaks a line,Escdiscards it.Comments collect at the foot of the panel in the order the diff was read, each removable on its own, and one Send hands the batch to the session as a single prompt:
Sending clears them.
Decisions
[Pasted text #1 +N lines].eq()keeps the widget alive across unrelated state changes so what was typed survives, and the root iscontenteditable=falsewith every keydown stopped there, or the note would run through CodeMirror's own input handling.path:12-30— the same form the line inject already writes. No hunk is pasted inline: the provider reads the file.useInjectnow answers whether the write landed, so a Send with no session to take it says so and keeps the comments instead of dropping them.Ceilings
Test plan
frontend/src/lib/review-comments.test.tscovers the store (per-review keying, blank text, removal, notification) and the composed payload (bullets, anchors, multi-line continuation, paste markers).biome check,tsc, 516 frontend tests,vite build,go test ./...(576),go vet,gofmt.⏎files it → bar shows the batch → Send → the session's PTY receives[Pasted text #1 +3 lines], unsent.Escdiscards without touching the batch.