github.com-microsoft-terminal
all · 6 devs · built 2026-06-13
Repository snapshot
Monthly reports
Highlights
- Major *UI redesign* for the New Tab Menu, now referred to as the "Dropdown Menu", enhancing usability and responsiveness within the *Terminal Settings Editor* [a8a5c881 · Carlos Zamora].
- Significant *accessibility improvements* were implemented, including screen reader announcements for navigation pane states [00e0ecb8 · Carlos Zamora] and improved text legibility in High Contrast mode for settings error messages [a325a2fa · Sushaanth Srinivasan].
- New *user control features* were introduced, such as a `safeUriSchemes` setting to bypass URI confirmation dialogs [fb71a046 · Lucas Trzesniewski] and a more flexible `confirmOnClose` setting for unified session management warnings [b753e3de · Carlos Zamora].
- A critical *security fix* was delivered to prevent integer overflow vulnerabilities during `WM_COPYDATA` message deserialization within the *WindowEmperor* component [8edac5fb · Dustin L. Howett].
- Enhanced *localization support* was provided with new Chinese (Simplified) strings for confirmation dialogs [d3f76e7a · Windows Console Service Bot] and comprehensive updates across various languages for recent features and terminology adjustments [ea4cb814 · Windows Console Service Bot], [7fc12cf9 · Windows Console Service Bot].
Observations
- Commit volume for May 2026 was 21, representing a 33% decrease compared to the 2-month average of 32 commits.
- The waste score significantly decreased by 36% compared to the 2-month average, suggesting improved code quality or fewer reverted/reworked changes.
- A pattern of *bug fixes* related to *terminal core behavior* and *rendering* is observed, including cursor management [12e3455b · aarushi singh], *sixel image rendering* memory optimization [c829d4ca · Dustin L. Howett], and window title management [b991eb04 · Dustin L. Howett].
- Several commits addressed *developer experience* and *build system* improvements, such as a new solution filter for *Conhost* components [8ef8e95e · Dustin L. Howett] and fixes for `clang-cl` compatibility issues [dbc5177f · Dustin L. Howett].
- The *Terminal Settings Editor* received multiple updates, including UI redesigns, accessibility features, and new configuration options, indicating ongoing refinement of the user settings experience.
Performance over time
ETV stacked by Growth, Maintenance and Fixes — 90-day moving average, normalized to ETV / month.
Average performance per developer
ETV per active developer per month — 30-day moving average.
Active developers over time
Unique developers committing each day — 90-day moving average.
Knowledge concentration
How dependent is this repo on a small number of contributors? Higher top-1 share = higher key-person risk.
Dustin L. Howett owns 39.6 % of commits.
Top contributors
Most impactful commits
Top 20 by ETV in the all-time window.
- 2.4ETVRewrite media resource handling (relative path icons, web URLs) (#19143) This pull request broadly rewrites how we handle all media resources in the Terminal settings model. ## What is a media resource? A media resource is any JSON property that refers to a file on disk, including: - `icon` on profile - `backgroundImage` on profile (appearance) - `pixelShaderPath` and `pixelShaderImagePath` on profile (appearance) - `icon` on command and the new tab menu entries The last two bullet points were newly discovered during the course of this work. ## Description of Changes In every place the settings model used to store a string for a media path, it now stores an `IMediaResource`. A media resource must be _resolved_ before it's used. When resolved, it can report whether it is `Ok` (found, valid) and what the final normalized path was. This allows the settings model to apply some new behaviors. One of those new behaviors is resolving media paths _relative to the JSON file that referred to them._ This means fragments and user settings can now contain _local_ images, pixel shaders and more and refer to them by filename. Relative path support requires us to track the path from which every media resource "container" was read[^2]. For "big" objects like Profile, we track it directly in the object and for each layer. This means that fragments **updating** a profile pass their relative base path into the mix. For some of the entries such as those in `newTabMenu`, we just wing it (#19191). For everything that is recursively owned by a parent that has a path (say each Command inside an ActionMap), we pass it in from the parent during media resolution. During resolution, we now track _exactly which layer_ an icon, background image, or pixel shader path came from and read the "base path" from only that layer. The base path is not inherited. Another new behavior is in the handling of web and other URLs. Canonical and a few other WSL distributors had to resort to web URLs for icons because we did not support loading them from the package. Julia tried to use `ms-appx://JuliaPackageNameHere/path/to/icon` for the same reason. Neither was intended, and of the two the second _should_ have worked but never could[^1]. For both `http(s?)` URLs and `ms-appx://` URLs which specify a package name, we now strip everything except the filename. As an example... If my fragment specifies `https://example.net/assets/foo.ico`, and my fragment was loaded from `C:\Fragments`, Terminal will look *only* at `C:\Fragments\foo.ico`. This works today for Julia (they put their icon in the fragment folder hoping that one day we would support this.) It will require some work from existing WSL distributors. I'm told that this is similar to how XML schema documents work. Now, icons are special. They support _Emoji_ and _Segoe Icons_. This PR adds an early pass to avoid resolving anything that looks like an emoji. This PR intentionally expands the heuristic definition of an emoji. It used to only cover 1-2 code unit emoji, which prevented the use of any emoji more complicated than "man in business suite levitating." An icon path will now be considered an emoji or symbol icon if it is composed of a single grapheme cluster (as measured by ICU.) This is not perfect, as it errs on the side of allowing too many things... but each of those things is technically a single grapheme cluster and is a perfectly legal FontIcon ;) Profile icons are _even more special_ than icons. They have an additional fallback behavior which we had to preserve. When a profile icon fails validation, or is expressly set to `null`, we fall back to the EXE specified in the command line. Because we do this fallback during resolution, _and the icon may be inherited by any higher profile,_ we can only resolve it against the commandline at the same level as the failed or nulled icon. Therefore, if you specify `icon: null` in your `defaults` profile, it will only ever resolve to `cmd.exe` for any profile that inherits it (unless you change `defaults.commandline`). This change expands support for the magic keywords `desktopWallpaper` and `none` to all media paths (yes, even `pixelShaderPath`... but also, `pixelShaderImagePath`!) It also expands support for _environment variables_ to all of those places. Yes, we had like forty different handlers for different types of string path. They are now uniform. ## Resource Validation Media resources which are not found are "rejected". If a rejected resource lives in _user_ settings, we will generate a warning and display it. In the future, we could detect this in the Settings UI and display a warning inline. ## Surprises I learned that `Windows.Foundation.Uri` parses file paths into `file://` URIs, but does not offer you a way to get the original file path back out. If you pass `C:\hello world`, _`Uri.Path`_ will return `/C:/hello%20world`. I kid you not. As a workaround, we bail out of URL handling if the `:` is too close to the start (indicating an absolute file path). ## Testing I added a narow test hook in the media resource resolver, which is removed completely by link-time code generation. It is a real joy. The test cases are all new and hopefully comprehensive. Closes #19075 Closes #16295 Closes #10359 (except it doesn't support fonts) Supersedes #16949 somewhat (`WT_SETTINGS_DIR`) Refs #18679 Refs #19215 (future work) Refs #19201 (future work) Refs #19191 (future work) [^1]: Handling a `ms-appx` path requires us to _add their package to our dependency graph_ for the entire duration during which the resource will be used. For us, that could be any time (like opening the command palette for the first time!) [^2]: We don't bother tracking where the defaults came from, because we control everything about them.Dustin L. Howett · c0f9a198 · 2025-08-05
- 2.1ETVMove all blink handling into Renderer (#19330) This PR moves the cursor blinker and VT blink rendition timer into `Renderer`. To do so, this PR introduces a generic timer system with which you can schedule arbitrary timer jobs. Thanks to this, this PR removes a crapton of code, particularly throughout conhost. ## Validation Steps Performed * Focus/unfocus starts/stops blinking ✅ * OS-wide blink settings apply on focus ✅Leonard Hecker · 2e78665e · 2025-11-11
- 2.0ETVAdd an Extensions page to the Settings UI (#18559) This pull request adds an Extensions page to the Settings UI, which lets you enable/disable extensions and see how they affect your settings (i.e. adding/modifying profiles and adding color schemes). This page is specifically designed for fragment extensions and dynamic profile generators, but can be expanded on in the future as we develop a more advanced extensions model. App extensions extract the name and icon from the extension package and display it in the UI. Dynamic profile generators extract the name and icon from the generator and display it in the UI. We prefer to use the display name for breadcrumbs when possible. A "NEW" badge was added to the Extensions page's `NavigationViewItem` to highlight that it's new. It goes away once the user visits it. ## Detailed Description of the Pull Request / Additional comments - Settings Model changes: - `FragmentSettings` represents a parsed json fragment extension. - `FragmentProfileEntry` and `FragmentColorSchemeEntry` are used to track profiles and color schemes added/modified - `ExtensionPackage` bundles the `FragmentSettings` together. This is how we represent multiple JSON files in one extension. - `IDynamicProfileGenerator` exposes a `DisplayName` and `Icon` - `ExtensionPackage`s created from app extensions extract the `DisplayName` and `Icon` from the extension - `ApplicationState` is used to track which badges have been dismissed and prevent them from appearing again - a `std::unordered_set` is used to keep track of the dismissed badges, but we only expose a get and append function via the IDL to interact with it - Editor changes - view models: - `ExtensionsViewModel` operates as the main view model for the page. - `FragmentProfileViewModel` and `FragmentColorSchemeViewModel` are used to reference specific components of fragments. They also provide support for navigating to the linked profile or color scheme via the settings UI! - `ExtensionPackageViewModel` is a VM for a group of extensions exposed by a single source. This is mainly needed because a single source can have multiple JSON fragments in it. This is used for the navigators on the main page. Can be extended to provide additional information (i.e. package logo, package name, etc.) - `CurrentExtensionPackage` is used to track which extension package is currently in view, if applicable (similar to how the new tab menu page works) - Editor changes - views: - `Extensions.xaml` uses _a lot_ of data templates. These are reused in `ItemsControl`s to display extension components. - `ExtensionPackageTemplateSelector` is used to display `ExtensionPackage`s with metadata vs simple ones that just have a source (i.e. Git) - Added a `NewInfoBadge` style that is just an InfoBadge with "New" in it instead of a number or an icon. Based on https://github.com/microsoft/PowerToys/pull/36939 - The visibility is bound to a `get` call to the `ApplicationState` conducted via the `ExtensionsPageViewModel`. The VM is also responsible for updating the state. - Lazy loading extension objects - Since most instances of Terminal won't actually open the settings UI, it doesn't make sense to create all the extension objects upon startup. Instead, we defer creating those objects until the user actually navigates to the Extensions page. This is most of the work that happened in `CascadiaSettingsSerialization.cpp`. The `SettingsLoader` can be used specifically to load and create the extension objects. ## Validation Steps ✅ Keyboard navigation feels right ✅ Screen reader reads all info on screen properly ✅ Accessibility Insights FastPass found no issues ✅ "Discard changes" retains subpage, but removes any changes ✅ Extensions page nav item displays a badge if page hasn't been visited ✅ The badge is dismissed when the user visits the page ## Follow-ups - Streamline a process for adding extensions from the new page - Long-term, we can reuse the InfoBadge system and make the following minor changes: - `SettingContainer`: display the badge and add logic to read/write `ApplicationState` appropriately (similarly to above) - `XPageViewModel`: - count all the badges that will be displayed and expose/bind that to `InfoBadge.Value` - If a whole page is new, we can just style the badge using the `NewInfoBadge` styleCarlos Zamora · e332c67f · 2025-05-28
- 1.7ETVImplement search in Settings UI (#19519) ## Summary of the Pull Request Adds search functionality to the settings UI. This is added to an `AutoSuggestBox` in the main `NavigationView`. Invoking a result navigates to the proper location in the settings UI and focuses the setting, when possible. ## References and Relevant Issues Based on https://github.com/microsoft/PowerToys/pull/41285 ## Detailed Description of the Pull Request / Additional comments - tools/GenerateSettingsIndex.ps1: parses all the XAML files in the settings UI for SettingsContainers and builds a search index from them - XAML changes: ensures all SettingContainer objects have an `x:Name` so that we can navigate to them and bring them into view. - TerminalSettingsEditor/Utils.h: implements `BringIntoViewWhenLoaded()` which navigates to the relevant part of the UI. This is called in `OnNavigatedTo()` for each page. - fzf was moved out of TerminalApp so that TerminalSettingsEditor can access it - There's a few main components to searching, all of it is in `MainPage`: - `MainPage::_UpdateSearchIndex()`|`SearchIndex::Reset()`: loads the search index generated by `GenerateSettingsIndex.ps1`; provides additional localization, if needed - `MainPage::SettingsSearchBox_TextChanged`: - detect that text changed in the search box - perform the actual search in `SearchIndex::SearchAsync()`. This is a HEFTY async function that can be cancelled. It needs a lot of context passed in to expand the search index appropriately (i.e. build awareness of "PowerShell" profile and generate results appropriately). This is also where fzf is used to perform weighted matching. - the weighted matching itself is pretty complicated, but all the associated bonus weights are at the top of SearchIndex.cpp. - `SettingsSearchBox_QuerySubmitted`: extract the search index metadata and call the correct `_Navigate()` function ## Validation Steps Performed Search for... - settings that don't change at runtime: - [x] global settings - [x] settings in profile.defaults - [x] "add new profile" page - settings that may change at runtime: - [x] settings in a profile - [x] individual color schemes - [x] actions (main actions page + edit action subpage) - [x] new tab menu folders - [x] extensions - misc. corner cases: - [x] terminal chat (blocked in indexing script; requires minor changes in feature branch) - [x] settings in appearance objects To test fzf matching and weighted results, I specifically tested these scenarios: - "PowerShell" --> prioritize the PowerShell profile page(s) - "font size" --> prioritize profile defaults entry - "font size powershell" --> prioritize PowerShell > font size ## PR Checklist Closes #12949 ## Follow-ups - search by JSON key: need a way to add JSON keys to index entries. `GetSearchableFields()` should make the rest pretty easy. - search by keywords: need to define keywords. `GetSearchableFields()` should make the rest pretty easy.Carlos Zamora · f20c549d · 2026-02-20
- 1.6ETVFix a shutdown race condition in ControlCore (#18632) I found multiple issues while investigating this: * Render thread shutdown is racy, because it doesn't actually stop the render thread. * Lifetime management in `ControlCore` failed to account for the circular dependency of render thread --> renderer --> render data --> terminal --> renderer --> render thread. Fixed by reordering the `ControlCore` members to ensure their correct destruction. * Ensured that the connection setter calls close on the previous connection. (Hopefully) Closes #18598 ## Validation Steps Performed * Can't repro the original failure ❌ * Opening and closing tabs as fast as possible doesn't crash anymore ✅ * Detaching and reattaching a tab producing continuous output ✅Leonard Hecker · 70f85a4a · 2025-03-14
- 1.6ETVImplement the Kitty Keyboard Protocol (#19817) This essentially rewrites `TerminalInput` from scratch. There's significant overlap between what kind of information the Kitty protocol needs from the OS and our existing code. The rewrite allows us to share large parts of the implementation. Closes #11509 ## Validation Steps Performed * `kitten show-key -m kitty` ✅ * US Intern. " + ' produces `\'` ✅ * Hebrew base keys produce Unicode ✅ * Hebrew AltGr combinations produce Unicode ✅ * French AltGr+Space produces U+00A0 ✅ * German AltGr+Decimals produce []{}... ✅Leonard Hecker · c81c66b4 · 2026-02-17
- 1.5ETVRemove TerminalSettings from the TerminalSettingsModel project (#19262) The idea with IControlSettings (and friends) was always that a consumer of the terminal control could implement it in whatever way they pleased. Windows Terminal (the application) was intended to be only one consumer. It has a whole JSON settings model. Nobody wants to think about JSON at the Terminal Control level. We could have an "adapter" in TerminalApp, which spoke Terminal JSON Settings on one side and Terminal Control on the other side. That worked until we added the settings editor. The settings editor needed to display a control, and that control's settings needed to be based on the JSON settings. Oops. We took the expedient route of moving the adapter into TerminalSettingsModel itself, and poking a bunch of holes in it so that TerminalApp and TerminalSettingsEditor could tweak it as needed. Later, we doubled down on the control settings interface by having every Terminal Control _make its own ControlSettings_ when we were going to do the multi-process model. This reduced the number of IPC round trips for every settings query to 0. Later we built color scheme previewing on top of that--adding structs to carry color schemes and stuff which was already in the Appearance config. Sheesh. Layers and layers and layers. This pull request moves it back into its own library and strips it from the surface of TerminalSettingsModel. It also deletes `ControlSettings` and `struct CoreScheme`. That library is called `TerminalSettingsAppAdapterLib`, and it contains a hidden WinRT _implements_ type rather than a full-fledged activatable `runtimeclass`. It also implements one-level inheritance on its own rather than using IInheritable. It adheres to the following principles: - The control will never modify its settings in a way that is visible to the control's consumer; therefore, none of the properties have setters - The settings should never contain things of interest only to the Application that the Application uses to communicate data _back to itself_ (see `ProfileName`, removed in 68b723c and `KeyBindings`, removed in fa09141). This generalizes to "we should never store stuff in an unrelated object passed between layers solely for the purpose of getting it back". I made a few changes to the settings interface, including introducing a new `ICoreScheme` interface that _only_ contains color scheme info. This is designed to support the Preview/Set color scheme actions, which no longer work by _app backing up the scheme and restoring it later._ All of that machinery lives inside TermControl/ControlCore now. `ICoreScheme` no longer supports `GetColorAtIndex`; you must read all 16 colors at the same time. I am not sorry. Every consumer did that already, so now we have 15 fewer COM calls for every color scheme. The new TerminalSettings is mostly consumed via `com_ptr<TerminalSettings>`, so a bunch of `.` (projected) accesses had to turn into `->` (com_ptr dereferencing) accesses. I also realized, in the course of this work, that the old TerminalSettings contained a partial hand-written reimplementation of _every setting_ in `ControlProperties`. Every contributor had to add every new setting to both places--why? I can't figure it out. I'm using ControlProperties comprehensively now. I propagated any setting whose default value was different from that in ControlProperties back to ControlProperties. This is part X in a series of pull requests that will remove all mention of Microsoft.Terminal.Control and Microsoft.Terminal.Core from the settings model. Once that is done, the settings model can consume _only_ the base WinRT types and build very early and test more easily. Previewing is fun. I introduced a new place to stash an entire color table on ControlCore, which we use to save the "active" colors while we temporarily overwrite them. SetColorScheme is _also_ fun. We now have a slot for overriding only the focused color scheme on ControlCore. It's fine. It's clearer than "back up the focused appearance, overwrite the focused appearance, create a child of the user's settings and apply the color scheme to it, etc.". There is a bug/design choice in color scheme overriding, which may or may not matter: overlaying a color scheme on a terminal with an unfocused appearance which _does not_ have its own color scheme will result in the previously-deleted overridden focused color scheme peeking through when the terminal is not focused. I also got rid of our only in-product use of `Terminal::CreateFromSettings` which required us to set `InitialRows` and `InitialCols` on the incoming settings object (see core tenet 2). Refs #19261 Refs #19314 Refs #19254Dustin L. Howett · 52e60b95 · 2025-09-03
- 1.3ETVAdd support for "workspaces" based on window names (#20162) This adds a new feature to the Windows Terminal: "Workspaces" Workspaces are very shamelessly inspired by Edge workspaces of the same name. The core idea is that when users name a window and they close that window, we will persist that Windows layout and buffers, seperately from the rest of window restoration. So a user can open a named window, open some profiles, some panes, do some stuff in it, then close it, and we will keep that state around for the next time the user opens that window name. Unnamed windows still behave the same. If you close an unnamed window, and it's not the last window, then we won't persist the state of it. To facilitate restoring named windows, we add a `openWorkspace` action. This allows us to persist the open workspace action in the window layout restoration path. So when we deserialize the list of tab layouts, and open workspace action will tell us, hey, go retrieve this known workspace from the state.json, instead of trying to serialize the window state in two places. As demoed in the video, we add a flyout to list the windows that the user has open, and the named workspaces that they have saved. This allows users to quickly reopen previously closed workspaces, as well as quickly rename a window, thereby adding it to the list of saved workspaces. This button can also be hidden using the theme settings. Closes #17084Mike Griese · 4b8b6ffc · 2026-06-04
- 1.3ETVRewrite the MSAA/UIA integration into conhost (#19344) Goal: Remove `CursorBlinker`. Problem: Spooky action at a distance via `Cursor::HasMoved`. Solution: Moved all the a11y event raising into `_stream.cpp` and pray for the best. Goal: Prevent node.js from tanking conhost performance via MSAA (WHY). Problem: `ServiceLocator`. Solution: Unserviced the locator. Debounced event raising. Performance increased by >10x. Problem 2: Lots of files changed. This PR is a prerequisite for #19330 ## Validation Steps Performed Ran NVDA with and without UIA enabled and with different delays. ✅Leonard Hecker · 4600c479 · 2025-09-22
- 1.0ETVFilter Command Palette in English in addition to local language (#19166) The bulk of this work is changing `Command::Name` (and its descendants like `GenerateName`) to support looking up names in English and in the local language. When matching a "palette item" with a "subtitle" (a new field introduced to store the English command name when the current language is not English), the weight of the subtitle is used only if it is greater than the weight of the name. This ensures that we do not penalize or over-promote results that contain similar Latin letters in both fields. Refs #19130, #19131, #19132, #19165 Closes #7039Dustin L. Howett · 65788d90 · 2025-07-28
- 0.9ETVMerge TabBase+TerminalTab into just Tab (#19136) This removes the need to construct two objects per tab (TabBase, Actual Tab) and the other overhead inherent in WinRT composition-based inheritance. Important renames: - `GetTerminalTabImpl` -> `GetTabImpl` This pull request does not rename `TerminalTabStatus`; that is left as work for a future PR. Closes #17529Dustin L. Howett · f22295ef · 2025-07-22
- 0.9ETVMake selection an exclusive range (#18106) Selection is generally stored as an inclusive start and end. This PR makes the end exclusive which now allows degenerate selections, namely in mark mode. This also modifies mouse selection to round to the nearest cell boundary (see #5099) and improves word boundaries to be a bit more modern and make sense for degenerate selections (similar to #15787). Closes #5099 Closes #13447 Closes #17892 ## Detailed Description of the Pull Request / Additional comments - Buffer, Viewport, and Point - Introduced a few new functions here to find word boundaries, delimiter class runs, and glyph boundaries. - 📝These new functions should be able to replace a few other functions (i.e. `GetWordStart` --> `GetWordStart2`). That migration is going to be a part of #4423 to reduce the risk of breaking UIA. - Viewport: added a few functions to handle navigating the _exclusive_ bounds (namely allowing RightExclusive as a position for buffer coordinates). This is important for selection to be able to highlight the entire line. - 📝`BottomInclusiveRightExclusive()` will replace `EndExclusive` in the UIA code - Point: `iterate_rows_exclusive` is similar to `iterate_rows`, except it has handling for RightExclusive - Renderer - Use `iterate_rows_exclusive` for proper handling (this actually fixed a lot of our issues) - Remove some workarounds in `_drawHighlighted` (this is a boundary where we got inclusive coords and made them exclusive, but now we don't need that!) - Terminal - fix selection marker rendering - `_ConvertToBufferCell()`: add a param to allow for RightExclusive or clamp it to RightInclusive (original behavior). Both are useful! - Use new `GetWordStart2` and `GetWordEnd2` to improve word boundaries and make them feel right now that the selection an exclusive range. - Convert a few `IsInBounds` --> `IsInExclusiveBounds` for safety and correctness - Add `TriggerSelection` to `SelectNewRegion` - 📝 We normally called `TriggerSelection` in a different layer, but it turns out, UIA's `Select` function wouldn't actually update the renderer. Whoops! This fixes that. - TermControl - `_getTerminalPosition` now has a new param to round to the nearest cell (see #5099) - UIA - `TermControlUIAProvider::GetSelectionRange` no need to convert from inclusive range to exclusive range anymore! - `TextBuffer::GetPlainText` now works on an exclusive range, so no need to convert the range anymore! ## Validation Steps Performed This fundamental change impacts a lot of scenarios: - ✅Rendering selections - ✅Selection markers - ✅Copy text - ✅Session restore - ✅Mark mode navigation (i.e. character, word, line, buffer) - ✅Mouse selection (i.e. click+drag, shift+click, multi-click, alt+click) - ✅Hyperlinks (interaction and rendering) - ✅Accessibility (i.e. get selection, movement, text extraction, selecting text) - [ ] Prev/Next Command/Output (untested) - ✅Unit tests ## Follow-ups - Refs #4423 - Now that selection and UIA are both exclusive ranges, it should be a lot easier to deduplicate code between selection and UIA. We should be able to remove `EndExclusive` as well when we do that. This'll also be an opportunity to modernize that code and use more `til` classes.Carlos Zamora · 64d4fbab · 2025-01-28
- 0.8ETVFetch the terminal cursor position after a resize (#19535) Closes #18725 ## Validation Steps Performed Functionality was observed under a debugger while using PowerShell 5.Leonard Hecker · 898b9e3c · 2025-11-18
- 0.8ETVUpdate 'restart connection' action to reset internal state (#19971) ## Summary of the Pull Request I was becoming frustrated with getting in a state where a CLI app (i.e. Copilot CLI) enters some VT state (like mouse mode) then doesn't unset it when it accidentally exits. I normally use "Restart connection" to keep the buffer and connect again. Problem is, "restart connection" didn't actually reset the internal state! So I would type and get a bunch of lingering VT escape sequences. This bug is tracked over in #18425 in a more generic way. This fixes that bug by doing the following: - update `ITermDispatch::HardReset()` -->`HardReset(bool erase)` - `erase=true` does what `HardReset()` already did - `erase=false` skips over clearing the screen and resetting the cursor position - expose `HardReset(false)` as `HardResetWithoutErase()` by piping it up through `Terminal` --> `ControlCore` --> `TermControl` - update the restart connection handler - `TerminalPage::_restartPaneConnection()` now calls `HardResetWithoutErase()` before setting the new connection - this is also the same route for the "Enter to restart connection" scenario (text displayed when connection ends) Relevant notes from PR discussion: - `PSEUDOCONSOLE_INHERIT_CURSOR` is passed into the new connection via `_restartPaneConnection()` --> `_duplicateConnectionForRestart()` --> `_CreateConnectionFromSettings(profile, *controlSettings.DefaultSettings(), true)` ## Validation Steps Performed - Used this to enter mouse tracking mode: `Write-Host -NoNewline "`e[?1003h`e[?1006h"` - mouse selection doesn't work as usual (expected) - invoke "restart connection" action - mouse selection works as usual Closes #18425Carlos Zamora · ec939aab · 2026-03-30
- 0.7ETVAdd IconPicker to New Tab Menu folders in SUI (#19591) ## Summary of the Pull Request This PR pulls out the icon picker used in Profiles_Base.xaml to be its own control. Then it's reused as a way to set an icon on folders in the new tab menu. As a part of pulling out the icon picker into its own control, some minor code-health polish was added (i.e. lazy loading icon types and built in icons). The new tab menu didn't have a `NavigateToPageArgs`, so I took the declaration from #19519 and moved it here. I chose to do that instead of creating a `NavigateToNewTabMenuArgs` since that's more strict and it would be removed as a part of #19519 anyways. Aside from that, the PR is pretty straightforward. ## References and Relevant Issues Part of #18281 ## Validation Steps Performed - Profile icon picker - [x] set none - [x] set built-in icon - [x] set emoji - [x] set file - [x] loads icons properly (for all of the scenarios above) - New tab menu folder icon picker - [x] set none - [x] set built-in icon - [x] set emoji - [x] set file - [x] loads icons properly (for all of the scenarios above)Carlos Zamora · a4498997 · 2026-02-02
- 0.7ETVRemove `startOnUserLogin` from the settings; use OS APIs only (#18530) Before we had a Settings UI, we added support for a setting called `startOnUserLogin`. It was a boolean, and on startup we would try to yeet the value of that setting into the Windows API responsible for registering us as a startup task. Unfortunately, we failed to take into account a few things. - Startup tasks can be independently controlled by the user in Windows Settings or by an enterprise using enterprise policy - This control is not limited to *disabling* the task; it also supports enabling it! Users could enable our startup task outside the settings file and we would never know it. We would load up, see that `startOnUserLogin` was `false`, and go disable the task again. :facepalm: Conversely, if the user disables our task outside the app _we can never enable it from inside the app._ If an enterprise has configured it either direction, we can't change it either. The best way forward is to remove it from our settings model and only ever interact with the Windows API. This pull request replaces `startOnUserLogin` with a rich settings experience that will reflect the current and final state of the task as configured through Windows. Terminal will enable it if it can and display a message if it can't. My first attempt at this PR (which you can read in the commit history) made us try harder to sync the state between the settings model and the OS; we would propagate the disabled state back to the user setting when the task was disabled in the OS or if we failed to enable it when the user asked for it. That was fragile and didn't support reporting the state in the settings UI, and it seems like it would be confusing for a setting to silently turn itself back off anyway... Closes #12564Dustin L. Howett · a46fac25 · 2025-02-20
- 0.7ETVFix WindowRoot memory leak in SUI (#19826) ## Summary of the Pull Request Fixes a memory leak for `IHostedInWindow` in the TerminalSettingsEditor. The memory leak occurs from `MainPage` creating an object that stores reference to back to `MainPage` as `IHostedInWindow`. With `IconPicker` specifically, the cycle would look like `MainPage` --> `NewTabMenu` --> `IconPicker` --> `MainPage`. I've audited uses of `IHostedInWindow` in the TerminalSettingsEditor and replaced them as weak references. I also checked areas that `WindowRoot()` was called and added a null-check for safety. ## Validation Steps Performed Verified `MainPage` and `NewTabMenu` dtors are called when the settings UI closes from... ✅ Launch page (base case - it doesn't have an `IHostedInWindow`) ✅ New Tab Menu pageCarlos Zamora · 27aae1f2 · 2026-02-03
- 0.7ETVMake SUI previews readable by screen readers (#18418) Fixes a few accessibility bugs in the SettingContainer previews. Main changes include: - `SettingContainer` was considered a separate UIA element from the inner expander. It's been marked as `AccessibilityView=Raw` to "remove" it from the UIA tree. - Added a `CurrentValueAccessibleName` property to the `SettingContainer` to expose the current value to the screen reader for `SettingContainer`s that have expanders. Non-expander `SetttingContainer`s already worked fine. - Applied `CurrentValueAccessibleName` to various settings throughout the settings UI for full coverage. Added a `CurrentValue` for the ones that were missing it. - Removed a redundant/hidden tab stop in `Icon` `Padding` was not updated since #18300 is handling that. This'll just automatically make it accessible. Font axes and features weren't updated to show previews, but I'm happy to do it if given a suggestion. Part of #18318 ## Details - `SettingContainer` updates: - `AccessibilityView = Raw` for `SettingContainer`s with expanders. This is because the expander itself is the one we care about. No need to have another layer of UIA objects saying it's a group. - Added a `CurrentValueAccessibleName` property - This specifically defines what should be read out by the screen reader, similar to `AutomationProperties.Name` - It updates automatically when `CurrentValue` changes. - It's applied on the inner `Expander`, if one exists. - The accessible name is constructed to be `"<Header>: <CurrentValueAccessibleName>"`. If `CurrentValueAccessibleName` isn't provided, we try to use the `CurrentValue` if it's a string. - Profile (and appearance) settings: - `Icon`'s value is now read out by a screen reader instead of staying silent. It'll read the icon path. - A redundant/hidden tab stop was removed from `Icon`. - `TabTitle` now displays/reads "None" if no tab title is set. - `ColorScheme` is now read out by a screen reader. - The color scheme overrides (i.e. `Foreground`, `Background`, `SelectionBackground`, and `CursorColor`) are now read out by a screen reader. Format is "#<hex value>". - `BackgroundImageAlignment` is now displayed and read out by a screen reader. - `LaunchSize` is now displayed and read out by a screen reader. Format is "Width x Height". ## Validation Steps Performed Tabbed through the settings UI with a screen reader. Each of these settings now reads out a preview.Carlos Zamora · 3e969d53 · 2025-01-22
- 0.6ETVFix panes being dropped when tearing off tabs (#18627) I don't actually know why this is happening, because it doesn't happen with startup actions specified in the settings file. In any case, it's fixed with more delays. Closes #18572 ## Validation Steps Performed * Create a tab with 2 panes * Tear it off into a new window * New window has 1 tab with 2 panes ✅Leonard Hecker · e1b28e72 · 2025-02-26
- 0.6ETVImplement custom text context menus to fix crashes (#18854) This works around a bug in WinUI where it creates a single context menu/flyout for text elements per thread, not per `XamlRoot`, similar to many other areas. Since the `XamlRoot` cannot change after creation, this means that once you've opened the flyout, you're locked into that window (= XAML root) forever. You can't open the flyout in another window and once you've closed that window, you can't open it anywhere at all. Closes #18599 ## Validation Steps Performed * Flies out right click in the * About dialog ✅ * Search dialog ✅ * Word delimiters setting ✅ * Launch size setting ✅ * Across two windows ✅Leonard Hecker · 076746a7 · 2025-05-14