Repository navigation
feat: write a review comment against a line from the file view - #30
JacobAtchley wants to merge 2 commits into
Conversation
The file view knows which line the cursor is on and the pull request pane knows how to show a discussion; nothing could start one. Leaving a review comment meant the web UI. c opens a prompt against the cursor's line and posts a thread anchored there. The comment appears under the line as soon as the server takes it, added to what the view already holds rather than refetched: the thread is known, and waiting for a round trip to see your own comment reads as the comment having failed. A line the pull request removed is not in the new file, so there is nothing for the server to anchor to. Refused before the request with the reason, the precedent armDraft sets — a check that only happens at the network call hides why behind an error code. CreateThread sends the position as a start and an end, both at column one of the line, which is how Azure DevOps spells a whole-line comment. Whether it accepts that exact shape is the one thing here not seen against a live tenant, so it goes in the README's list — and it is worth a single comment on a throwaway pull request before relying on it, since a rejection loses something you typed. Parsing a thread payload moves into threadJSON.thread(). Two endpoints now answer with that shape — listing a discussion and creating one thread — and a second copy of the parsing is a second place for it to drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KT78DJemENKXRQsfKzZPmn
Smoke-testing inline comments against a real pull request turned up a CRLF README, and every composed line carried a trailing \r. The carriage return is not part of the line. A terminal reading one returns the cursor to column zero, so the padding the viewport writes after the text would overwrite the text. Bubbles strips control characters before it paints, which is why this was invisible rather than broken — but the pane's correctness should not rest on a detail of a component underneath it, and fileLine.Text should hold the line rather than the line plus a control character. Dropped where the lines are made, so both the file's own lines and the removed ones recovered from the hunks are clean. Also verified in the same run: the threadContext boardwalk sends is accepted. A comment posted against new-side line 6 of a real pull request came back anchored to line 6 on the right side, and read back the same from the threads endpoint. That entry leaves the README's unverified list, and the note recommending a smoke test before relying on c goes with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KT78DJemENKXRQsfKzZPmn
Smoke-tested against the sandbox — it worksRan the real code path ( The And it found a bugThat README is CRLF, so every composed line carried a trailing A terminal reading a carriage return returns the cursor to column zero — so the padding the viewport writes after the text would overwrite the text. Bubbles strips control characters before painting, which made this invisible rather than broken, but the pane's correctness should not rest on a detail of a component underneath it, and Dropped where the lines are made, so both the file's own lines and the removed ones recovered from the hunks are clean. Pushed as a second commit here with a test. Two smoke-test comments (#90223, #90224) are on 11917 and can be deleted. |
Stacked on #29 — base is
feat/file-view, so the diff here is only the comment feature. Merge #29 first and this retargets tomainon its own.The file view knows which line the cursor is on, and the pull request pane knows how to show a discussion. Nothing could start one — leaving a review comment meant the web UI.
What it does
copens a prompt against the cursor's line and posts a thread anchored there. The comment appears under the line as soon as the server takes it: added to what the view already holds rather than refetched, because the thread is known and waiting for a round trip to see your own comment reads as the comment having failed.The prompt owns every key while open, so
dtyped into it is a letter rather than the diff toggle — the same discipline as the branch and reply prompts.Refusals
A line the pull request removed is not in the new file, so there is nothing for the server to anchor to. Refused before the request with the reason, the precedent
armDraftsets: a check that only happens at the network call hides why behind an error code.An empty prompt closed with enter is a mistake, not a request to post nothing.
The one unverified thing
CreateThreadsends the position as a start and an end, both at column one of the commented line, which is how Azure DevOps spells a whole-line comment. Whether it accepts that exact shape is from the reference, not from a live tenant — and unlike the other unverified assertions, a rejection here loses something you typed.It goes in the README's list with an explicit recommendation: write one comment on a throwaway pull request before relying on
c. I did not want to post a real comment to a real pull request to find out, but say the word and I'll smoke-test one.Refactor included
Parsing a thread payload moves into
threadJSON.thread(). Two endpoints now answer with that shape — listing a discussion and creating one thread — and a second copy of the parsing is a second place for it to drift.Threads()uses it too, so the existing tests cover the extraction.Tests
Client: the request method, path and body, including that the context carries
filePath, both positions and an offset; the created thread coming back parsed with its line and side; a rejection carrying the server's own message.View: the prompt opens on the cursor's line and names it; enter posts; an empty comment does not; esc cancels;
dis swallowed as a letter; a removed line is refused with the reason and reported as a failure; a posted comment appears without a refetch; a failure lands on the status line; a thread for another file is ignored, since Root broadcasts to every view in the stack.Full suite and
go vetpass.🤖 Generated with Claude Code
https://claude.ai/code/session_01KT78DJemENKXRQsfKzZPmn