Add support for "workspaces" based on window names#20162
Conversation
This adds a new feature to the Windows Terminal: "Workspaces"
Workspaces are very shamelessly inspired by Edge workspaces of the same name.
{{video here}}
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.
<details>
<summary>
state.json
</summary>
```jsonc
"persistedWindowLayouts" :
[
{
"initialPosition" : "910,462",
"initialSize" :
{
"height" : 623.20001220703125,
"width" : 934.4000244140625
},
"launchMode" : "default",
"tabLayout" :
[
{
"action" : "newTab",
"commandline" : "\"C:\\Program Files\\PowerShell\\7\\pwsh.exe\"",
"profile" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"sessionId" : "{09d3ef05-ba3d-44ed-a8f1-065d08a806a4}",
"startingDirectory" : "C:\\Users\\zadji",
"suppressApplicationTitle" : false,
"tabTitle" : "PowerShell"
},
]
},
{
"tabLayout" :
[
{
"action" : "openWorkspace",
"name" : "wsl"
}
]
}
],
"persistedWorkspaces" :
{
"wsl" :
{
"initialPosition" : "109,502",
"initialSize" :
{
"height" : 568,
"width" : 1184.800048828125
},
"launchMode" : "default",
"tabLayout" :
[
{
"action" : "newTab",
"commandline" : "ubuntu.exe",
"profile" : "{51855cb2-8cce-5362-8f54-464b92b32386}",
"sessionId" : "{a7f70c49-71ec-4ac4-9f3a-4884528a3fd6}",
"startingDirectory" : null,
"suppressApplicationTitle" : false,
"tabTitle" : "Ubuntu"
},
{
"action" : "renameWindow",
"name" : "wsl"
}
]
}
},
```
</details>
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.
This comment has been minimized.
This comment has been minimized.
carlos-zamora
left a comment
There was a problem hiding this comment.
Looks and feels great! Excited for this to land!
The bug below is blocking, for sure.
Also, the global setting should be added to the settings UI in the global appearance page.
Bug
Repro:
- Open Terminal
- Open a second tab and rename the 2 tabs to "Tab 1" and "Tab 2"
- Click Workspaces button > Name this window... --> "My Workspace"
- ctrl+shift+n --> open new terminal window
- (NOTE) at this point, the workspaces flyout show "#1: My Workspace" appropriately. State seems good!
- Close "My Workspace" window
- Click Workspaces button > "My Workspace"
Expected: "My Workspace" opens in the same state as in stage 5 above
Looks like the space in "My Workspace" caused some kind of parsing issue? The workspace is named "My" and the first tab is named "Workspace".
Clicking on the Workspaces button in the other window shows this:
I think I have a separate comment that found the bug over here: https://github.com/microsoft/terminal/pull/20162/changes#r3352221900
| rem run clang-format on c++ files | ||
|
|
||
| powershell -noprofile "import-module %OPENCON_TOOLS%\openconsole.psm1; Invoke-CodeFormat" | ||
| pwsh -noprofile -c "import-module %OPENCON_TOOLS%\openconsole.psm1; Invoke-CodeFormat" |
There was a problem hiding this comment.
So @zadjii-msft did we merge with these unrelated changes in?
| ) | ||
|
|
||
| $clangFormatPath = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -find "**\x64\bin\clang-format.exe" | ||
| $clangFormatPath = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -prerelease -find "**\x64\bin\clang-format.exe" |
| // Right-click to delete: attach a context flyout with a | ||
| // "Delete workspace?" item that opens a confirmation dialog. |
There was a problem hiding this comment.
Love this functionality. Found it by total accident. Wonder if there's a way to make this more discoverable. Noticed we do something similar with "run as administrator" in the new tab menu.
There was a problem hiding this comment.
Lemme tell you, winui menuflyouts suck. I tried a bunch of different things to add like, a ... button to this flyout and nothing looked remotely good. That's just not a pattern that's used in WinUI.
CmdPal did do something similar, but to do that we basically built our own menuflyout manually, and I'm not keen to try and port all that code to C++/winrt 😔
| // Right-click to delete: attach a context flyout with a | ||
| // "Delete workspace?" item that opens a confirmation dialog. |
There was a problem hiding this comment.
Separately, is there a way to do this with the keyboard? Only workaround I can think of is renaming the current workspace to an empty string.
There was a problem hiding this comment.
In this PR, no. I didn't have a clever solution for that. I figured we'd get it in, feel it out, and figure out what we think feels right after playing with it more.
carlos-zamora
left a comment
There was a problem hiding this comment.
Nice work! Excited to see this land! 😊
Extracted from PR bodyDemowt-workspaces-000.mp4state.json "persistedWindowLayouts" :
[
{
"initialPosition" : "910,462",
"initialSize" :
{
"height" : 623.20001220703125,
"width" : 934.4000244140625
},
"launchMode" : "default",
"tabLayout" :
[
{
"action" : "newTab",
"commandline" : "\"C:\\Program Files\\PowerShell\\7\\pwsh.exe\"",
"profile" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"sessionId" : "{09d3ef05-ba3d-44ed-a8f1-065d08a806a4}",
"startingDirectory" : "C:\\Users\\zadji",
"suppressApplicationTitle" : false,
"tabTitle" : "PowerShell"
},
]
},
{
"tabLayout" :
[
{
"action" : "openWorkspace",
"name" : "wsl"
}
]
}
],
"persistedWorkspaces" :
{
"wsl" :
{
"initialPosition" : "109,502",
"initialSize" :
{
"height" : 568,
"width" : 1184.800048828125
},
"launchMode" : "default",
"tabLayout" :
[
{
"action" : "newTab",
"commandline" : "ubuntu.exe",
"profile" : "{51855cb2-8cce-5362-8f54-464b92b32386}",
"sessionId" : "{a7f70c49-71ec-4ac4-9f3a-4884528a3fd6}",
"startingDirectory" : null,
"suppressApplicationTitle" : false,
"tabTitle" : "Ubuntu"
},
{
"action" : "renameWindow",
"name" : "wsl"
}
]
}
}, |
DHowett
left a comment
There was a problem hiding this comment.
@zadjii-msft I would have blocked this PR had I read it in time. I think we might should revert it, even, until the wt.exe thing is fixed!
| seInfo.lpFile = exePath.c_str(); | ||
| seInfo.lpParameters = cmdline.c_str(); | ||
| seInfo.nShow = SW_SHOWNORMAL; | ||
| LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo)); |
There was a problem hiding this comment.
Why are doing this to ourselves? We are literally calling from inside the process, and we are spawning wt TO SPAWN WindowsTerminal to send a commandline back to ourselves. Why.
This is worse than "we serialize the tab to JSON to tell the other part of the app to launch it anew". Why.
Why.
There was a problem hiding this comment.
this is literally what openWindow already does literally 20 lines above this. I copypasted that code cause it just worked.
We can refactor that one too... but it seemed like the parity between the two implementations was the easiest path.
There was a problem hiding this comment.
Fair enough. Do we want to re-open this PR then? Sorry for the back and forth.
There was a problem hiding this comment.
Nope, we're working on the correct solution
…20291) This reverts commit 4b8b6ff: Add support for "workspaces" based on window names (#20162) @DHowett found the following blocking issues: - #20162 (comment) - unrelated changes in `tools/runformat.cmd` and `OpenConsole.psm1`
From ye old days where we were a multi-process app, we would `shellexecute` to create a new Terminal process when we wanted to open a new window. Now that we're all one process (#18215), this dance doesn't make sense anymore. So: let's just add a bunch of plumbing to take the IContentArgs up to the emperor and have it hand them back to a new window. (this will be used to resurrect #20162)
This is a pair of changes to make the code formatting scripts work, circa 2026. * If you only have VS Insiders installed, then vswhere can't actually find a clang-format to build with. You need `-prerelease` to let it also find preview builds of VS. * Years ago we updated OpenConsole.psm1 to only support pwsh 7. I, of course, use `cmd` as my dev env for terminal, so I use `runformat.cmd`, which always shelled to `powershell` (5). This updates that tool to use pwsh 7. Clearly this impacted only me ever. related to #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 #17084 (this is a reboot of #20162, but with the event from #20311)


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
openWorkspaceaction. 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 #17084