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

93 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `TextDocument`s 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.