feat(merge): unify planning conflicts onto the resolver + 3-pane VM foundation
Route planning unit-merge conflicts through ConflictResolverViewModel (OpenForPlanningAsync) and delete the old ConflictResolutionViewModel dialog. Add active-file 3-pane reconstruction (MergeFile OursText/TheirsText/ResultText, ActiveFile, SelectFileCommand, active-file readout) as the VM foundation for the Rider-style editor. Seam preserved; Ui.Tests 128/128.
This commit is contained in:
@@ -41,9 +41,6 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
|
||||
|
||||
public Func<MergeModalViewModel> ResolveMergeVm => _mergeVmFactory;
|
||||
|
||||
// Set by MainWindow to open the conflict resolution dialog.
|
||||
public Func<ConflictResolutionViewModel, Task>? ShowConflictDialog { get; set; }
|
||||
|
||||
// Layer C seam: composition root sets the factory; MainWindow sets the dialog opener.
|
||||
// The integrator connects Layer A/B's RequestConflictResolution(taskId, target) to this method.
|
||||
public Func<string, ClaudeDo.Ui.ViewModels.Conflicts.ConflictResolverViewModel>? ConflictResolverFactory { get; set; }
|
||||
@@ -146,44 +143,17 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
|
||||
private void OnPlanningMergeConflict(string planningTaskId, string subtaskId, IReadOnlyList<string> conflictedFiles)
|
||||
{
|
||||
// Already on UI thread (WorkerClient dispatches via Dispatcher.UIThread.Post).
|
||||
_ = OpenConflictDialogAsync(planningTaskId, subtaskId, conflictedFiles);
|
||||
// A unit-merge conflict resolves in the same in-app 3-way editor as a single-task merge.
|
||||
_ = OpenPlanningConflictAsync(planningTaskId, subtaskId);
|
||||
}
|
||||
|
||||
private async Task OpenConflictDialogAsync(string planningTaskId, string subtaskId, IReadOnlyList<string> conflictedFiles)
|
||||
private async Task OpenPlanningConflictAsync(string planningTaskId, string subtaskId)
|
||||
{
|
||||
if (ShowConflictDialog == null || _dbFactory == null) return;
|
||||
|
||||
string subtaskTitle = subtaskId;
|
||||
// The conflict lives in the list's working dir (the repo being merged into),
|
||||
// not the subtask worktree. VS Code must open this folder to show the merge UI.
|
||||
string repoDirectory = System.Environment.CurrentDirectory;
|
||||
string targetBranch = Worker?.LastApproveTarget ?? "main";
|
||||
|
||||
try
|
||||
{
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await ctx.Tasks
|
||||
.Include(t => t.List)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(t => t.Id == subtaskId);
|
||||
if (entity != null)
|
||||
{
|
||||
subtaskTitle = entity.Title;
|
||||
if (entity.List?.WorkingDir is { } dir && !string.IsNullOrWhiteSpace(dir))
|
||||
repoDirectory = dir;
|
||||
}
|
||||
}
|
||||
catch { /* Non-fatal: fall back to subtaskId and cwd */ }
|
||||
|
||||
var vm = new ConflictResolutionViewModel(
|
||||
Worker!,
|
||||
planningTaskId,
|
||||
subtaskTitle,
|
||||
targetBranch,
|
||||
conflictedFiles,
|
||||
repoDirectory);
|
||||
|
||||
await ShowConflictDialog(vm);
|
||||
if (ConflictResolverFactory is null || ShowConflictResolver is null) return;
|
||||
var vm = ConflictResolverFactory(subtaskId);
|
||||
var hasConflicts = await vm.OpenForPlanningAsync(planningTaskId, subtaskId);
|
||||
if (hasConflicts)
|
||||
await ShowConflictResolver(vm);
|
||||
}
|
||||
|
||||
// For tests only — does NOT wire up events.
|
||||
|
||||
Reference in New Issue
Block a user