Contribute rewrite Phase 4: §8.10 gutter accent in CM6 raw pane

Restores the §8.10 paragraph-margin marker layer Phase 1 dropped when
Tiptap left, this time against the CM6 raw pane on the left half of
the Contribute split. The matching inline tracked-insert/tracked-
delete overlay Phase 3 shipped lives on the preview pane to the
right; the two visual layers answer different questions on the two
halves of the split — "did anything change in this region?" (gutter,
amber, scannable) vs. "what changed here?" (inline, green/red,
precise) — and are deliberately separate signals.

New file: frontend/src/components/diffGutterExtension.js. The
extension exposes a `setBaseline` StateEffect and a gutter that marks
every line whose text differs from the same-indexed line of the
baseline (the last server-confirmed branch body). Per-line, not
paragraph-block — CM6's natural unit; collapsing to paragraph ranges
is more spec-faithful but adds code, and the per-line stance is the
pre-fancy default. A TODO is left for a future paragraph-collapse
pass if the result reads noisy.

MarkdownSourceEditor.jsx changes:
- Install the gutter extension in the editor's extension list.
- Seed the baseline to `initialDoc` at construct time.
- In the existing `initialDoc`-watching effect, dispatch the doc
  replacement AND a `setBaseline` effect in the SAME transaction so
  there's no one-frame window where the new doc reads as "divergent"
  against the old baseline. This carries through every server-
  confirmed branch refresh that RFCView already wires (accept,
  decline, manual flush, branch switch); no RFCView changes needed
  because all four paths already re-push `initialDoc` after pulling
  fresh state.

Design calls per the Phase 4 prompt's open list:
  • Per-line marks, not paragraph-block ranges. Pre-fancy stance.
  • Amber (#f59e0b) thin 3px vertical bar, distinct from the
    preview pane's green-on-light / red-on-light tracked-change
    inline overlay. Reads as "in-flight / not yet on the server."
  • Baseline reset on every branchView refresh (accept / decline /
    manual flush / branch switch), matching RFCView's existing
    originalSourceLinesRef discipline. Gutter then reads as "what's
    in the buffer but not on the server."
  • No hover / no click. The inline overlay already carries the
    click-to-card binding; the gutter is scannable only.

Known caveats kept deliberately:
  • Insert/delete shifts. Adding a line in the middle shifts every
    subsequent line's index and the naive compare lights up
    everything below — tolerated as the honest "you've touched
    stuff below this point" cue. A future LCS-anchored pass could
    fix it; Phase 4 doesn't need to.

SPEC §8.10:
- First paragraph rewritten to name the CM6 raw pane as the gutter's
  home and replace the stale "ProseMirror plugin" wording with the
  CodeMirror gutter framing. Mirrors how Phase 3 named the preview
  pane as the inline overlay's home.
- DiffView retirement paragraph adjusted: gutter and inline overlay
  together (across the split) cover its review affordance, not
  "both layers in the same surface" — the layers are deliberately on
  different surfaces.

Verification:
- Vite preview sandbox eval — standalone CM6 mount, dispatch tests
  across construct (no marks on identity), per-line edit (mark on
  exactly the touched line, confirmed by Y-coordinate matching the
  line-number gutter element), baseline reset clears, atomic
  doc+baseline dispatch leaves zero marks (the RFCView accept-flow
  path), insert-in-middle exhibits the expected cascade, and
  computed-style proof for the amber bar (`#f59e0b`, 3px wide,
  positioned left of the line-numbers gutter at x=21 vs x=24).
- Screenshot captures the bar on the touched line only.
- 125 backend integration tests still green.
- Live RFCView accept → branch refresh → gutter clear flow against a
  real branch was not driven (backend not running this session,
  carrying forward Phase 2/3's verification gap). The sandbox-level
  proof covers the atomic dispatch correctness; the next session
  with a backend up should drive that golden path before piling
  Phase 5 work on top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-25 11:00:45 -07:00
parent 2a7c099a33
commit 886bbf5512
4 changed files with 139 additions and 12 deletions
+13 -12
View File
@@ -829,18 +829,20 @@ declined predecessor still visible.
### 8.10 Tracked-change markup and the review-mode toggle
Two visual layers carry change information in the editor. The first
is a paragraph-margin marker — a thin gutter accent on any paragraph
that differs from the branch's open-session baseline, rendered by a
ProseMirror plugin against a baseline snapshot taken when the editor
opens. The second is inline `tracked-delete` / `tracked-insert`
markup at the exact range of an accepted change, with the deleted
text shown struck-through and the inserted text shown in an additive
Two visual layers carry change information in the Contribute split.
The first is a paragraph-margin marker on the left raw-source pane —
a thin gutter accent on any line that differs from the branch's
open-session baseline, rendered by a CodeMirror gutter extension
against a baseline reset on every server-confirmed branch refresh
(accept, decline, manual flush, branch switch). The second is inline
`tracked-delete` / `tracked-insert` markup on the right preview pane
at the exact range of an accepted change, with the deleted text
shown struck-through and the inserted text shown in an additive
style; both carry the change's ID via a data attribute, enabling the
click-to-card binding from §8.8. The margin marker is scannable
("did anything change in this region?"); the inline markup is
precise ("what changed here?"). The two answer different questions
and both are kept.
on the two halves of the split and both are kept.
The inline markup lives in the rendered preview surface, not on the
writable editor. With the Contribute-mode split (§8.3) the raw markdown
@@ -865,10 +867,9 @@ selection-quote that drove the change, and the AI's `reason`.
DiffView is the legacy read-only render surface invoked via the
Contribute-mode toolbar toggle (§8.15) — a full-editor swap-in that
reads the same `changes` table. It is retained as an interim path while
the preview-pane overlay matures and is slated for retirement once
gutter markers (the §8.10 paragraph-margin layer) land in the preview
itself; at that point both visual layers live in the same surface and
the toolbar toggle collapses.
the split-pane layers mature, and is slated for retirement once
the raw-pane gutter and the preview-pane inline overlay together cover
its review affordance; at that point the toolbar toggle collapses.
### 8.11 Manual edits and collisions with AI proposals