Files
ClaudeDo/docs/superpowers/plans/2026-06-19-rider-merge-editor.md

5.2 KiB
Raw Blame History

Plan: Rider-style 3-pane merge editor

Spec: docs/superpowers/specs/2026-06-19-rider-merge-editor-design.md

TDD, one focused commit per task (Conventional Commits, feat(merge): …). Build with -c Release per project (a running Worker locks Debug). Run ClaudeDo.Ui.Tests (and Localization.Tests for Task 6). No real claude CLI in tests. Stage ONLY the files each task touches, by explicit path (parallel sessions leave WIP). Backend + seam stay unchanged. Implementer/reviewer subagents use sonnet.

Task 1 — VM: active-file model + 3-pane reconstruction + readout

ConflictResolverViewModel / ConflictModels.cs, additive (seam untouched).

  • Add ActiveFile (MergeFile?), SelectFileCommand(MergeFile), default to first file after load. Keep Files, Current/CurrentIndex/Next/Previous (focused conflict for the header arrows), CanContinue, binary guard, planning routing — all unchanged.
  • Add computed, per ActiveFile:
    • ActiveOursText = concat(stable.Text | conflict.Ours)
    • ActiveTheirsText = concat(stable.Text | conflict.Theirs)
    • ActiveResultText = concat(stable.Text | conflict.Resolution ?? conflict.Ours)
    • ActiveConflicts = ordered descriptors (block + segment index) for the view.
  • PositionText"{conflicts} conflicts · {resolved} resolved" for the active file; keep CanContinue = every file resolved AND no binary.
  • Switching files raises a change event the view listens to (reuse/extend CurrentChanged → e.g. ActiveFileChanged).
  • Tests (Ui.Tests): reconstruction text for ours/theirs/result (result seeds unresolved with Ours); resolving a block updates ActiveResultText + readout; switching files preserves each block's Resolution; CanContinue blocks until all files resolved; binary file still blocks. Keep all existing tests green.

Task 2 — View: 3-pane AXAML shell + document assembly + synced scroll

Views/Conflicts/ConflictResolverView.axaml(.cs). Visual — verified by running.

  • Replace AXAML: ModalShell host kept; header row (◀/▶ focus arrows bound to Previous/Next, file switcher ItemsControl/ComboBox over Files bound to SelectFileCommand, right-aligned PositionText); Grid ColumnDefinitions="*,*,*" of three bordered panes with headers Ours · current (merge target) / Result / Theirs · incoming (task) (drop Base); footer Continue (IsEnabled=CanContinue) / Abort; binary banner (kept); Escape→Abort (kept).
  • Code-behind: build three TextDocuments from ActiveFile segments, recording each conflict's start line + line count per document; install TextMate per pane by file extension; rebuild on ActiveFileChanged; Ours/Theirs IsReadOnly=true.
  • Proportional synced vertical scroll across the three panes (re-entrancy guard).
  • Push Result edits back to the active block Resolution (refined in Task 4).

Task 3 — Result pane: read-only stable, editable conflicts

ConflictResolverView.axaml.cs + a small IReadOnlySectionProvider helper.

  • Track each conflict's result span in a TextSegmentCollection<…> over the Result document (anchors auto-adjust on edit).
  • IReadOnlySectionProvider: CanInsert only strictly inside a conflict span; GetDeletableSegments intersects with conflict spans only. Stable text becomes immutable; conflict regions stay editable.
  • Editing inside a conflict span writes the span text back to the block Resolution and flips it resolved (updates readout + CanContinue).

Task 4 — Color blocks (IBackgroundRenderer) + accept overlay

ConflictResolverView.axaml.cs + renderer/overlay helpers.

  • IBackgroundRenderer per pane: unresolved conflict = red (Blood tint), resolved = green/muted, Ours side = Moss tint, Theirs side = Accent tint — driven by recorded spans + block IsResolved.
  • Between-pane overlay Canvas (Ours|Result and Result|Theirs): accept-ours / accept-theirs + dismiss per conflict, positioned at the block's TextView visual top, recomputed on scroll/resize. Click → block.AcceptOurs/AcceptTheirs and replace the tracked Result span; resolved blocks recolor.

Task 5 — Polish: readout, focus arrows scroll-to-conflict, resolved styling

  • ◀/▶ arrows move Current and scroll all three panes to that conflict.
  • M conflicts · K resolved live readout; Continue tooltip/hint when blocked.
  • Resolved conflict recolors and drops its accept overlay; unresolved stays red. (Fold into Task 4 if small.)

Task 6 — Localization + tokens

  • Add conflictResolver.* keys (pane headers, readout, accept tooltips, hints) to locales/en.json AND locales/de.json (keep key parity).
  • Add Tokens.axaml color tokens only if a needed conflict/resolved shade is missing.
  • Run Localization.Tests (parity) + a quick scan for hard-coded strings in the view.

Task 7 — Verify

  • Build ClaudeDo.App + ClaudeDo.Ui -c Release; run Ui.Tests + Localization.Tests.
  • Update src/ClaudeDo.Ui/CLAUDE.md (Planning/Conflicts paragraph → new 3-pane editor).
  • Visual verification gap (flag to Mika): run the app, trigger a real conflict (single-task approve + planning unit-merge) and confirm panes/colors/accept/scroll/ gating/binary render correctly — cannot be asserted in tests.