Repository: microsoft/terminal (language: cpp)

Issue:
search web for highlighted text feature?
would be nice to highlight some terminal output and right click -> search in browser and it would just send whatever you highlighted to your default web browser.

would also be nice to have a "search stackoverflow" function, I was using google colab and when you get an error in that, it gives you a button that offers to search stackoverflow for you. might be nice to have a thing that detects text in your terminal looking like it might be an error message and offering the user a button to search web for that text. picture attached is what that looks like on colab.

![image](https://user-images.githubusercontent.com/57028307/119467928-5807af80-bd89-11eb-807c-b9c2bdef3df0.png)

so that button sends you here..
![image](https://user-images.githubusercontent.com/57028307/119468439-cb112600-bd89-11eb-940c-310454ad2144.png)

pretty simple little tool, but makes a world of difference.
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

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

<!-- 
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).
-->

# Proposed technical implementation details (optional)

<!-- 
A clear and concise description of what you want to happen.
-->



Discussion / hints:
This is a neat idea for an extension, thanks!

> **Note**
> ## Walkthrough

Start with this branch:  https://github.com/microsoft/terminal/compare/dev/migrie/fhl/10175-web-search-for-text

Anyone coming to this thread: Taking the above commit, it should be fairly trivial to get this across the finish line. 
* The TODO! in `ActionArgs.h` should just be removed
* The TODO! in `TerminalPage::_HandleSearchForText` might be tricker - should we join the lines with spaces? How does this interact with wrapped lines? Block selection? Basically, if you have answers there, that's good enough. 
> * The TODO! in `TerminalPage::_HandleSearchForText` might be tricker - should we join the lines with spaces? How does this interact with wrapped lines? Block selection? Basically, if you have answers there, that's good enough.

Could I just trim spaces from the end of each line and join with a single space? That way all wrapped lines would unwrap and block selection.
> Could I just trim spaces from the end of each line and join with a single space? That way all wrapped lines would unwrap and block selection

That seems like a sensible default to me! Dunno why I never thought of that 😅 

Fix patch:
diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json
index 9a1a24bdebb..41a671626b6 100644
--- a/doc/cascadia/profiles.schema.json
+++ b/doc/cascadia/profiles.schema.json
@@ -421,6 +421,7 @@
         "scrollToMark",
         "clearMark",
         "clearAllMarks",
+        "searchWeb",
         "experimental.colorSelection",
         "unbound"
       ],
@@ -1709,6 +1710,29 @@
         }
       ]
     },
+    "SearchWebAction": {
+      "description": "Search the web for selected text",
+      "allOf": [
+        {
+          "$ref": "#/$defs/ShortcutAction"
+        },
+        {
+          "properties": {
+            "action": {
+              "type": "string",
+              "const": "searchWeb"
+            },
+            "queryUrl": {
+              "type": "string",
+              "description": "URL of the web page to launch, %s is replaced with the selected text"
+            }
+          }
+        }
+      ],
+      "required": [
+        "queryUrl"
+      ]
+    },
     "AdjustOpacityAction": {
       "description": "Changes the opacity of the active Terminal window. If `relative` is specified, then this action will increase/decrease relative to the current opacity.",
       "allOf": [
@@ -2004,6 +2028,9 @@
             {
               "$ref": "#/$defs/ColorSelectionAction"
             },
+            {
+              "$ref": "#/$defs/SearchWebAction"
+            },
             {
               "type": "null"
             }
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index cd548423b56..aa912fd22d7 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -1021,6 +1021,49 @@ namespace winrt::TerminalApp::implementation
         args.Handled(true);
     }
 
+    void TerminalPage::_HandleSearchForText(const IInspectable& /*sender*/,
+                                            const ActionEventArgs& args)
+    {
+        if (const auto termControl{ _GetActiveControl() })
+        {
+            if (termControl.HasSelection())
+            {
+                const auto selections{ termControl.SelectedText(true) };
+
+                // concatenate the selection into a single line
+                auto searchText = std::accumulate(selections.begin(), selections.end(), std::wstring());
+
+                // make it compact by replacing consecutive whitespaces with a single space
+                searchText = std::regex_replace(searchText, std::wregex(LR"(\s+)"), L" ");
+
+                std::wstring queryUrl;
+                if (args)
+                {
+                    if (const auto& realArgs = args.ActionArgs().try_as<SearchForTextArgs>())
+                    {
+                        queryUrl = realArgs.QueryUrl().c_str();
+                    }
+                }
+
+                // use global default if query URL is unspecified
+                if (queryUrl.empty())
+                {
+                    queryUrl = _settings.GlobalSettings().SearchWebDefaultQueryUrl().c_str();
+                }
+
+                constexpr std::wstring_view queryToken{ L"%s" };
+                if (const auto pos{ queryUrl.find(queryToken) }; pos != std::wstring_view::npos)
+                {
+                    queryUrl.replace(pos, queryToken.length(), Windows::Foundation::Uri::EscapeComponent(searchText));
+                }
+
+                winrt::Microsoft::Terminal::Control::OpenHyperlinkEventArgs shortcut{ queryUrl };
+                _OpenHyperlinkHandler(termControl, shortcut);
+                args.Handled(true);
+            }
+        }
+    }
+
     void TerminalPage::_HandleGlobalSummon(const IInspectable& /*sender*/,
                                            const ActionEventArgs& args)
     {
diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
index e33bc405dd8..1a20db37695 100644
--- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
@@ -214,6 +214,9 @@
   <data name="SplitPaneText" xml:space="preserve">
     <value>Split Pane</value>
   </data>
+  <data name="SearchWebText" xml:space="preserve">
+    <value>Web Search</value>
+  </data>  
   <data name="TabColorChoose" xml:space="preserve">
     <value>Color...</value>
   </data>
@@ -833,4 +836,4 @@
   <data name="MoveTabToNewWindowToolTip" xml:space="preserve">
     <value>Moves tab to a new window </value>
   </data>
-</root>
\ No newline at end of file
+</root>
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 2f6691162ce..af116ca8e23 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -4578,7 +4578,7 @@ namespace winrt::TerminalApp::implementation
     }
 
     void TerminalPage::_PopulateContextMenu(const IInspectable& sender,
-                                            const bool /*withSelection*/)
+                                            const bool withSelection)
     {
         // withSelection can be used to add actions that only appear if there's
         // selected text, like "search the web". In this initial draft, it's not
@@ -4635,6 +4635,11 @@ namespace winrt::TerminalApp::implementation
             makeItem(RS_(L"PaneClose"), L"\xE89F", ActionAndArgs{ ShortcutAction::ClosePane, nullptr });
         }
 
+        if (withSelection)
+        {
+            makeItem(RS_(L"SearchWebText"), L"\xF6FA", ActionAndArgs{ ShortcutAction::SearchForText, nullptr });
+        }
+
         makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
     }
 
diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h
index f444b81e79d..533b20edf50 100644
--- a/src/cascadia/TerminalControl/ControlCore.h
+++ b/src/cascadia/TerminalControl/ControlCore.h
@@ -145,6 +145,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         int ViewHeight() const;
         int BufferHeight() const;
 
+        bool HasSelection() const;
+        Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
+
         bool BracketedPasteEnabled() const noexcept;
 
         Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
@@ -188,9 +191,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         bool ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const;
         Core::Point CursorPosition() const;
 
-        bool HasSelection() const;
         bool CopyOnSelect() const;
-        Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
         Control::SelectionData SelectionInfo() const;
         void SetSelectionAnchor(const til::point position);
         void SetEndSelectionPoint(const til::point position);
diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl
index fdd7fa53b2a..3137424cfd7 100644
--- a/src/cascadia/TerminalControl/ControlCore.idl
+++ b/src/cascadia/TerminalControl/ControlCore.idl
@@ -124,8 +124,6 @@ namespace Microsoft.Terminal.Control
         void Search(String text, Boolean goForward, Boolean caseSensitive);
         Microsoft.Terminal.Core.Color BackgroundColor { get; };
 
-        Boolean HasSelection { get; };
-        IVector<String> SelectedText(Boolean trimTrailingWhitespace);
         SelectionData SelectionInfo { get; };
         SelectionInteractionMode SelectionMode();
 
diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h
index 2551843df20..e1586e600a3 100644
--- a/src/cascadia/TerminalControl/EventArgs.h
+++ b/src/cascadia/TerminalControl/EventArgs.h
@@ -180,3 +180,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         WINRT_PROPERTY(bool, ClearMarkers, false);
     };
 }
+
+namespace winrt::Microsoft::Terminal::Control::factory_implementation
+{
+    BASIC_FACTORY(OpenHyperlinkEventArgs);
+}
diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl
index bc67bd13624..7dbe9d47e47 100644
--- a/src/cascadia/TerminalControl/EventArgs.idl
+++ b/src/cascadia/TerminalControl/EventArgs.idl
@@ -40,6 +40,7 @@ namespace Microsoft.Terminal.Control
 
     runtimeclass OpenHyperlinkEventArgs
     {
+        OpenHyperlinkEventArgs(String uri);
         String Uri { get; };
     }
 
diff --git a/src/cascadia/TerminalControl/ICoreState.idl b/src/cascadia/TerminalControl/ICoreState.idl
index 405d1bd00f8..ed60ab4ea6c 100644
--- a/src/cascadia/TerminalControl/ICoreState.idl
+++ b/src/cascadia/TerminalControl/ICoreState.idl
@@ -45,6 +45,9 @@ namespace Microsoft.Terminal.Control
         Int32 ViewHeight { get; };
         Int32 BufferHeight { get; };
 
+        Boolean HasSelection { get; };
+        IVector<String> SelectedText(Boolean trimTrailingWhitespace);
+
         Boolean BracketedPasteEnabled { get; };
 
         Microsoft.Terminal.TerminalConnection.ConnectionState ConnectionState { get; };
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 1c1fc03da54..215d8ee1344 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -3335,6 +3335,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         return _core.Opacity();
     }
 
+    bool TermControl::HasSelection() const
+    {
+        return _core.HasSelection();
+    }
+    Windows::Foundation::Collections::IVector<winrt::hstring> TermControl::SelectedText(bool trimTrailingWhitespace) const
+    {
+        return _core.SelectedText(trimTrailingWhitespace);
+    }
+
     // Method Description:
     // - Called when the core raises a FoundMatch event. That's done in response
     //   to us starting a search query with ControlCore::Search.
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 334c9b2ddc1..f177c816ab7 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -69,6 +69,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         int ViewHeight() const;
         int BufferHeight() const;
 
+        bool HasSelection() const;
+        Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
+
         bool BracketedPasteEnabled() const noexcept;
 
         double BackgroundOpacity() const;
diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
index 1af79967f17..75ba4487b0c 100644
--- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
@@ -72,6 +72,7 @@ static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" };
 static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" };
 static constexpr std::string_view RenameWindowKey{ "renameWindow" };
 static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
+static constexpr std::string_view SearchForTextKey{ "searchWeb" };
 static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
 static constexpr std::string_view QuakeModeKey{ "quakeMode" };
 static constexpr std::string_view FocusPaneKey{ "focusPane" };
@@ -403,6 +404,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
                 { ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") },
                 { ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") },
                 { ShortcutAction::GlobalSummon, MustGenerate },
+                { ShortcutAction::SearchForText, MustGenerate },
                 { ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
                 { ShortcutAction::FocusPane, MustGenerate },
                 { ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
index 67d7493e2f7..b002f7d0b7a 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
@@ -37,6 +37,7 @@
 #include "PrevTabArgs.g.cpp"
 #include "NextTabArgs.g.cpp"
 #include "RenameWindowArgs.g.cpp"
+#include "SearchForTextArgs.g.cpp"
 #include "GlobalSummonArgs.g.cpp"
 #include "FocusPaneArgs.g.cpp"
 #include "ExportBufferArgs.g.cpp"
@@ -765,6 +766,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         return RS_(L"ResetWindowNameCommandKey");
     }
 
+    winrt::hstring SearchForTextArgs::GenerateName() const
+    {
+        return winrt::hstring{
+            fmt::format(std::wstring_view(RS_(L"SearchForTextCommandKey")),
+                        Windows::Foundation::Uri(QueryUrl()).Domain().c_str())
+        };
+    }
+
     winrt::hstring GlobalSummonArgs::GenerateName() const
     {
         // GH#10210 - Is this action literally the same thing as the `quakeMode`
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h
index 30367fdae37..0cf0eb4d593 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.h
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h
@@ -39,6 +39,7 @@
 #include "PrevTabArgs.g.h"
 #include "NextTabArgs.g.h"
 #include "RenameWindowArgs.g.h"
+#include "SearchForTextArgs.g.h"
 #include "GlobalSummonArgs.g.h"
 #include "FocusPaneArgs.g.h"
 #include "ExportBufferArgs.g.h"
@@ -227,6 +228,10 @@ private:                                                                    \
 #define RENAME_WINDOW_ARGS(X) \
     X(winrt::hstring, Name, "name", false, L"")
 
+////////////////////////////////////////////////////////////////////////////////
+#define SEARCH_FOR_TEXT_ARGS(X) \
+    X(winrt::hstring, QueryUrl, "queryUrl", false, L"")
+
 ////////////////////////////////////////////////////////////////////////////////
 #define GLOBAL_SUMMON_ARGS(X)                                                               \
     X(winrt::hstring, Name, "name", false, L"")                                             \
@@ -711,6 +716,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
 
     ACTION_ARGS_STRUCT(RenameWindowArgs, RENAME_WINDOW_ARGS);
 
+    ACTION_ARGS_STRUCT(SearchForTextArgs, SEARCH_FOR_TEXT_ARGS);
+
     struct GlobalSummonArgs : public GlobalSummonArgsT<GlobalSummonArgs>
     {
         ACTION_ARG_BODY(GlobalSummonArgs, GLOBAL_SUMMON_ARGS)
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
index 06b81964481..d2127922dcd 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
@@ -349,6 +349,11 @@ namespace Microsoft.Terminal.Settings.Model
         String Name { get; };
     };
 
+    [default_interface] runtimeclass SearchForTextArgs : IActionArgs
+    {
+        String QueryUrl { get; };
+    };
+
     [default_interface] runtimeclass GlobalSummonArgs : IActionArgs
     {
         String Name { get; };
diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
index 22740c1e3d3..127b0bf2df9 100644
--- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
+++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
@@ -85,6 +85,7 @@
     ON_ALL_ACTIONS(IdentifyWindows)         \
     ON_ALL_ACTIONS(RenameWindow)            \
     ON_ALL_ACTIONS(OpenWindowRenamer)       \
+    ON_ALL_ACTIONS(SearchForText)           \
     ON_ALL_ACTIONS(GlobalSummon)            \
     ON_ALL_ACTIONS(QuakeMode)               \
     ON_ALL_ACTIONS(FocusPane)               \
@@ -115,6 +116,7 @@
     ON_ALL_ACTIONS_WITH_ARGS(CopyText)             \
     ON_ALL_ACTIONS_WITH_ARGS(ExecuteCommandline)   \
     ON_ALL_ACTIONS_WITH_ARGS(FindMatch)            \
+    ON_ALL_ACTIONS_WITH_ARGS(SearchForText)        \
     ON_ALL_ACTIONS_WITH_ARGS(GlobalSummon)         \
     ON_ALL_ACTIONS_WITH_ARGS(MoveFocus)            \
     ON_ALL_ACTIONS_WITH_ARGS(MovePane)             \
diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
index a9745cf7804..14adf38112a 100644
--- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
@@ -100,6 +100,7 @@ namespace Microsoft.Terminal.Settings.Model
         INHERITABLE_SETTING(Boolean, EnableColorSelection);
         INHERITABLE_SETTING(Boolean, IsolatedMode);
         INHERITABLE_SETTING(Boolean, AllowHeadless);
+        INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
 
         Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
         void AddColorScheme(ColorScheme scheme);
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index 6b2a021e4cf..367bf389fc6 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -65,7 +65,8 @@ Author(s):
     X(bool, EnableColorSelection, "experimental.enableColorSelection", false)                                                                                                                         \
     X(winrt::Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry>, NewTabMenu, "newTabMenu", winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} })) \
     X(bool, AllowHeadless, "compatibility.allowHeadless", false)                                                                                                                                      \
-    X(bool, IsolatedMode, "compatibility.isolatedMode", false)
+    X(bool, IsolatedMode, "compatibility.isolatedMode", false)                                                                                                                                        \
+    X(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22")
 
 #define MTSM_PROFILE_SETTINGS(X)                                                                                                                               \
     X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE)                                                                                               \
diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
index e0f171dc8fe..414783c6557 100644
--- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
@@ -699,4 +699,12 @@
   <data name="SelectCommandPreviousCommandKey" xml:space="preserve">
     <value>Select previous command</value>
   </data>
+  <data name="SearchForTextCommandKey" xml:space="preserve">
+    <value>Search {0}</value>
+    <comment>{0} will be replaced with a user-specified URL to a web page</comment>
+  </data>
+  <data name="SearchWebCommandKey" xml:space="preserve">
+    <value>Search the web for selected text</value>
+    <comment>This will open a web browser to search for some user-selected text</comment>
+  </data>
 </root>
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index 16246378aaa..117abb5c892 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -466,6 +466,9 @@
         { "command": "expandSelectionToWord" },
         { "command": "showContextMenu", "keys": "menu" },
 
+        // Web Search
+        { "command": { "action": "searchWeb" }, "name": { "key": "SearchWebCommandKey" } },
+
         // Scrollback
         { "command": "scrollDown", "keys": "ctrl+shift+down" },
         { "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },


Test patch: