Repository: microsoft/terminal (language: cpp)

Issue:
Add "Close All Other Panes" Action to Settings/Actions
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

# Description of the new feature/enhancement

Add "Close All Other Panes" action to settings/actions.

<!-- 
A clear and concise description of what the problem is that the new feature would solve.
Describe why and how a user would use this new functionality (if applicable).
-->

There is an action for "Close All Other Tabs", so I can close all other tabs with a keyboard shortcut. There is not an action for "Close All Other Panes", so I can't close all other panes with a keyboard shortcut. If added, this would enable users to close all other panes with a keyboard shortcut, making the app more pleasant to use.

# Proposed technical implementation details (optional)

<!-- 
A clear and concise description of what you want to happen.
-->

I want to see an action for "Close All Other Tabs" in settings/actions so I can assign a keyboard shortcut to that action.


Discussion / hints:
Sure, I don't see why not.

Fix patch:
diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json
index 2ec1ac9033a..7c6e51676e9 100644
--- a/doc/cascadia/profiles.schema.json
+++ b/doc/cascadia/profiles.schema.json
@@ -306,6 +306,7 @@
       "enum": [
         "adjustFontSize",
         "clearBuffer",
+        "closeOtherPanes",
         "closeOtherTabs",
         "closePane",
         "closeTab",
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index 10a7f8cfa31..b2b39a1e362 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -166,6 +166,40 @@ namespace winrt::TerminalApp::implementation
         }
     }
 
+    void TerminalPage::_HandleCloseOtherPanes(const IInspectable& /*sender*/,
+                                              const ActionEventArgs& args)
+    {
+        if (const auto terminalTab{ _GetFocusedTabImpl() })
+        {
+            const auto activePane = terminalTab->GetActivePane();
+            if (terminalTab->GetRootPane() != activePane)
+            {
+                _UnZoomIfNeeded();
+
+                // Accumulate list of all unfocused leaf panes, ignore read-only panes
+                std::vector<uint32_t> unfocusedPaneIds;
+                const auto activePaneId = activePane->Id();
+                terminalTab->GetRootPane()->WalkTree([&](auto&& p) {
+                    const auto id = p->Id();
+                    if (id.has_value() && id != activePaneId && !p->ContainsReadOnly())
+                    {
+                        unfocusedPaneIds.push_back(id.value());
+                    }
+                });
+
+                if (!empty(unfocusedPaneIds))
+                {
+                    // Start by removing the panes that were least recently added
+                    sort(begin(unfocusedPaneIds), end(unfocusedPaneIds), std::less<uint32_t>());
+                    _ClosePanes(terminalTab->get_weak(), std::move(unfocusedPaneIds));
+                    args.Handled(true);
+                    return;
+                }
+            }
+            args.Handled(false);
+        }
+    }
+
     void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
                                        const ActionEventArgs& args)
     {
diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp
index b8c1d079ed2..7d3fab8f5ac 100644
--- a/src/cascadia/TerminalApp/Pane.cpp
+++ b/src/cascadia/TerminalApp/Pane.cpp
@@ -1755,6 +1755,9 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
         remainingChild->_firstChild = nullptr;
         remainingChild->_secondChild = nullptr;
     }
+
+    // Notify the discarded child that it was closed by its parent
+    closedChild->_ClosedByParentHandlers();
 }
 
 winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst)
diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h
index 2c1fec243d6..c4824fdf9d9 100644
--- a/src/cascadia/TerminalApp/Pane.h
+++ b/src/cascadia/TerminalApp/Pane.h
@@ -193,6 +193,7 @@ class Pane : public std::enable_shared_from_this<Pane>
 
     void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states);
 
+    WINRT_CALLBACK(ClosedByParent, winrt::delegate<>);
     WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
 
     using gotFocusArgs = winrt::delegate<std::shared_ptr<Pane>, winrt::Windows::UI::Xaml::FocusState>;
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index 332b8331292..95ef15c0d47 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -760,6 +760,65 @@ namespace winrt::TerminalApp::implementation
         }
     }
 
+    // Method Description:
+    // - Disables read-only mode on pane if the user wishes to close it and read-only mode is enabled.
+    // Arguments:
+    // - pane: the pane that is about to be closed.
+    // Return Value:
+    // - bool indicating whether the (read-only) pane can be closed.
+    winrt::Windows::Foundation::IAsyncOperation<bool> TerminalPage::_PaneConfirmCloseReadOnly(std::shared_ptr<Pane> pane)
+    {
+        if (pane->ContainsReadOnly())
+        {
+            auto warningResult = co_await _ShowCloseReadOnlyDialog();
+
+            // If the user didn't explicitly click on close tab - leave
+            if (warningResult != ContentDialogResult::Primary)
+            {
+                co_return false;
+            }
+
+            // Clean read-only mode to prevent additional prompt if closing the pane triggers closing of a hosting tab
+            pane->WalkTree([](auto p) {
+                if (const auto control{ p->GetTerminalControl() })
+                {
+                    if (control.ReadOnly())
+                    {
+                        control.ToggleReadOnly();
+                    }
+                }
+            });
+        }
+        co_return true;
+    }
+
+    // Method Description:
+    // - Removes the pane from the tab it belongs to.
+    // Arguments:
+    // - pane: the pane to close.
+    void TerminalPage::_HandleClosePaneRequested(std::shared_ptr<Pane> pane)
+    {
+        // Build the list of actions to recreate the closed pane,
+        // BuildStartupActions returns the "first" pane and the rest of
+        // its actions are assuming that first pane has been created first.
+        // This doesn't handle refocusing anything in particular, the
+        // result will be that the last pane created is focused. In the
+        // case of a single pane that is the desired behavior anyways.
+        auto state = pane->BuildStartupActions(0, 1);
+        {
+            ActionAndArgs splitPaneAction{};
+            splitPaneAction.Action(ShortcutAction::SplitPane);
+            SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane() };
+            splitPaneAction.Args(splitPaneArgs);
+
+            state.args.emplace(state.args.begin(), std::move(splitPaneAction));
+        }
+        _AddPreviouslyClosedPaneOrTab(std::move(state.args));
+
+        // If specified, detach before closing to directly update the pane structure
+        pane->Close();
+    }
+
     // Method Description:
     // - Close the currently focused pane. If the pane is the last pane in the
     //   tab, the tab will also be closed. This will happen when we handle the
@@ -772,46 +831,10 @@ namespace winrt::TerminalApp::implementation
 
             if (const auto pane{ terminalTab->GetActivePane() })
             {
-                if (pane->ContainsReadOnly())
+                if (co_await _PaneConfirmCloseReadOnly(pane))
                 {
-                    auto warningResult = co_await _ShowCloseReadOnlyDialog();
-
-                    // If the user didn't explicitly click on close tab - leave
-                    if (warningResult != ContentDialogResult::Primary)
-                    {
-                        co_return;
-                    }
-
-                    // Clean read-only mode to prevent additional prompt if closing the pane triggers closing of a hosting tab
-                    pane->WalkTree([](auto p) {
-                        if (const auto control{ p->GetTerminalControl() })
-                        {
-                            if (control.ReadOnly())
-                            {
-                                control.ToggleReadOnly();
-                            }
-                        }
-                    });
+                    _HandleClosePaneRequested(pane);
                 }
-
-                // Build the list of actions to recreate the closed pane,
-                // BuildStartupActions returns the "first" pane and the rest of
-                // its actions are assuming that first pane has been created first.
-                // This doesn't handle refocusing anything in particular, the
-                // result will be that the last pane created is focused. In the
-                // case of a single pane that is the desired behavior anyways.
-                auto state = pane->BuildStartupActions(0, 1);
-                {
-                    ActionAndArgs splitPaneAction{};
-                    splitPaneAction.Action(ShortcutAction::SplitPane);
-                    SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane() };
-                    splitPaneAction.Args(splitPaneArgs);
-
-                    state.args.emplace(state.args.begin(), std::move(splitPaneAction));
-                }
-                _AddPreviouslyClosedPaneOrTab(std::move(state.args));
-
-                pane->Close();
             }
         }
         else if (auto index{ _GetFocusedTabIndex() })
@@ -824,6 +847,38 @@ namespace winrt::TerminalApp::implementation
         }
     }
 
+    // Method Description:
+    // - Close all panes with the given IDs sequentially.
+    // Arguments:
+    // - weakTab: weak reference to the tab that the pane belongs to.
+    // - paneIds: collection of the IDs of the panes that are marked for removal.
+    void TerminalPage::_ClosePanes(weak_ref<TerminalTab> weakTab, std::vector<uint32_t> paneIds)
+    {
+        if (auto strongTab{ weakTab.get() })
+        {
+            // Close all unfocused panes one by one
+            while (!paneIds.empty())
+            {
+                const auto id = paneIds.back();
+                paneIds.pop_back();
+
+                if (const auto pane{ strongTab->GetRootPane()->FindPane(id) })
+                {
+                    pane->ClosedByParent([ids{ std::move(paneIds) }, weakThis{ get_weak() }, weakTab]() {
+                        if (auto strongThis{ weakThis.get() })
+                        {
+                            strongThis->_ClosePanes(weakTab, std::move(ids));
+                        }
+                    });
+
+                    // Close the pane which will eventually trigger the closed by parent event
+                    _HandleClosePaneRequested(pane);
+                    break;
+                }
+            }
+        }
+    }
+
     // Method Description:
     // - Close the tab at the given index.
     void TerminalPage::_CloseTabAtIndex(uint32_t index)
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 3d5b70efe5e..538654daef0 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -322,8 +322,11 @@ namespace winrt::TerminalApp::implementation
         winrt::com_ptr<TerminalTab> _GetFocusedTabImpl() const noexcept;
         TerminalApp::TabBase _GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept;
 
+        void _HandleClosePaneRequested(std::shared_ptr<Pane> pane);
         winrt::fire_and_forget _SetFocusedTab(const winrt::TerminalApp::TabBase tab);
         winrt::fire_and_forget _CloseFocusedPane();
+        void _ClosePanes(weak_ref<TerminalTab> weakTab, std::vector<uint32_t> paneIds);
+        winrt::Windows::Foundation::IAsyncOperation<bool> _PaneConfirmCloseReadOnly(std::shared_ptr<Pane> pane);
         void _AddPreviouslyClosedPaneOrTab(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>&& args);
 
         winrt::fire_and_forget _RemoveOnCloseRoutine(Microsoft::UI::Xaml::Controls::TabViewItem tabViewItem, winrt::com_ptr<TerminalPage> page);
diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
index 8591457e611..b9aaaf1aae3 100644
--- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
@@ -10,6 +10,7 @@
 #include <LibraryResources.h>
 
 static constexpr std::string_view AdjustFontSizeKey{ "adjustFontSize" };
+static constexpr std::string_view CloseOtherPanesKey{ "closeOtherPanes" };
 static constexpr std::string_view CloseOtherTabsKey{ "closeOtherTabs" };
 static constexpr std::string_view ClosePaneKey{ "closePane" };
 static constexpr std::string_view CloseTabKey{ "closeTab" };
@@ -329,6 +330,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         static const auto GeneratedActionNames = []() {
             return std::unordered_map<ShortcutAction, winrt::hstring>{
                 { ShortcutAction::AdjustFontSize, RS_(L"AdjustFontSizeCommandKey") },
+                { ShortcutAction::CloseOtherPanes, RS_(L"CloseOtherPanesCommandKey") },
                 { ShortcutAction::CloseOtherTabs, L"" }, // Intentionally omitted, must be generated by GenerateName
                 { ShortcutAction::ClosePane, RS_(L"ClosePaneCommandKey") },
                 { ShortcutAction::CloseTab, L"" }, // Intentionally omitted, must be generated by GenerateName
diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
index 23d4cf2110c..800e4352878 100644
--- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
+++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
@@ -23,80 +23,81 @@
 // each action. This is _NOT_ something that should be used when any individual
 // case should be customized.
 
-#define ALL_SHORTCUT_ACTIONS               \
-    ON_ALL_ACTIONS(CopyText)               \
-    ON_ALL_ACTIONS(PasteText)              \
-    ON_ALL_ACTIONS(OpenNewTabDropdown)     \
-    ON_ALL_ACTIONS(DuplicateTab)           \
-    ON_ALL_ACTIONS(NewTab)                 \
-    ON_ALL_ACTIONS(CloseWindow)            \
-    ON_ALL_ACTIONS(CloseTab)               \
-    ON_ALL_ACTIONS(ClosePane)              \
-    ON_ALL_ACTIONS(NextTab)                \
-    ON_ALL_ACTIONS(PrevTab)                \
-    ON_ALL_ACTIONS(SendInput)              \
-    ON_ALL_ACTIONS(SplitPane)              \
-    ON_ALL_ACTIONS(ToggleSplitOrientation) \
-    ON_ALL_ACTIONS(TogglePaneZoom)         \
-    ON_ALL_ACTIONS(SwitchToTab)            \
-    ON_ALL_ACTIONS(AdjustFontSize)         \
-    ON_ALL_ACTIONS(ResetFontSize)          \
-    ON_ALL_ACTIONS(ScrollUp)               \
-    ON_ALL_ACTIONS(ScrollDown)             \
-    ON_ALL_ACTIONS(ScrollUpPage)           \
-    ON_ALL_ACTIONS(ScrollDownPage)         \
-    ON_ALL_ACTIONS(ScrollToTop)            \
-    ON_ALL_ACTIONS(ScrollToBottom)         \
-    ON_ALL_ACTIONS(ScrollToMark)           \
-    ON_ALL_ACTIONS(AddMark)                \
-    ON_ALL_ACTIONS(ClearMark)              \
-    ON_ALL_ACTIONS(ClearAllMarks)          \
-    ON_ALL_ACTIONS(ResizePane)             \
-    ON_ALL_ACTIONS(MoveFocus)              \
-    ON_ALL_ACTIONS(MovePane)               \
-    ON_ALL_ACTIONS(SwapPane)               \
-    ON_ALL_ACTIONS(Find)                   \
-    ON_ALL_ACTIONS(ToggleShaderEffects)    \
-    ON_ALL_ACTIONS(ToggleFocusMode)        \
-    ON_ALL_ACTIONS(ToggleFullscreen)       \
-    ON_ALL_ACTIONS(ToggleAlwaysOnTop)      \
-    ON_ALL_ACTIONS(OpenSettings)           \
-    ON_ALL_ACTIONS(SetFocusMode)           \
-    ON_ALL_ACTIONS(SetFullScreen)          \
-    ON_ALL_ACTIONS(SetMaximized)           \
-    ON_ALL_ACTIONS(SetColorScheme)         \
-    ON_ALL_ACTIONS(SetTabColor)            \
-    ON_ALL_ACTIONS(OpenTabColorPicker)     \
-    ON_ALL_ACTIONS(RenameTab)              \
-    ON_ALL_ACTIONS(OpenTabRenamer)         \
-    ON_ALL_ACTIONS(ExecuteCommandline)     \
-    ON_ALL_ACTIONS(ToggleCommandPalette)   \
-    ON_ALL_ACTIONS(CloseOtherTabs)         \
-    ON_ALL_ACTIONS(CloseTabsAfter)         \
-    ON_ALL_ACTIONS(TabSearch)              \
-    ON_ALL_ACTIONS(MoveTab)                \
-    ON_ALL_ACTIONS(BreakIntoDebugger)      \
-    ON_ALL_ACTIONS(TogglePaneReadOnly)     \
-    ON_ALL_ACTIONS(FindMatch)              \
-    ON_ALL_ACTIONS(NewWindow)              \
-    ON_ALL_ACTIONS(IdentifyWindow)         \
-    ON_ALL_ACTIONS(IdentifyWindows)        \
-    ON_ALL_ACTIONS(RenameWindow)           \
-    ON_ALL_ACTIONS(OpenWindowRenamer)      \
-    ON_ALL_ACTIONS(GlobalSummon)           \
-    ON_ALL_ACTIONS(QuakeMode)              \
-    ON_ALL_ACTIONS(FocusPane)              \
-    ON_ALL_ACTIONS(OpenSystemMenu)         \
-    ON_ALL_ACTIONS(ExportBuffer)           \
-    ON_ALL_ACTIONS(ClearBuffer)            \
-    ON_ALL_ACTIONS(MultipleActions)        \
-    ON_ALL_ACTIONS(Quit)                   \
-    ON_ALL_ACTIONS(AdjustOpacity)          \
-    ON_ALL_ACTIONS(RestoreLastClosed)      \
-    ON_ALL_ACTIONS(SelectAll)              \
-    ON_ALL_ACTIONS(MarkMode)               \
-    ON_ALL_ACTIONS(ToggleBlockSelection)   \
-    ON_ALL_ACTIONS(SwitchSelectionEndpoint)
+#define ALL_SHORTCUT_ACTIONS                \
+    ON_ALL_ACTIONS(CopyText)                \
+    ON_ALL_ACTIONS(PasteText)               \
+    ON_ALL_ACTIONS(OpenNewTabDropdown)      \
+    ON_ALL_ACTIONS(DuplicateTab)            \
+    ON_ALL_ACTIONS(NewTab)                  \
+    ON_ALL_ACTIONS(CloseWindow)             \
+    ON_ALL_ACTIONS(CloseTab)                \
+    ON_ALL_ACTIONS(ClosePane)               \
+    ON_ALL_ACTIONS(NextTab)                 \
+    ON_ALL_ACTIONS(PrevTab)                 \
+    ON_ALL_ACTIONS(SendInput)               \
+    ON_ALL_ACTIONS(SplitPane)               \
+    ON_ALL_ACTIONS(ToggleSplitOrientation)  \
+    ON_ALL_ACTIONS(TogglePaneZoom)          \
+    ON_ALL_ACTIONS(SwitchToTab)             \
+    ON_ALL_ACTIONS(AdjustFontSize)          \
+    ON_ALL_ACTIONS(ResetFontSize)           \
+    ON_ALL_ACTIONS(ScrollUp)                \
+    ON_ALL_ACTIONS(ScrollDown)              \
+    ON_ALL_ACTIONS(ScrollUpPage)            \
+    ON_ALL_ACTIONS(ScrollDownPage)          \
+    ON_ALL_ACTIONS(ScrollToTop)             \
+    ON_ALL_ACTIONS(ScrollToBottom)          \
+    ON_ALL_ACTIONS(ScrollToMark)            \
+    ON_ALL_ACTIONS(AddMark)                 \
+    ON_ALL_ACTIONS(ClearMark)               \
+    ON_ALL_ACTIONS(ClearAllMarks)           \
+    ON_ALL_ACTIONS(ResizePane)              \
+    ON_ALL_ACTIONS(MoveFocus)               \
+    ON_ALL_ACTIONS(MovePane)                \
+    ON_ALL_ACTIONS(SwapPane)                \
+    ON_ALL_ACTIONS(Find)                    \
+    ON_ALL_ACTIONS(ToggleShaderEffects)     \
+    ON_ALL_ACTIONS(ToggleFocusMode)         \
+    ON_ALL_ACTIONS(ToggleFullscreen)        \
+    ON_ALL_ACTIONS(ToggleAlwaysOnTop)       \
+    ON_ALL_ACTIONS(OpenSettings)            \
+    ON_ALL_ACTIONS(SetFocusMode)            \
+    ON_ALL_ACTIONS(SetFullScreen)           \
+    ON_ALL_ACTIONS(SetMaximized)            \
+    ON_ALL_ACTIONS(SetColorScheme)          \
+    ON_ALL_ACTIONS(SetTabColor)             \
+    ON_ALL_ACTIONS(OpenTabColorPicker)      \
+    ON_ALL_ACTIONS(RenameTab)               \
+    ON_ALL_ACTIONS(OpenTabRenamer)          \
+    ON_ALL_ACTIONS(ExecuteCommandline)      \
+    ON_ALL_ACTIONS(ToggleCommandPalette)    \
+    ON_ALL_ACTIONS(CloseOtherTabs)          \
+    ON_ALL_ACTIONS(CloseTabsAfter)          \
+    ON_ALL_ACTIONS(TabSearch)               \
+    ON_ALL_ACTIONS(MoveTab)                 \
+    ON_ALL_ACTIONS(BreakIntoDebugger)       \
+    ON_ALL_ACTIONS(TogglePaneReadOnly)      \
+    ON_ALL_ACTIONS(FindMatch)               \
+    ON_ALL_ACTIONS(NewWindow)               \
+    ON_ALL_ACTIONS(IdentifyWindow)          \
+    ON_ALL_ACTIONS(IdentifyWindows)         \
+    ON_ALL_ACTIONS(RenameWindow)            \
+    ON_ALL_ACTIONS(OpenWindowRenamer)       \
+    ON_ALL_ACTIONS(GlobalSummon)            \
+    ON_ALL_ACTIONS(QuakeMode)               \
+    ON_ALL_ACTIONS(FocusPane)               \
+    ON_ALL_ACTIONS(OpenSystemMenu)          \
+    ON_ALL_ACTIONS(ExportBuffer)            \
+    ON_ALL_ACTIONS(ClearBuffer)             \
+    ON_ALL_ACTIONS(MultipleActions)         \
+    ON_ALL_ACTIONS(Quit)                    \
+    ON_ALL_ACTIONS(AdjustOpacity)           \
+    ON_ALL_ACTIONS(RestoreLastClosed)       \
+    ON_ALL_ACTIONS(SelectAll)               \
+    ON_ALL_ACTIONS(MarkMode)                \
+    ON_ALL_ACTIONS(ToggleBlockSelection)    \
+    ON_ALL_ACTIONS(SwitchSelectionEndpoint) \
+    ON_ALL_ACTIONS(CloseOtherPanes)
 
 #define ALL_SHORTCUT_ACTIONS_WITH_ARGS             \
     ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize)       \
diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
index 49135e63364..4f1514af722 100644
--- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
@@ -567,4 +567,7 @@
   <data name="SwitchSelectionEndpointCommandKey" xml:space="preserve">
     <value>Switch selection endpoint</value>
   </data>
+  <data name="CloseOtherPanesCommandKey" xml:space="preserve">
+    <value>Close all other panes</value>
+  </data>
 </root>
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index 7a672723c33..96ac06fe9e4 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -359,6 +359,7 @@
         { "command": { "action": "switchToTab", "index": 4294967295 }, "keys": "ctrl+alt+9" },
 
         // Pane Management
+        { "command": "closeOtherPanes" },
         { "command": "closePane", "keys": "ctrl+shift+w" },
         { "command": { "action": "splitPane", "split": "up" } },
         { "command": { "action": "splitPane", "split": "down" }, "keys": "alt+shift+-" },


Test patch: