Line-number anchor deeplinks#31
Conversation
Click any line number to highlight it and write a GitHub-style anchor into the URL (#<file>R<n> for the additions side, L<n> for deletions); opening or pasting such a URL re-selects and scrolls to that line. The highlight uses the @pierre/diffs native `selectedLines` prop, so the library owns the tagging and re-applies it across its virtualized re-renders -- no manual shadow-DOM bookkeeping. Because the library reserves `data-selected-line` for that selection (its renderer clears all of them before repainting), the keyboard cursor moves to its own `data-cursor-line` attribute so the two highlights never clobber each other. The selection's color vars are registered non-inheriting and can't be reached from the host, so the amber re-theme is a scoped stylesheet injected into each diff's shadow root. URL <-> selection routing (buildAnchor/parseAnchor, hashchange) and scroll-to-line stay app-side: the library exposes no reveal API for an off-screen virtualized row, so scrolling still locates the row in the shadow DOM once it mounts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thank you! I am in the process of trying to switch over to the new CodeView thing they discussed in https://pierre.computer/writing/on-rendering-diffs, but I ran into trouble keeping the keyboard shortcuts working, so I'm not sure if I'll be able to finish hat. If I do, this would probably have to be rethought. I'll have to decide which direction. In the meantime, anyone can enjoy this feature by running your fork locally. I like the idea overall, and it's not costly to have around even if you're not using it, but I'm curious to hear more how you use it. Since the tool is meant to be used locally, you're the only one generating these links and linking yourself to them. In order for the links to work, you have to be running the server on the same revset/commit range as when you generated the link, right? It makes me think that the commit range shouldn't have to be baked into the server run command — what if you could specify it in the query string? That way you could have a single server running, and the links to a particular line could build in the commit range that makes the fragment links work. I'm not suggesting you add this functionality in this PR, just thinking out loud about future directions. |
|
All of that makes sense. I'm using it to help me with local agent-driven code reviews before pushing up to github - the basic flow is "explain your changes with links to the relevant lines in sk". It's pretty helpful, though switching between tabs is kind of a pain so I'm considering also adding something to show the markdown explanation in the same view, with the diff alonside. |
|
Oh! That makes so much sense. There are two local users, you and the agent. I do this with Github permalinks all the time. |
Line-number anchor deeplinks
Adds GitHub-style line deeplinks to the diff viewer. Click any line number to highlight that line and write an anchor into the URL; open or paste such a URL to re-select and scroll to the line. Works on both sides of the diff, in split and unified views.
(screenshots are from a diff of this PR 😄)
What you get
#<file>R<n>anchor (R= additions/right side,L= deletions/left). Click the same line again to clear it.#src/App.tsxR76), not a hash — slashes stay literal; only genuinely unsafe characters are percent-encoded.How it works
The highlight uses
@pierre/diffs' nativeselectedLinesprop rather than hand-rolled DOM bookkeeping. The library owns the tagging and re-applies it across its virtualized re-renders, so scrolling a long diff never drops the highlight.The one wrinkle is that the library reserves the

data-selected-lineattribute for that selection — its renderer clears every one of them before repainting — and the existing keyboard (j/k) cursor was painting onto the same attribute. The cursor now lives on its owndata-cursor-lineattribute, so the two highlights coexist instead of clobbering each other: the anchor reads as amber, the cursor as a neutral gray.The selection's color is themed amber via a small stylesheet injected into each diff's shadow root. This is deliberate: the library's selection-color CSS variables are registered non-inheriting, so they can't be overridden from the host element the way
--diffs-modified-color-overridecan — a scoped style is the reliable hook.URL routing (
buildAnchor/parseAnchor, thehashchangelistener) and scroll-to-line stay app-side. The library exposes no "reveal line N" API for an off-screen virtualized row, so on deeplink load we scroll the file into view and then poll for the row in the shadow DOM once it mounts. This is the exact two-phase workaround described in pierrecomputer/pierre#452 (request for a nativeVirtualizer.scrollToLine()); if that lands,scrollToAnchorcan be simplified to a single call.Verification
Driven through a real browser against a live diff:
#…R<n>set, native selection painted, legacy hand-rolled attribute absentj/kcursor and anchor on different lines → neither clobbers the othernpm run tsc,npm run lint,npm test, andnpm run buildare all green.Notes for review
dist/is included, consistent with how this repo tracks build artifacts.MutationObserver; adopting the nativeselectedLinesdeleted that machinery and inherited virtualization handling for free.