From 2ef4f81e393f64fbdb0b424562309e6b4a682014 Mon Sep 17 00:00:00 2001 From: JacobPoteet Date: Mon, 24 Aug 2026 15:17:41 -0400 Subject: [PATCH] Let the toolbar commit pending marks, not just discard them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marks are made in the list with K/X/U as often as in the deck, but the only thing the main window could do with them was throw them away: the pending-marks pill carried a Discard link and nothing else. Committing meant clicking Triage, landing on a card deck you didn't want (Open starts at the first *unmarked* file), then Review, then Commit — three clicks and a mode switch to reach the productive action, one to destroy the work. The cause was ownership: Commit_Click lived on TriageView, a control that has nothing to do with committing. CommitDialog was already independent of the deck — it takes a per-folder summary and a totals callback and knows nothing else — so only the call site was trapped. TriageCommitFlow now owns the dialog, the issue #1 preview-handle release and the commit itself; the deck and the toolbar both go through it, so neither can drift from the other or forget the handle release before files start moving. The pill becomes the staging control it was already halfway to being: the keep/reject split, Review (which opens the overlay straight onto the piles, skipping the deck), a primary Commit, and Discard demoted into the overflow — destructive actions shouldn't outrank productive ones. Ctrl+Enter commits from anywhere in the window rather than FileList_KeyDown, so it still works from the filter box, and it gets a chip in the status-bar hint row next to K / X and U. Also drops the trailing ellipsis from the commit labels. "Commit…" is the Windows convention for "opens a dialog", but at 12px it reads as truncated text, which is the opposite of reassuring on the button that touches the disk. MainViewModel.TotalsFor replaces the InScope filtering the commit dialog's caller was doing over the piles, which duplicated TriageSession.Pending. Co-Authored-By: Claude Opus 5 --- src/ExplorerHelper/Controls/TriageView.xaml | 2 +- .../Controls/TriageView.xaml.cs | 51 ++++------------- src/ExplorerHelper/MainWindow.xaml | 55 +++++++++++++++--- src/ExplorerHelper/MainWindow.xaml.cs | 34 +++++++++++ src/ExplorerHelper/TriageCommitFlow.cs | 57 +++++++++++++++++++ .../ViewModels/MainViewModel.cs | 23 +++++++- 6 files changed, 171 insertions(+), 51 deletions(-) create mode 100644 src/ExplorerHelper/TriageCommitFlow.cs diff --git a/src/ExplorerHelper/Controls/TriageView.xaml b/src/ExplorerHelper/Controls/TriageView.xaml index e52d63d..7bc36bf 100644 --- a/src/ExplorerHelper/Controls/TriageView.xaml +++ b/src/ExplorerHelper/Controls/TriageView.xaml @@ -322,7 +322,7 @@ + Content="Commit" Click="Commit_Click" /> diff --git a/src/ExplorerHelper/Controls/TriageView.xaml.cs b/src/ExplorerHelper/Controls/TriageView.xaml.cs index 319f149..728563e 100644 --- a/src/ExplorerHelper/Controls/TriageView.xaml.cs +++ b/src/ExplorerHelper/Controls/TriageView.xaml.cs @@ -43,7 +43,7 @@ public TriageView() /// (filtered, sorted) file list — folders never get cards. Starts at the first unmarked /// file so a resumed session picks up where it left off. /// - public void Open(MainViewModel vm) + public void Open(MainViewModel vm, bool startInReview = false) { _vm = vm; _deck = vm.Files.Where(f => !f.IsDirectory).ToList(); @@ -51,7 +51,12 @@ public void Open(MainViewModel vm) _index = _deck.FindIndex(f => f.Flag == TriageFlag.None); Visibility = Visibility.Visible; - ShowDeckState(); + // Marks made in the list have no deck behind them, so the toolbar opens straight onto the + // piles; "Back to deck" still works because the deck was snapshotted above either way. + if (startInReview) + ShowReviewState(); + else + ShowDeckState(); Focus(); } @@ -339,7 +344,7 @@ private void RefreshReviewState() UnmarkedStrip.Visibility = _vm.UnmarkedFileCount > 0 ? Visibility.Visible : Visibility.Collapsed; UnmarkedText.Text = $"{_vm.UnmarkedFileCount} file(s) not decided yet — they stay untouched unless you keep going."; CommitButton.IsEnabled = _vm.KeepCount + _vm.RejectCount > 0; - CommitButton.Content = $"Commit (✗ {_vm.RejectCount} reject · ✓ {_vm.KeepCount} keep)…"; + CommitButton.Content = $"Commit (✗ {_vm.RejectCount} reject · ✓ {_vm.KeepCount} keep)"; } private static FileEntry? EntryOf(object sender) => @@ -373,42 +378,8 @@ private void PileItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) private void Commit_Click(object sender, RoutedEventArgs e) { - // Marks can span folders now (issue #43), so the dialog gets the per-folder breakdown and - // a way to recompute its totals when the user narrows the scope. - var dialog = new CommitDialog( - _vm.SummarizeMarksByFolder(), - _vm.FolderPath, - onlyHere => - { - var rejects = _vm.RejectPile.Where(f => InScope(f, onlyHere)).ToList(); - var keeps = _vm.KeepPile.Where(f => InScope(f, onlyHere)).ToList(); - return (rejects.Count, rejects.Sum(f => f.SizeBytes), - keeps.Count, keeps.Sum(f => f.SizeBytes)); - }) - { - Owner = Window.GetWindow(this), - }; - if (dialog.ShowDialog() != true) - return; - var destination = dialog.KeepDestination; - var copyKeepers = dialog.CopyKeepers; - var deleteRejects = dialog.DeleteRejects; - var currentFolderOnly = dialog.CurrentFolderOnly; - - bool InScope(Models.FileEntry entry, bool onlyHere) => - !onlyHere || string.Equals( - Services.TriageSession.FolderOf(entry), _vm.FolderPath, StringComparison.OrdinalIgnoreCase); - - // Release every preview handle, then let the dispatcher pump the media teardown - // before files start moving — same discipline as delete/rename (issue #1). - CardPreview.Clear(); - Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() => - { - var error = _vm.CommitTriage(destination, copyKeepers, deleteRejects, currentFolderOnly); - if (error is not null) - MessageBox.Show(Window.GetWindow(this)!, error, "Commit finished with errors", - MessageBoxButton.OK, MessageBoxImage.Warning); - Close(); - })); + // The dialog, the handle release and the commit itself are shared with the toolbar's + // pending-marks pill; the overlay only adds closing itself afterwards. + TriageCommitFlow.Run(_vm, Window.GetWindow(this)!, CardPreview.Clear, Close); } } diff --git a/src/ExplorerHelper/MainWindow.xaml b/src/ExplorerHelper/MainWindow.xaml index 0709fc3..b4992cc 100644 --- a/src/ExplorerHelper/MainWindow.xaml +++ b/src/ExplorerHelper/MainWindow.xaml @@ -152,8 +152,10 @@ Visibility="{Binding IsUpdateAvailable, Converter={StaticResource BoolToVisibility}}" ToolTip="A new version is ready — click to update; the app restarts on this folder" /> - + - -