feat(merge): diff Merge opens the 3-pane editor + conflict overview ruler

- The Merge button in the Diff window now hands a conflicting merge to the in-app
  3-pane editor (MergeModal routes 'conflict' through RequestConflictResolution,
  the same seam Approve uses) instead of dead-ending on a conflict message.
- Add a conflict overview ruler right of the Result pane: a proportional map of
  every conflict in the file, recolored by resolved state, click a tick to jump —
  so conflicts are findable in long files without scrolling.
- New MergeResolvedEdgeBrush token + conflictMap en/de key. Ui 128 + Loc 16 green.
This commit is contained in:
Mika Kuns
2026-06-19 11:31:34 +02:00
parent ca4377e641
commit 29a294b7f3
9 changed files with 79 additions and 8 deletions

View File

@@ -28,10 +28,17 @@ public sealed partial class MergeModalViewModel : ViewModelBase
public Action? CloseAction { get; set; }
/// Set by the caller to hand a conflicting merge off to the in-app 3-pane editor
/// instead of dead-ending on the conflict message.
public Func<string, string, Task>? RequestConflictResolution { get; set; }
/// True once a merge has succeeded — lets the caller (e.g. the diff window)
/// close itself after this modal closes.
public bool Merged { get; private set; }
/// True once a conflict has been handed off to the resolver — also a cue to close the diff window.
public bool RoutedToResolver { get; private set; }
public MergeModalViewModel(IWorkerClient worker)
{
_worker = worker;
@@ -96,9 +103,21 @@ public sealed partial class MergeModalViewModel : ViewModelBase
});
break;
case "conflict":
HasConflict = true;
ConflictFiles = result.ConflictFiles;
ErrorMessage = Loc.T("vm.merge.conflict");
// Hand off to the in-app 3-pane merge editor when wired (MergeTask aborted
// cleanly, so the resolver re-starts the merge leaving conflicts in the tree).
if (RequestConflictResolution is not null)
{
var branch = SelectedBranch!;
RoutedToResolver = true;
CloseAction?.Invoke();
await RequestConflictResolution(TaskId, branch);
}
else
{
HasConflict = true;
ConflictFiles = result.ConflictFiles;
ErrorMessage = Loc.T("vm.merge.conflict");
}
break;
case "blocked":
ErrorMessage = Loc.T("vm.merge.blocked", result.ErrorMessage ?? "");