Skip to content

Diff viewer: copyable line-anchor links #126

Description

@gregoryfoster

Background

Split out from the descoped #115 Phase D. Phase A + B.1 of the diff viewer have shipped; this is the remaining "polish" item that's still relevant.

Problem

When a Watcher user wants to share a specific change with someone (a colleague, a stakeholder, a comment in another tool), they currently have to send the whole /changes/{id} URL — there's no way to point at a specific line within the diff. For long diffs that means the recipient has to scroll-hunt for what's being referenced.

Goal

Each line in the diff (and ranges) gets a stable, copyable URL fragment, e.g.:

  • https://watcher.exe.xyz/changes/{id}#L42 — single line
  • https://watcher.exe.xyz/changes/{id}#L42-L57 — range

When the URL with a fragment is loaded, the page should:

  • Auto-scroll to the referenced line(s)
  • Visually highlight the referenced span (subtle background tint)

Approach sketch

diff2html-ui renders each line as .d2h-code-line / .d2h-code-side-line with an associated line number cell. Two pieces:

  1. Anchor IDs at render time (src/dashboard/static/js/diff-viewer.js): after Diff2HtmlUI.draw(), walk the rendered DOM and add id="L{n}" to each line number cell. Add a small "copy link" affordance on hover (chip near the line number that copies ${location.pathname}${location.search}#L{n} to clipboard).
  2. Range selection — shift-click a second line number to extend a selection; #L{a}-L{b} form. Standard pattern from GitHub.
  3. Fragment handling on load — read location.hash, scroll the line into view, apply a .d2h-line-anchor-target class for highlight.

Acceptance

  • Each diff line has a stable id="L{n}" after Diff2HtmlUI.draw()
  • Visiting /changes/{id}#L42 scrolls to line 42 and visually highlights it
  • Visiting /changes/{id}#L42-L57 highlights the range
  • A copy-link affordance is reachable via keyboard (a11y)
  • HTMX mode swaps (Extracted ↔ Raw) re-attach IDs after re-render
  • Theme toggle preserves anchor highlight
  • Works in both outputFormat: "side-by-side" and "line-by-line"

Out of scope

  • Cross-mode anchor preservation (line 42 in Extracted ≠ line 42 in Raw — that's a different feature)
  • Persisting selections in localStorage

Dependencies

None — purely client-side enhancement on top of existing diff2html-ui mount.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions