@@ -394,7 +394,9 @@ namespace winrt::TerminalApp::implementation
394394 // - Removes the tab (both TerminalControl and XAML) after prompting for approval
395395 // Arguments:
396396 // - tab: the tab to remove
397- winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested (winrt::TerminalApp::Tab tab)
397+ // - skipConfirmClose: if true, skip the confirmOnClose check. Used when
398+ // an aggregate confirmation has already been shown (i.e. close other tabs)
399+ winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested (winrt::TerminalApp::Tab tab, bool skipConfirmClose)
398400 {
399401 winrt::com_ptr<TerminalPage> strong;
400402
@@ -413,6 +415,24 @@ namespace winrt::TerminalApp::implementation
413415 }
414416 }
415417
418+ // Skip the per-tab confirmOnClose check when the caller has already
419+ // shown an aggregate confirmation dialog (e.g. _RemoveTabs).
420+ if (!skipConfirmClose)
421+ {
422+ const auto tabImpl = _GetTabImpl (tab);
423+ if (tabImpl && _ShouldWarnOnCloseTab (tabImpl))
424+ {
425+ const auto weak = get_weak ();
426+
427+ auto warningResult = co_await _ShowConfirmCloseDialog (ConfirmCloseDialogKind::Tab);
428+ strong = weak.get ();
429+ if (!strong || warningResult != ContentDialogResult::Primary)
430+ {
431+ co_return ;
432+ }
433+ }
434+ }
435+
416436 auto t = winrt::get_self<implementation::Tab>(tab);
417437 auto actions = t->BuildStartupActions (BuildStartupKind::None);
418438 _AddPreviouslyClosedPaneOrTab (std::move (actions));
@@ -782,6 +802,22 @@ namespace winrt::TerminalApp::implementation
782802 if (const auto pane{ activeTab->GetActivePane () })
783803 {
784804 const auto weak = get_weak ();
805+
806+ // Check if we should warn before closing a single pane
807+ // (only triggers on Always — Automatic doesn't warn for single pane)
808+ const auto setting = _settings.GlobalSettings ().ConfirmOnClose ();
809+ if (setting == ConfirmOnClose::Always)
810+ {
811+ // If this is the last pane, closing it closes the tab,
812+ // so use the tab dialog text instead.
813+ const auto kind = activeTab->GetLeafPaneCount () == 1 ? ConfirmCloseDialogKind::Tab : ConfirmCloseDialogKind::Pane;
814+ auto warningResult = co_await _ShowConfirmCloseDialog (kind);
815+ if (!weak.get () || warningResult != ContentDialogResult::Primary)
816+ {
817+ co_return ;
818+ }
819+ }
820+
785821 if (co_await _PaneConfirmCloseReadOnly (pane))
786822 {
787823 if (const auto strong = weak.get ())
@@ -795,10 +831,33 @@ namespace winrt::TerminalApp::implementation
795831
796832 // Method Description:
797833 // - Close all panes with the given IDs sequentially.
834+ // - Shows a single aggregate confirmation dialog upfront if the confirmOnClose setting warrants it.
798835 // Arguments:
799- // - weakTab: weak reference to the tab that the pane belongs to.
836+ // - weakTab: weak reference to the tab that the panes belong to.
800837 // - paneIds: collection of the IDs of the panes that are marked for removal.
801- void TerminalPage::_ClosePanes (weak_ref<Tab> weakTab, std::vector<uint32_t > paneIds)
838+ safe_void_coroutine TerminalPage::_ClosePanes (weak_ref<Tab> weakTab, std::vector<uint32_t > paneIds)
839+ {
840+ // Show a single aggregate confirmation for closing multiple panes.
841+ if (_settings.GlobalSettings ().ConfirmOnClose () != ConfirmOnClose::Never)
842+ {
843+ const auto weak = get_weak ();
844+ auto warningResult = co_await _ShowConfirmCloseDialog (ConfirmCloseDialogKind::MultiplePanes);
845+ if (!weak.get () || warningResult != ContentDialogResult::Primary)
846+ {
847+ co_return ;
848+ }
849+ }
850+ _CloseRemainingPanes (weakTab, std::move (paneIds));
851+ }
852+
853+ // Method Description:
854+ // - Recursively closes panes by ID, chaining each close via the
855+ // ClosedByParent callback. Called after confirmation has already
856+ // been handled by _ClosePanes.
857+ // Arguments:
858+ // - weakTab: weak reference to the tab that the panes belong to
859+ // - paneIds: remaining pane IDs to close
860+ void TerminalPage::_CloseRemainingPanes (weak_ref<Tab> weakTab, std::vector<uint32_t > paneIds)
802861 {
803862 if (auto strongTab{ weakTab.get () })
804863 {
@@ -813,10 +872,9 @@ namespace winrt::TerminalApp::implementation
813872 pane->ClosedByParent ([ids{ std::move (paneIds) }, weakThis{ get_weak () }, weakTab]() {
814873 if (auto strongThis{ weakThis.get () })
815874 {
816- strongThis->_ClosePanes (weakTab, std::move (ids));
875+ strongThis->_CloseRemainingPanes (weakTab, std::move (ids));
817876 }
818877 });
819-
820878 // Close the pane which will eventually trigger the closed by parent event
821879 _HandleClosePaneRequested (pane);
822880 break ;
@@ -841,18 +899,33 @@ namespace winrt::TerminalApp::implementation
841899
842900 // Method Description:
843901 // - Closes provided tabs one by one
902+ // - Shows a single aggregate confirmation dialog upfront if the confirmOnClose setting warrants it.
844903 // Arguments:
845904 // - tabs - tabs to remove
846905 safe_void_coroutine TerminalPage::_RemoveTabs (const std::vector<winrt::TerminalApp::Tab> tabs)
847906 {
907+ if (tabs.empty ())
908+ {
909+ co_return ;
910+ }
911+
912+ // Show a single aggregate confirmation instead of per-tab dialogs.
848913 const auto weak = get_weak ();
914+ if (_settings.GlobalSettings ().ConfirmOnClose () != ConfirmOnClose::Never)
915+ {
916+ auto warningResult = co_await _ShowConfirmCloseDialog (ConfirmCloseDialogKind::MultipleTabs);
917+ if (!weak.get () || warningResult != ContentDialogResult::Primary)
918+ {
919+ co_return ;
920+ }
921+ }
849922
850923 for (auto & tab : tabs)
851924 {
852925 winrt::Windows::Foundation::IAsyncAction action{ nullptr };
853926 if (const auto strong = weak.get ())
854927 {
855- action = _HandleCloseTabRequested (tab);
928+ action = _HandleCloseTabRequested (tab, /* skipConfirmClose */ true );
856929 }
857930
858931 if (!action)
0 commit comments