Compare commits

..

26 Commits

Author SHA1 Message Date
Dustin L. Howett
56a2f32d66 Migrate spelling-0.0.21 changes from main 2020-08-17 20:24:00 -04:00
Dustin L. Howett
2a43c20546 Migrate spelling-0.0.19 changes from main 2020-08-17 20:24:00 -04:00
Jorge Cabot
b58f60117e Add startOnUserLogin & fullscreen launchMode to schema (#7300)
Fixes #7294

Co-authored-by: Mike Griese <migrie@microsoft.com>
(cherry picked from commit 8943f68d4d)
2020-08-17 17:24:46 -07:00
Dustin Howett
eff3e93d79 Fix the botched cherry-pick in the previous commit 2020-08-10 19:05:10 -07:00
Dustin L. Howett
e354313dc4 Resolve the default profile during defaults load, don't crash on launch (#7237)
The "default profile as name" feature in 1.1 broke the loading of
default settings, as we would never get to the validation phase where
the default profile string was transformed into a guid.

I moved knowledge of the "unparsed default profile" optional to the
consumer so that we could make sure we only attempted to deserialize it
once (and only if it was present.)

Fixes #7236.

* [x] Closes #7236

(cherry picked from commit c03677b0c9)
2020-08-10 17:40:39 -07:00
Dustin L. Howett
b023fff281 wpf: fixup mouse wheel events from screen coords (#7168)
I found this while crawling through conhost's WindowIo. Mouse wheel
events come in in screen coordinates, unlike literally every other mouse
event.

The WPF control was doing it wrong.

(cherry picked from commit cd7235661e)
2020-08-04 11:21:52 -07:00
Dustin L. Howett
37e629c747 Fix VT mouse capture issues in Terminal and conhost (#7166)
This pull request fixes capture and event generation in VT mouse mode
for both conhost and terminal.

Fixes #6401.

[1/3] Terminal: clamp mouse events to the viewport, don't throw them away

 gnome-terminal (at least) sends mouse events whose x/y are at the
 extreme ends of the buffer when a drag starts inside the terminal and
 then exits it.

 We would previously discard any mouse events that exited the borders of
 the viewport. Now we will keep emitting events where X/Y=0/w/h.

[2/3] conhost: clamp VT mouse to viewport, capture pointer

 This is the same as (1), but for conhost. conhost wasn't already
 capturing the pointer when VT mouse mode was in use. By capturing, we
 ensure that events that happen outside the screen still result in events
 sent to an application (like a release after a drag)

[3/3] wpf: capture the pointer when VT mouse is enabled

 This is the same as (2), but for the WPF control. Clamping is handled
 in TerminalCore in (1), so we didn't need to do it in WPF.

(cherry picked from commit d29be591a8)
2020-08-04 11:21:45 -07:00
Dustin L. Howett
4a03c20323 Send ENHANCED_KEY in Win32 input mode in the wpf/uwp controls (#7106)
When we added support for win32 input mode, we neglected to pass
`ENHANCED_KEY` through the two surfaces that would generate events. This
broke arrow keys in much the same way was #2397, but in a different
layer.

While I was working on the WPF control, I took a moment to refactor the
message cracking out into a helper. It's a lot easier on the eyes than
four lines of bit shifting repeated three times.

Fixes #7074

(cherry picked from commit dd0f7b701a)
2020-07-31 10:21:04 -07:00
Javier
87bc0004e8 Expose text selection through terminal WPF control API (#7121)
We've been trying to improve the copy/paste experience with the terminal
in Visual Studio. Once of our problematic scenarios is when the terminal
is connected to a remote environment and the user attempts to
copy/paste. This gets forwarded to the remote shell that copy/paste into
the remote clipboard instead of the local one.

So we opted to add ctrl+shift+c/v to the terminal and need access to the
selected text via the terminal control

VALIDATION
Tested with Visual Studio integrated terminal

(cherry picked from commit f486a6504c)
2020-07-31 10:17:48 -07:00
Dustin L. Howett
fda80eaf64 wpf: fix a handful of issues with the wpf control (#6983)
* send alt/F10 through the control
  We were not listening for WM_SYSKEY{UP,DOWN}
* extract the actual scancode during WM_CHAR, not the bitfield
  We were accidentally sending some of the additional keypress data in with
  the character event in Win32 Input Mode
* set default fg/bg to campbell
  The WPF control starts up in PowerShell blue even though it's not typically used
  in PowerShell blue.
* don't rely on the font to determine wideness
  This is a cross-port of #2928 to the WPF control
* deterministic shutdown
  In testing, I saw a handful of crashes on teardown because we were not shutting
  down the render thread properly.
* don't pass 10 for the font weight ...
  When Cascadia Code is set, it just looks silly.
* trigger render when selection is cleared, do it under lock

Fixes #6966.

(cherry picked from commit 76de2aedc2)
2020-07-20 16:14:06 -07:00
Carlos Zamora
889467c920 UIA: use full buffer comparison in rects and endpoint setter (#6447)
In UiaTextRange, `_getBufferSize` returns an optimized version of the
size of the buffer to be the origin and the last character in the
buffer. This is to improve performance on search or checking if you are
currently on the last word/line.

When setting the endpoint and drawing the bounding rectangles, we should
be retrieving the true buffer size. This is because it is still possible
to create UiaTextRanges that are outside of this optimized size. The
main source of this is `ExpandToEnclosingUnit()` when the unit is
`Document`. The end _should_ be the last visible character, but it isn't
because that would break our tests.

This is an incomplete solution. #6986 is a follow up to completely test
and implement the solution.

The crash in #6402 was caused by getting the document range (a range of
the full text buffer),  then moving the end by one character. When we
get the document range, we get the optimized size of the buffer (the
position of the last character). Moving by one character is valid
because the buffer still has more to explore. We then crash from
checking if the new position is valid on the **optimized size**, not the
**real size**.

REFERENCES

#6986 - follow up to properly handle/test this "end of buffer" problem

Closes #6402

(cherry picked from commit c390b61648)
2020-07-20 16:14:05 -07:00
Dustin L. Howett
cb52de02e7 Swap brightBlack/black in the Solarized color schemes (#6985)
Original notes from @M-Pixel:

> Console applications assume that backgrounds are black, and that
> `lightBlack`/`DarkGrey` are lighter than `black`/`Black`.  This
> assumption is accounted for by all color schemes in `defaults.json`,
> except for the Solarized themes.
>
> The Solarized Dark theme, in particular, makes `-Parameters` invisible
> against the background in PowerShell, which is obviously an unacceptable
> usability flaw.
>
> This change makes `black` and `background` to the same (which is common
> throughout the color schemes), and makes `brightBlack` (`DarkGray` in
> .NET) lighter than black (which is obviously more correct given the
> meanings of those words).

Out of the box, we ship a pretty bad behavior.

If I look at all of the existing shipped color schemes--and that
includes things like Tango and One Half--we are universally following a
`background` == `black` rule.

If I consult gnome-terminal or xterm, they do the same thing; Xterm by
default, gnome-terminal for solarized. The background generally matches
color index `0` across all their **dark** schemes. Konsole and
lxterminal disagree and map background to `0 intense` for Solarized.

I want to put our Solarized schemes on a deprecation path, but
unfortunately we still need to ship _something_ for users who we're
going to strand on them.

I'm going to have to swallow my bitter and say that yes, we should
probably just change the index mapping and go with something that works
right out of the box while we figure out how to do perceptual color
nudging and eventually remove bad defaults (like Solarized).

From #6618.

Fixes #4047.
Closes #6618.

(cherry picked from commit 04f5ee7ebf)
2020-07-20 13:51:18 -07:00
Dustin L. Howett
a95a3c427d Move to the TerminalDependencies NuGet feed (#6954)
After we stood up our own NuGet feed in Azure blob storage, Azure DevOps
came out with a public artifact feed feature. I've migrated all our
packages over, and the only thing left to do is switch our project's
NuGet config to use it.

Fixes #6952

(cherry picked from commit 03e25f12e9)
2020-07-20 13:48:55 -07:00
jtippet
20a60628bc Add High Contrast image assets (#6915)
This commit adds image assets for High Contrast mode

Tagging this issue so it contains a nice list of all the recent HC
fixes: #5360

I made several changes to DHowett's script and added it to the repo:
* Add support for generating high contrast icons
* Add the ability to easily edit the "intermediate" (previously "zbase")
  files for manual hinting
* Appease the spellchecker

I created new SVGs for HC mode. There's one SVG for both Black and White
modes -- I just invert the colors. Then I manually hinted the generated
bitmaps for the production icons. I didn't bother hinting the Dev/Pre
ones, so the text does get unreadable at small sizes.

View the original images in #6915.

Co-authored-by: Jeffrey Tippet <jtippet@microsoft.com>
Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
Closes #6822

(cherry picked from commit bd93cb5e8a)
2020-07-20 13:48:46 -07:00
Dustin L. Howett
91cfeb0b8b Update Cascadia Code to 2007.15 (#6958)
(cherry picked from commit bcbe246a93)
2020-07-16 18:17:26 -07:00
Dustin L. Howett
0b3aaf6a9c Account for WHEEL_DELTA when dispatching VT mouse wheel events (#6843)
By storing up the accumulated delta in the mouse input handler, we can
enlighten both conhost and terminal about wheel events that are less
than one line in size. Previously, we had a workaround in conhost that
clamped small scroll deltas to a whole line, which made trackpad
scrolling unimaginably fast. Terminal didn't make this mistake, but it
also didn't handle delta accumulation . . . which resulted in the same
behavior.

MouseInput will now wait until it's received WHEEL_DELTA (well-known
constant, value 120) worth of scrolling delta before it dispatches a
single scroll event.

Future considerations may include sending multiple wheel button events
for every *multiple* of WHEEL_DELTA, but that would be a slightly larger
refactoring that I'm not yet ready to undertake.

There's a chance that we should be dividing WHEEL_DELTA by the system's
"number of lines to scroll at once" setting, because on trackpads
conhost now scrolls a little _slow_. I think the only way to determine
whether this is palatable is to just ship it.

Fixes #6184.

(cherry picked from commit fc083296b9)
2020-07-16 18:17:26 -07:00
Michael Niksa
6b0b6be252 Avoid copying the bitmap on the way into the tracing function (#6839)
## PR Checklist
* [x] Closes perf itch.
* [x] I work here.
* [x] Manual perf test.
* [x] Documentation irrelevant.
* [x] Schema irrelevant.
* [x] Am core contributor.

## Detailed Description of the Pull Request / Additional comments
Passes the bitmap by ref into the tracing function instead of making a copy on the way in. It's only read anyway for tracing (if enabled) so the copy was a pointless oversight.

## Validation Steps Performed
- Observed WPR trace before and after with `time cat big.txt` in WSL.

(cherry picked from commit 99c33e084a)
2020-07-16 18:17:26 -07:00
jtippet
f370b0d8e4 Update colors of our custom NewTab button to match MUX's TabView (#6812)
Update colors of our custom NewTab button to match MUX's TabView button

MUX has a NewTab button, but Terminal uses a homemade lookalike.  The
version in Terminal doesn't use the same brush color resources as MUX's
button, so it looks very slightly different.  This PR updates Terminal's
button to use the exact same colors that MUX uses.  I literally copied
these brush names out of MUX source code.

## References
This is the color version of the layout fix #6766
This is a prerequisite for fixing #5360

## Detailed Description of the Pull Request / Additional comments
The real reason that this matters is that once you flip on
`ApplicationHighContrastAdjustment::None`, the existing colors will not
work at all.  The existing brushes are themed to black foreground on a
black background when High Contrast (HC) Black theme is enabled.  The
only thing that's saving you is
`ApplicationHighContrastAdjustment::Auto` is automatically backplating
the glyphs on the buttons, which (by design) hides the fact that the
colors are poor.  The backplates are those ugly squares inside the
buttons on the HC themes.

Before I can push a PR that disables automatic backplating (set
`ApplicationHighContrastAdjustment` to `None`), we'll need to select
better brushes that work in HC mode.  MUX has already selected brushes
that work great in all modes, so it just makes sense to use their
brushes.

The one very subtle difference here is that, for non-HC themes, the
glyph's foreground has a bit more contrast when the button is in
hovered/pressed states.  Again this slight difference hardly matters
now, but using the correct brushes will become critical when we try to
remove the HC backplating.

Closes #6812

(cherry picked from commit 4faa104f6a)
2020-07-16 18:17:26 -07:00
jtippet
0c8e5db388 Update the shape of our custom NewTab button to match MUX's TabView button (#6766)
The MUX TabView control has a uniquely-shaped [+] button.  TerminalApp
doesn't use it: instead, it has a SplitView button that is styled to
look like MUX's official button.  However, it doesn't get the button's
shape right.  This PR updates TerminalApp's custom button to look more
like MUX's.

The difference is that MUX only rounds the top two corners, and it uses
a bigger radius.  Without matching MUX's radius, the upper-left corner
of the button makes an awkward asymmetric divot with the abutting tab.
There's also a spot in the lower-left corner that just looks like
someone accidentally spilled a few pixels on the floor.

Current appearance before this PR:
![oldlight](https://user-images.githubusercontent.com/10259764/86410863-74ca5e80-bc70-11ea-8c15-4ae22998b209.png)

New appearance with this PR:
![newlight](https://user-images.githubusercontent.com/10259764/86410871-772cb880-bc70-11ea-972c-13332f1a1bdb.png)

Most important deltas highlighted with red circles:
![marklight](https://user-images.githubusercontent.com/10259764/86410877-78f67c00-bc70-11ea-8a6d-696cfbd89b1d.png)

Note that this PR does *not* attempt to fix the colors.  The colors are
also just slightly different from what MUX uses.  I'll save that for a
separate PR, since all those screenshots would clutter this up this PR.

(cherry picked from commit d350a89324)
2020-07-16 18:17:26 -07:00
James Holderness
950fac82e7 Don't abort early in VT reset operations if one of the steps fails (#6763)
The VT reset operations `RIS` and `DECSTR` are implemented as a series
of steps, each of which could potentially fail. Currently these
operations abort as soon as an error is detected, which is particularly
problematic in conpty mode, where some steps deliberately "fail" to
indicate that they need to be "passed through" to the conpty client. As
a result, the reset won't be fully executed. This PR changes that
behaviour, so the error state is recorded for any failures, but the
subsequent steps are still run.

Originally the structure of these operations was of the form:

    bool success = DoSomething();
    if (success)
    {
        success = DoSomethingElse();
    }

But I've now changed the code so it looks more like this:

    bool success = DoSomething();
    success = DoSomethingElse() && success;

This means that every one of the steps should execute, regardless of
whether previous steps were successful, but the final _success_ state
will only be true if none of the steps has failed.

While this is only really an issue in the conhost code, I've updated
both the `AdaptDispatch` and `TerminalDispatch` classes, since I thought
it would be best to have them in sync, and in general this seems like a
better way to handle multi-step operations anyway.

VALIDATION

I've manually tested the `RIS` escape sequence (`\ec`) in the Windows
Terminal, and confirmed that it now correctly resets the cursor
position, which it wasn't doing before.

Closes #6545

(cherry picked from commit 0651fcff14)
2020-07-06 21:38:00 -07:00
Antonio Garcia
d8421ba66a Add tooltip text to Color Buttons (#6498)
This commit adds tooltip text to every color button in the tab color
picker.

(cherry picked from commit 44e80d40b6)
2020-07-06 21:37:58 -07:00
greg904
fdc7bd5e0f Double-click a tab to rename it (#6628)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

When the user double clicks on a tab, show the tab rename box
as if they right clicked on the tab and clicked on "Rename".

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #6600
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

I added a handler for the `DoubleTapped` event on the tab view item
when we are constructing it for the tab (in `Tab::_MakeTabViewItem`).

The code for that handler was copied the "rename tab menu item" click
handler.

I did not extract the code into a member function because it is very
short (only 2 lines of code) and only used twice so it is not worth
it IMO.

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

(cherry picked from commit 073e732301)
2020-07-06 21:37:11 -07:00
Dustin L. Howett
3cc57a480e When we add a new tab in compact mode, re-enforce Compact mode (#6670)
This workaround was suggested by @chingucoding in
microsoft/microsoft-ui-xaml#2711

Fixes #6570

## References

microsoft/microsoft-ui-xaml#2711
#6570

## PR Checklist
* [x] Closes an issue
* [x] CLA
* [x] Tested
* [x] Docs not required
* [x] Schema not required

(cherry picked from commit fefd1408f2)
2020-06-29 17:58:16 -07:00
Dustin L. Howett
d2ae82dadf Update Cascadia Code to 2007.01 (#6721) 2020-06-29 17:57:56 -07:00
Dustin L. Howett
583ff5b389 Hardcode the paths to Windows PowerShell and CMD (#6684)
Occasionally, we get users with corrupt PATH environment variables: they
can't lauch PowerShell, because for some reason it's dropped off their
PATH. We also get users who have stray applications named
`powershell.exe` just lying around in random system directories.

We can combat both of these issues by simply hardcoding where we expect
PowerShell and CMD to live. %SystemRoot% was chosen over %WINDIR%
because apparently (according to Stack Overflow), SystemPath is
read-only and WINDIR isn't.

Refs #6039, #4390, #4228 (powershell was not found)
Refs #4682, Fixes #6082 (stray powershell.exe)

(cherry picked from commit 2fc1ef04ce)
2020-06-26 12:10:15 -07:00
Dustin L. Howett
98e050de87 Revert "Skip ... analysis when the ... text is simple (6206)" (#6665)
This reverts commit 94eab6e391.

We'll reintroduce this again after making sure it plays nicely with
recycling and box drawing glyphs.

Fixes #6488
Fixes #6664

(cherry picked from commit 653716fd8d15db105282eb0921135d87c6ff7e87)
2020-06-24 15:25:10 -07:00
545 changed files with 1861 additions and 5859 deletions

View File

@@ -9,8 +9,7 @@
* [ ] Closes #xxx
* [ ] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] Requires documentation to be updated
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->

View File

@@ -8,7 +8,7 @@
<!--<add key="Static Package Dependencies" value="dep\packages" />-->
<!-- Use our own NuGet Feed -->
<add key="Windows Terminal NuGet Feed" value="https://terminalnuget.blob.core.windows.net/feed/index.json" />
<add key="TerminalDependencies" value="https://pkgs.dev.azure.com/ms/terminal/_packaging/TerminalDependencies/nuget/v3/index.json" />
<!-- Internal NuGet feeds that may not be accessible outside Microsoft corporate network -->
<!--<add key="TAEF - internal" value="https://microsoft.pkgs.visualstudio.com/DefaultCollection/_packaging/Taef/nuget/v3/index.json" />

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
<VersionMajor>1</VersionMajor>
<VersionMinor>2</VersionMinor>
<VersionMinor>1</VersionMinor>
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -56,8 +56,6 @@
"switchToTab",
"toggleFullscreen",
"find",
"setTabColor",
"openTabColorPicker",
"unbound"
],
"type": "string"
@@ -259,22 +257,6 @@
}
]
},
"SetTabColorAction": {
"description": "Arguments corresponding to a Set Tab Color Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "setTabColor" },
"color": {
"$ref": "#/definitions/Color",
"default": null,
"description": "If provided, will set the tab's color to the given value. If omitted, will reset the tab's color."
}
}
}
]
},
"Keybinding": {
"additionalProperties": false,
"properties": {
@@ -290,7 +272,6 @@
{ "$ref": "#/definitions/ResizePaneAction" },
{ "$ref": "#/definitions/SplitPaneAction" },
{ "$ref": "#/definitions/OpenSettingsAction" },
{ "$ref": "#/definitions/SetTabColorAction" },
{ "type": "null" }
]
},
@@ -372,10 +353,16 @@
"minimum": 1,
"type": "integer"
},
"startOnUserLogin": {
"default": false,
"description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
"type": "boolean"
},
"launchMode": {
"default": "default",
"description": "Defines whether the Terminal will launch as maximized or not.",
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window.",
"enum": [
"fullscreen",
"maximized",
"default"
],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -1,227 +0,0 @@
---
author: Leon Liang @leonMSFT
created on: 2019-11-27
last updated: 2020-06-16
issue id: 1502
---
# Advanced Tab Switcher
## Abstract
Currently the user is able to cycle through tabs on the tab bar. However, this horizontal cycling can be pretty inconvenient when the tab titles are long or when there are too many tabs on the tab bar. It could also get hard to see all your available tabs if the tab titles are long and your screen is small. In addition, there's a common use case to quickly switch between two tabs, e.g. when one tab is used as reference and the other is the actively worked-on tab. If the tabs are not right next to each other on the tab bar, it could be difficult to quickly swap between the two. Having the tabs displayed in Most Recently Used (MRU) order would help with this problem. It could also make the user experience better when there are a handful of tabs that are frequently used, but are nowhere near each other on the tab bar.
Having a tab switcher UI, like the ones in Visual Studio and Visual Studio Code, could help with the tab experience. Presenting the tabs vertically in their own little UI allows the user to see more of the tabs at once, compared to scanning the tab row horizontally and scrolling left/right to find the tab you want. The tab order in those tab switchers are also in MRU order by default.
To try to alleviate some of these user scenarios, we want to create a tab switcher similar to the ones found in VSCode and VS. This spec will cover the design of the switcher, and how a user would interact with the switcher. It would be primarily keyboard driven, and would give a pop-up display of a vertical list of tabs. The tab switcher would also be able to display the tabs in Most Recently Used (MRU) order.
## Inspiration
This was mainly inspired by the tab switcher that's found in Visual Studio Code and Visual Studio.
VS Code's tab switcher appears directly underneath the tab bar.
![Visual Studio Code Tab Switcher](img/VSCodeTabSwitcher.png)
Visual Studio's tab switcher presents itself as a box in the middle of the editor.
![Visual Studio Tab Switcher](img/VSTabSwitcher.png)
In terms of navigating the switcher, both VSCode and Visual Studio behave very similarly. Both open with the press of <kbd>ctrl+tab</kbd> and dismiss on release of <kbd>ctrl</kbd>. They both also allow the user to select the tab with the mouse and with <kbd>enter</kbd>. <kbd>esc</kbd> and a mouse click outside of the switcher both dismiss the window as well.
I'm partial towards looking like VSCode's Tab Switcher - specifically because it seems like both their Command Palette and Tab Switcher use the same UI. You can observe this by first bringing up the command palette, then hitting the keybinding to bring up the tab switcher. You'll notice that they're both using the same centered drop-down from the tab row. In fact, hitting the Tab Switcher keybinding in VSCode while the Command Palette is open simply auto fills the search box with "edit active", signifying that the user wants to select one of the tabs to edit, effectively "swapping" to the tab that's highlighted.
Since Terminal now has a command palette, it would be amazing to reuse that UI and simply fill it with the names of a user's currently open tabs!
## Solution Design
To extend upon the command palette, we simply need to create and maintain two Vector<Commands>, where each command will simply dispatch a `SwitchToTab` `ShortcutAction`. One vector will have the commands in tab row order, and the other will be in MRU order. They'll both have to be maintained along with our existing vector of tabs.
These vectors of commands can then be set as the commands to pull from in the command palette, and as long as the tab titles are available in these commands, the command palette will be able to naturally filter through the tabs as a user types in its search bar. Just like the command palette, a user will be able to navigate through the list of tabs with the arrow keys and pointer interactions. As part of this implementation, I can supplement these actions with "tab switcher specific" navigation keybindings that would only work if the command palette is in tab switcher mode.
The `TabSwitcherControl` will use `TerminalPage`'s `ShortcutActionDispatch` to dispatch a `SwitchToTab` `ShortcutAction`. This will eventually cause `TerminalPage::_OnTabSelectionChanged` to be called. We can update the MRU in this function to be sure that changing tabs from the TabSwitcher, clicking on a tab, or nextTab/prevTab-ing will keep the MRU up-to-date. Adding or closing tabs are handled in `_OpenNewTab` and `_CloseFocusedTab`, which will need to be modified to update the command vectors.
## UI/UX Design
The Tab Switcher will reuse a lot of the XAML code that's used in the command palette. This means it'll show up as a drop-down from the horizontal center of the tab row. It'll appear as a single overlay over the whole Terminal window. There will also be a search box on top of the list of tabs. Here's a rough mockup of how the command palette/tab switcher looks like:
![Mockup Command Palette with Tab Titles](img/CommandPaletteExample.png)
Each entry in the list will show the tab's titles and their assigned number for quick switching, and only one line will be highlighted to signify the tab that is currently selected. The top 9 tabs in the list are numbered for quick switching, and the rest of the tabs will simply have an empty space where a number would be.
The list would look (roughly) like this:
```
1 foo (highlighted)
2 boo
3 Windows
4 /c/Users/booboo
5 Git Moo
6 shoo
7 /c/
8 /d/
9 /e/
/f/
/g/
/h/
```
The highlighted line can move up or down, and if the user moves up while the highlighted line is already at the top of the list, the highlight will wrap around to the bottom of the list. Similarly, it will wrap to the top if the highlight is at the bottom of the list and the user moves down.
If there's more tabs than the UI can display, the list of tabs will scroll up/down as the user keeps iterating up/down. Even if some of the numbered tabs (the first 9 tabs) are not visible, the user can still press any number 1 through 9 to quick switch to that tab.
To give an example of what happens after scrolling past the end, imagine a user is starting from the state in the mock above. The user then iterates down past the end of the visible list four times. The below mock shows the result.
```
5 Git Moo
6 shoo
7 /c/
8 /d
9 /e/
/f/
/g/
/h/
/i/
/j/
/k/
/l/ (highlighted)
```
The tabs designated by numbers 1 through 4 are no longer visible (but still quick-switchable), and the list now starts with "Git Moo", which is associated with number 5.
### Using the Switcher
#### Opening the Tab Switcher
The user can press a keybinding named `tabSwitcher` to bring up the command palette UI with a list of tab titles.
The user can also bring up the command palette first, and type a "tab switcher" prefix like "@" into the search bar to switch into "tab switcher mode".
The user will be able to change it to whatever they like.
There will also be an optional `anchor` arg that may be provided to this keybinding.
#### Keeping it open
We use the term `anchor` to illustrate the idea that the UI stays visible as long as something is "anchoring" it down.
Here's an example of how to set the `anchor` key in the settings:
```
{"keys": ["ctrl+tab"], "command": {"action": "openTabSwitcher", "anchor": "ctrl" }}
```
This user provided the `anchor` key arg, and set it to <kbd>ctrl</kbd>. So, the user would open the UI with <kbd>ctrl+tab</kbd>, and as long as the user is holding <kbd>ctrl</kbd> down, the UI won't dismiss. The moment the user releases <kbd>ctrl</kbd>, the UI dismisses. The `anchor` key needs to be one of the keys in the `openTabSwitcher` keybinding. If it isn't, we'll display a warning dialog in this case saying that the `anchor` key isn't actually part of the keybinding, and the user might run into some weird behavior.
If `openTabSwitcher` is not given an `anchor` key, the switcher will stay visible even after the release of the keybinding.
#### Switching through Tabs
The user will be able to navigate through the switcher with the following keybindings:
- Switching Down: <kbd>tab</kbd> or <kbd>downArrow</kbd>
- Switching Up: <kbd>shift+tab</kbd> or <kbd>upArrow</kbd>
As the user is cycling through the tab list, the selected tab will be highlighted but the terminal won't actually switch focus to the selected tab. This also applies to pointer interaction. Hovering over an item with a mouse will highlight the item but not switch to the tab.
#### Closing the Switcher and Bringing a Tab into Focus
There are two _dismissal_ keybindings:
1. <kbd>enter</kbd> : brings the currently selected tab into focus and dismisses the UI.
2. <kbd>esc</kbd> : dismisses the UI without changing tab focus.
The following are ways a user can dismiss the UI, _whether or not_ the `Anchor` key is provided to `openTabSwitcher`.
1. The user can press a number associated with a tab to instantly switch to the tab and dismiss the switcher.
2. The user can click on a tab to instantly switch to the tab and dismiss the switcher.
3. The user can click outside of the UI to dismiss the switcher without bringing the selected tab into focus.
4. The user can press any of the dismissal keybindings.
If the `anchor` key is provided, then in addition to the above methods, the UI will dismiss upon the release of the `anchor` key.
Pressing the `openTabSwitcher` keychord again will not close the switcher, it'll do nothing.
### Most Recently Used Order
We'll provide a setting that will allow the list of tabs to be presented in either _in-order_ (how the tabs are ordered on the tab bar), or _Most Recently Used Order_ (MRU). MRU means that the tab that the terminal most recently visited will be on the top of the list, and the tab that the terminal has not visited for the longest time will be on the bottom.
There will be an argument for the `openTabSwitcher` action called `displayOrder`. This can be either `inOrder` or `mruOrder`. Making the setting an argument passed into `openTabSwitcher` would allow the user to have one keybinding to open an MRU Tab Switcher, and different one for the In-Order Tab Switcher. For example:
```
{"keys": ["ctrl+tab"], "command": {"action": "openTabSwitcher", "anchor":"ctrl", "displayOrder":"mruOrder"}}
{"keys": ["ctrl+shift+p"], "command": {"action": "openTabSwitcher", "anchor":"ctrl", "displayOrder":"inOrder"}}
```
By default (when the arg isn't specified), `displayOrder` will be "mruOrder".
### Numbered Tabs
Similar to how the user can currently switch to a particular tab with a combination of keys such as <kbd>ctrl+shift+1</kbd>, we want to have the tab switcher provide a number to the first nine tabs (1-9) in the list for quick switching. If there are more than nine tabs in the list, then the rest of the tabs will not have a number assigned.
## Capabilities
### Accessibility
- The tab switcher will be using WinUI, and so it'll be automatically linked to the UIA tree. This allows screen readers to find it, and so narrator will be able to navigate the switcher easily.
- The UI is also fully keyboard-driven, with the option of using a mouse to interact with the UI.
- When the tab switcher pops up, the focus immediately swaps to it.
- For the sake of more contrast with the background, we could use a ThemeShadow to bring the UI closer to the user, making the focus clearer.
### Security
This shouldn't introduce any security issues.
### Reliability
How we're updating the MRU is something to watch out for since it triggers on a lot of tab interactions. However, I don't foresee the update taking long at all, and I can't imagine that users can create and delete tabs fast enough to matter.
### Compatibility
- The existing way of navigating horizontally through the tabs on the tab bar should not break.
- These should also be separate keybindings from the keybindings associated with using the tab switcher.
- When a user reorders their tabs on the tab bar, the MRU order remains unchanged. For example:
- Tab Bar:`[cmd(focused), ps, wsl]` and MRU:`[cmd, ps, wsl]`
- Reordered Tab Bar:`[wsl, cmd(focused), ps]` and MRU:`[cmd, ps, wsl]`
### Performance, Power, and Efficiency
## Potential Issues
We'll need to be careful about how the UI is presented depending on different sizes of the terminal. We also should test how the UI looks as it's open and resizing is happening. Visual Studio's tab switcher is a fixed size, and is always in the middle. Even when the VS window is smaller than the tab switcher size, the tab switcher will show up larger than the VS window itself.
![Small Visual Studio Without Tab Switcher](img/VSMinimumSize.png)
![Small Visual Studio With Tab Switcher](img/VSMinimumSizeWithTabSwitcher.png)
Visual Studio Code only allows the user to shrink the window until it hits a minimum width and height. This minimum width and height gives its tab switcher enough space to show a meaningful amount of information.
![Small Visual Studio Code with Tab Switcher](img/VSCodeMinimumTabSwitcherSize.png)
Terminal can't really replicate Visual Studio's version of the tab switcher in this situation. The TabSwitcher needs to be contained within the Terminal. So, if the TabSwitcher is always centered and has a percentage padding from the borders of the Terminal, it'll shrink as Terminal shrinks. Since the Terminal also has a minimum width, the switcher should always have enough space to be usefully visible.
## Future considerations
### Pane Navigation
There was discussion in [#1502] that brought up the idea of pane navigation, inspired by tmux.
![Tmux Tab and Pane Switching](img/tmuxPaneSwitching.png)
Tmux allows the user to navigate directly to a pane and even give a preview of the pane. This would be extremely useful since it would allow the user to see a tree of their open tabs and panes. Currently there's no way to see what panes are open in each tab, so if you're looking for a particular pane, you'd need to cycle through your tabs to find it. If something like pane profile names (not sure what information to present in the switcher for panes) were presented in the TabSwitcher, the user could see all the panes in one box.
To support pane navigation, the tab switcher can simply have another column to the right of the tab list to show a list of panes inside the selected tab. As the user iterates through the tab list, they can simply hit right to dig deeper into the tab's panes, and hit left to come back to the tab list. Each tab's list of panes will be MRU or in-order, depending on which `displayOrder` arg was provided to the `openTabSwitcher` keybinding.
Pane navigation is a clear next step to build on top of the tab switcher, but this spec will specifically deal with just tab navigation in order to keep the scope tight. The tab switcher implementation just needs to allow for pane navigation to be added in later.
### Tab Preview on Hover
With this feature, having a tab highlighted in the switcher would make the Terminal display that tab as if it switched to it. I believe currently there is no way to set focus to a tab in a "preview" mode. This is important because MRU updates whenever a tab is focused, but we don't want the MRU to update on a preview. Given that this feature is a "nice thing to have", I'll leave it for
after the tab switcher has landed.
## Resources
Feature Request: An advanced tab switcher [#1502]
Ctrl+Tab toggle between last two windows like Alt+Tab [#973]
The Command Palette Thread [#2046]
The Command Palette Spec [#5674]
Feature Request: Search [#605]
<!-- Footnotes -->
[#605]: https://github.com/microsoft/terminal/issues/605
[#973]: https://github.com/microsoft/terminal/issues/973
[#1502]: https://github.com/microsoft/terminal/issues/1502
[#2046]: https://github.com/microsoft/terminal/issues/2046
[#5674]: https://github.com/microsoft/terminal/pull/5674

View File

@@ -1,7 +1,7 @@
---
author: Mike Griese @zadjii-msft
created on: 2019-08-01
last updated: 2020-06-16
last updated: 2020-06-10
issue id: 2046
---
@@ -522,13 +522,6 @@ default. These are largely the actions that are bound by default.
]
```
## Addenda
This spec also has a follow-up spec which introduces further changes upon this
original draft. Please also refer to:
* June 2020: Unified keybindings and commands, and synthesized action names.
## Future considerations
* Commands will provide an easy point for allowing an extension to add its

View File

@@ -1,608 +0,0 @@
---
author: Mike Griese @zadjii-msft
created on: 2020-06-15
last updated: 2020-06-19
issue id: 2046
---
# Command Palette, Addendum 1 - Unified keybindings and commands, and synthesized action names
## Abstract
This document is intended to serve as an addition to the [Command Palette Spec].
While that spec is complete in it's own right, subsequent discussion revealed
additional ways to improve the functionality and usability of the command
palette. This document builds largely on the topics already introduced in the
original spec, so readers should first familiarize themselves with that
document.
One point of note from the original document was that the original specification
was entirely too verbose when defining both keybindings and commands for
actions. Consider, for instance, a user that wants to bind the action "duplicate
the current pane". In that spec, they need to add both a keybinding and a
command:
```json
{
"keybindings": [
{ "keys": [ "ctrl+alt+t" ], "command": { "action": "splitPane", "split":"auto", "splitMode": "duplicate" } },
],
"commands": [
{ "name": "Duplicate Pane", "action": { "action": "splitPane", "split":"auto", "splitMode": "duplicate" }, "icon": null },
]
}
```
These two entries are practically the same, except for two key differentiators:
* the keybinding has a `keys` property, indicating which key chord activates the
action.
* The command has a `name` property, indicating what name to display for the
command in the Command Palette.
What if the user didn't have to duplicate this action? What if the user could
just add this action once, in their `keybindings` or `commands`, and have it
work both as a keybinding AND a command?
## Solution Design
This spec will outline two primary changes to keybindings and commands.
1. Unify keybindings and commands, so both `keybindings` and `commands` can
specify either actions bound to keys, and/or actions bound to entries in the
Command Palette.
2. Propose a mechanism by which actions do not _require_ a `name` to appear in
the Command Palette.
These proposals are two atomic units - either could be approved or rejected
independently of one another. They're presented together here in the same doc
because together, they present a compelling story.
### Proposal 1: Unify Keybindings and Commands
As noted above, keybindings and commands have nearly the exact same syntax, save
for a couple properties. To make things easier for the user, I'm proposing
treating everything in _both_ the `keybindings` _and_ the `commands` arrays as
**BOTH** a keybinding and a command.
Furthermore, as a change from the previous spec, we'll be using `bindings` from
here on as the unified `keybindings` and `commands` lists. This is considering
that we'll currently be using `bindings` for both commands and keybindings, but
we'll potentially also have mouse & touch bindings in this array in the future.
We'll "deprecate" the existing `keybindings` property, and begin to exclusively
use `bindings` as the new property name. For compatibility reasons, we'll
continue to parse `keybindings` in the same way we parse `bindings`. We'll
simply layer `bindings` on top of the legacy `keybindings`.
* Anything entry that has a `keys` value will be added to the keybindings.
Pressing that keybinding will activate the action defined in `command`.
* Anything with a `name`<sup>[1]</sup> will be added as an entry (using that
name) to the Command Palette's Action Mode.
###### Caveats
* **Nested commands** (commands with other nested commands). If a command has
nested commands in the `commands` property, AND a `keys` property, then
pressing that keybinding should open the Command Palette directly to that
level of nesting of commands.
* **"Iterable" commands** (with an `iterateOn` property): These are commands
that are expanded into one command per profile. These cannot really be bound
as keybindings - which action should be bound to the key? They can't all be
bound to the same key. If a KeyBinding/Command json blob has a valid
`iterateOn` property, then we'll ignore it as a keybinding. This includes any
commands that are nested as children of this command - we won't be able to
know which of the expanded children will be the one to bind the keys to.
<sup>[1]</sup>: This requirement will be relaxed given **Proposal 2**, below,
but ignored for the remainder of this section, for illustrative purposes.
#### Example
Consider the following settings:
```json
"bindings": [
{ "name": "Duplicate Tab", "command": "duplicateTab", "keys": "ctrl+alt+a" },
{ "command": "nextTab", "keys": "ctrl+alt+b" },
{
"icon": "...",
"name": { "key": "NewTabWithProfileRootCommandName" },
"commands": [
{
"iterateOn": "profiles",
"icon": "${profile.icon}",
"name": { "key": "NewTabWithProfileCommandName" },
"command": { "action": "newTab", "profile": "${profile.name}" }
}
]
},
{
"icon": "...",
"name": "Connect to ssh...",
"commands": [
{
"keys": "ctrl+alt+c",
"icon": "...",
"name": "first.com",
"command": { "action": "newTab", "commandline": "ssh me@first.com" }
},
{
"keys": "ctrl+alt+d",
"icon": "...",
"name": "second.com",
"command": { "action": "newTab", "commandline": "ssh me@second.com" }
}
]
}
{
"keys": "ctrl+alt+e",
"icon": "...",
"name": { "key": "SplitPaneWithProfileRootCommandName" },
"commands": [
{
"iterateOn": "profiles",
"icon": "${profile.icon}",
"name": { "key": "SplitPaneWithProfileCommandName" },
"commands": [
{
"keys": "ctrl+alt+f",
"icon": "...",
"name": { "key": "SplitPaneName" },
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "automatic" }
},
{
"icon": "...",
"name": { "key": "SplitPaneVerticalName" },
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" }
},
{
"icon": "...",
"name": { "key": "SplitPaneHorizontalName" },
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" }
}
]
}
]
}
]
```
This will generate a tree of commands as follows:
```
<Command Palette>
├─ Duplicate tab { ctrl+alt+a }
├─ New Tab With Profile...
│ ├─ Profile 1
│ ├─ Profile 2
│ └─ Profile 3
├─ Connect to ssh...
│ ├─ first.com { ctrl+alt+c }
│ └─ second.com { ctrl+alt+d }
└─ New Pane... { ctrl+alt+e }
├─ Profile 1...
| ├─ Split Automatically
| ├─ Split Vertically
| └─ Split Horizontally
├─ Profile 2...
| ├─ Split Automatically
| ├─ Split Vertically
| └─ Split Horizontally
└─ Profile 3...
├─ Split Automatically
├─ Split Vertically
└─ Split Horizontally
```
Note also the keybindings in the above example:
* <kbd>ctrl+alt+a</kbd>: This key chord is bound to the "Duplicate tab"
(`duplicateTab`) action, which is also bound to the command with the same
name.
* <kbd>ctrl+alt+b</kbd>: This key chord is bound to the `nextTab` action, which
doesn't have an associated command.
* <kbd>ctrl+alt+c</kbd>: This key chord is bound to the "Connect to
ssh../first.com" action, which will open a new tab with the `commandline`
`"ssh me@first.com"`. When the user presses this keybinding, the action will
be executed immediately, without the Command Palette appearing.
* <kbd>ctrl+alt+d</kbd>: This is the same as the above, but with the "Connect to
ssh../second.com" action.
* <kbd>ctrl+alt+e</kbd>: This key chord is bound to opening the Command Palette
to the "New Pane..." command's menu. When the user presses this keybinding,
they'll be prompted with this command's sub-commands:
```
Profile 1...
Profile 2...
Profile 3...
```
* <kbd>ctrl+alt+f</kbd>: This key will _not_ be bound to any action. The parent
action is iterable, which means that the `SplitPaneName` command is going to
get turned into one command for each and every profile, and therefore cannot
be bound to just a single action.
### Proposal 2: Automatically synthesize action names
Previously, all Commands were required to have a `name`. This name was used as
the text for the action in the Action Mode of the Command Palette. However, this
is a little tedious for users who already have lots of keys bound. They'll need
to go through and add names to each of their existing keybindings to ensure that
the actions appear in the palette. Could we instead synthesize the names for the
commands ourselves? This would enable users to automatically get each of their
existing keybindings to appear in the palette without any extra work.
To support this, the following changes will be made:
* `ActionAndArgs` will get a `GenerateName()` method defined. This will create a
string describing the `ShortcutAction` and it's associated `ActionArgs`.
- Not EVERY action _needs_ to define a result for `GenerateName`. Actions that
don't _won't_ be automatically added to the Command Palette.
- Each of the strings used in `GenerateName` will need to come from our
resources, so they can be localized appropriately.
* When we're parsing commands, if a command doesn't have a `name`, we'll instead
attempt to use `GenerateName` to create the unique string for the action
associated with this command. If the command does have a `name` set, we'll use
that string instead, allowing the user to override the default name.
- If a command has it's name set to `null`, then we'll ignore the command
entirely, not just use the generated name.
[**Appendix 1**](#appendix-1-name-generation-samples-for-ShortcutActions) below
shows a complete sample of the strings that will be generated for each of the existing
`ShortcutActions`, and many of the actions that have been proposed, but not yet
implemented.
These strings should be human-friendly versions of the actions and their
associated args. For some of these actions, with very few arguments, the strings
can be relatively simple. Take for example, `CopyText`:
JSON | Generated String
-- | --
`{ "action":"copyText" }` | "Copy text"
`{ "action":"copyText", "singleLine": true }` | "Copy text as a single line"
`{ "action":"copyText", "singleLine": false, "copyFormatting": false }` | "Copy text without formatting"
`{ "action":"copyText", "singleLine": true, "copyFormatting": true }` | "Copy text as a single line without formatting"
CopyText is a bit of a simplistic case however, with very few args or
permutations of argument values. For things like `newTab`, `splitPane`, where
there are many possible arguments and values, it will be acceptable to simply
append `", property:value"` strings to the generated names for each of the set
values.
For example:
JSON | Generated String
-- | --
`{ "action":"newTab", "profile": "Hello" }` | "Open a new tab, profile:Hello"
`{ "action":"newTab", "profile": "Hello", "directory":"C:\\", "commandline": "wsl.exe", title": "Foo" }` | "Open a new tab, profile:Hello, directory:C:\\, commandline:wsl.exe, title:Foo"
This is being chosen in favor of something that might be more human-friendly,
like "Open a new tab with profile {profile name} in {directory} with
{commandline} and a title of {title}". This string would be much harder to
synthesize, especially considering localization concerns.
#### Remove the resource key notation
Since we'll be using localized names for each of the actions in `GenerateName`,
we no longer _need_ to provide the `{ "name":{ "key": "SomeResourceKey" } }`
syntax introduced in the original spec. This functionality was used to allow us
to define localizable names for the default commands.
However, I think we should keep this functionality, to allow us additional
flexibility when defining default commands.
### Complete Defaults
Considering both of the above proposals, the default keybindings and commands
will be defined as follows:
* The current default keybindings will be untouched. These actions will
automatically be added to the Command Palette, using their names generated
from `GenerateName`.
- **TODO: FOR DISCUSSION**: Should we manually set the names for the default
"New Tab, profile index: 0" keybindings to `null`? This seems like a not
terribly helpful name for the Command Palette, especially considering the
iterable commands listed below.
* We'll add a few new commands:
- A nested, iterable command for "Open new tab with
profile..."/"Profile:{profile name}"
- A nested, iterable command for "Select color scheme..."/"{scheme name}"
- A nested, iterable command for "New Pane..."/"Profile:{profile
name}..."/["Automatic", "Horizontal", "Vertical"]
> 👉 NOTE: These default nested commands can be removed by the user defining
> `{ "name": "Open new tab with profile...", "action":null }` (et al) in their
> settings.
- If we so chose, in the future we can add further commands that we think are
helpful to `defaults.json`, without needing to give them keys. For example,
we could add
```json
{ "command": { "action": "copy", "singleLine": true } }
```
to `bindings`, to add a "copy text as a single line" command, without
necessarily binding it to a keystroke.
These changes to the `defaults.json` are represented in json as the following:
```json
"bindings": [
{
"icon": null,
"name": { "key": "NewTabWithProfileRootCommandName" },
"commands": [
{
"iterateOn": "profiles",
"icon": "${profile.icon}",
"name": "${profile.name}",
"command": { "action": "newTab", "profile": "${profile.name}" }
}
]
},
{
"icon": null,
"name": { "key": "SelectColorSchemeRootCommandName" },
"commands": [
{
"iterateOn": "schemes",
"icon": null,
"name": "${scheme.name}",
"command": { "action": "selectColorScheme", "scheme": "${scheme.name}" }
}
]
},
{
"icon": null,
"name": { "key": "SplitPaneWithProfileRootCommandName" },
"commands": [
{
"iterateOn": "profiles",
"icon": "${profile.icon}",
"name": { "key": "SplitPaneWithProfileCommandName" },
"commands": [
{
"icon": null,
"name": { "key": "SplitPaneName" },
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "automatic" }
},
{
"icon": null,
"name": { "key": "SplitPaneVerticalName" },
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" }
},
{
"icon": null,
"name": { "key": "SplitPaneHorizontalName" },
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" }
}
]
}
]
}
]
```
A complete diagram of what the default Command Palette will look like given the
default keybindings and these changes is given in [**Appendix
2**](#appendix-2-complete-default-command-palette).
## Concerns
**DISCUSSION**: "New tab with index {index}". How does this play with
the new tab dropdown customizations in [#5888]? In recent iterations of that
spec, we changed the meaning of `{ "action": "newTab", "index": 1 }` to mean
"open the first entry in the new tab menu". If that's a profile, then we'll open
a new tab with it. If it's an action, we'll perform that action. If it's a
nested menu, then we'll open the menu to that entry.
Additionally, how exactly does that play with something like `{ "action":
"newTab", "index": 1, "commandline": "wsl.exe" }`? This is really a discussion
for that spec, but is an issue highlighted by this spec. If the first entry is
anything other than a `profile`, then the `commandline` parameter doesn't really
mean anything anymore. I'm tempted to revert this particular portion of the new
tab menu customization spec over this.
We could instead add an `index` to `openNewTabDropdown`, and have that string
instead be "Open new tab dropdown, index:1". That would help disambiguate the
two.
Following discussion, it was decided that this was in fact the cleanest
solution, when accounting for both the needs of the new tab dropdown and the
command palette. The [#5888] spec has been updated to reflect this.
## Future considerations
* Some of these command names are starting to get _very_ long. Perhaps we need a
netting to display Command Palette entries on two lines (or multiple, as
necessary).
* When displaying the entries of a nested command to the user, should we display
a small label showing the name of the previous command? My gut says _yes_. In
the Proposal 1 example, pressing `ctrl+alt+e` to jump to "Split Pane..."
should probably show a small label that displays "Split Pane..." above the
list of nested commands.
* It wouldn't be totally impossible to allow keys to be bound to an iterable
command, and then simply have the key work as "open the command palette with
only the commands generated by this iterable command". This is left as a
future option, as it might require some additional technical plumbing.
## Appendix 1: Name generation samples for `ShortcutAction`s
### Current `ShortcutActions`
* `CopyText`
- "Copy text"
- "Copy text as a single line"
- "Copy text without formatting"
- "Copy text as a single line without formatting"
* `PasteText`
- "Paste text"
* `OpenNewTabDropdown`
- "Open new tab dropdown"
* `DuplicateTab`
- "Duplicate tab"
* `NewTab`
- "Open a new tab, profile:{profile name}, directory:{directory}, commandline:{commandline}, title:{title}"
* `NewWindow`
- "Open a new window"
- "Open a new window, profile:{profile name}, directory:{directory}, commandline:{commandline}, title:{title}"
* `CloseWindow`
- "Close window"
* `CloseTab`
- "Close tab"
* `ClosePane`
- "Close pane"
* `NextTab`
- "Switch to the next tab"
* `PrevTab`
- "Switch to the previous tab"
* `SplitPane`
- "Open a new pane, profile:{profile name}, split direction:{direction}, split size:{X%/Y chars}, resize parents, directory:{directory}, commandline:{commandline}, title:{title}"
- "Duplicate the current pane, split direction:{direction}, split size:{X%/Y chars}, resize parents, directory:{directory}, commandline:{commandline}, title:{title}"
* `SwitchToTab`
- "Switch to tab {index}"
* `AdjustFontSize`
- "Increase the font size"
- "Decrease the font size"
* `ResetFontSize`
- "Reset the font size"
* `ScrollUp`
- "Scroll up a line"
- "Scroll up {amount} lines"
* `ScrollDown`
- "Scroll down a line"
- "Scroll down {amount} lines"
* `ScrollUpPage`
- "Scroll up a page"
- "Scroll up {amount} pages"
* `ScrollDownPage`
- "Scroll down a page"
- "Scroll down {amount} pages"
* `ResizePane`
- "Resize pane {direction}"
- "Resize pane {direction} {percent}%"
* `MoveFocus`
- "Move focus {direction}"
* `Find`
- "Toggle the search box"
* `ToggleFullscreen`
- "Toggle fullscreen mode"
* `OpenSettings`
- "Open settings"
- "Open settings file"
- "Open default settings file"
* `ToggleCommandPalette`
- "Toggle the Command Palette"
- "Toggle the Command Palette in commandline mode"
### Other yet unimplemented actions:
* `SwitchColorScheme`
- "Select color scheme {name}"
* `ToggleRetroEffect`
- "Toggle the retro terminal effect"
* `ExecuteCommandline`
- "Run a wt commandline: {cmdline}"
* `ExecuteActions`
- OPINION: THIS ONE SHOULDN'T HAVE A NAME. We're not including any of these by
default. The user knows what they're putting in the settings by adding this
action, let them name it.
- Alternatively: "Run actions: {action.ToName() for action in actions}"
* `SendInput`
- OPINION: THIS ONE SHOULDN'T HAVE A NAME. We're not including any of these by
default. The user knows what they're putting in the settings by adding this
action, let them name it.
* `ToggleMarkMode`
- "Toggle Mark Mode"
* `NextTab`
- "Switch to the next most-recent tab"
* `SetTabColor`
- "Set the color of the current tab to {#color}"
* It would be _really_ cool if we could display a sample of the color
inline, but that's left as a future consideration.
- "Set the color for this tab..."
* this command isn't nested, but hitting enter immediately does something
with the UI, so that's _fine_
* `RenameTab`
- "Rename this tab to {name}"
- "Rename this tab..."
* this command isn't nested, but hitting enter immediately does something
with the UI, so that's _fine_
## Appendix 2: Complete Default Command Palette
This diagram shows what the default value of the Command Palette would be. This
assumes that the user has 3 profiles, "Profile 1", "Profile 2", and "Profile 3",
as well as 3 schemes: "Scheme 1", "Scheme 2", and "Scheme 3".
```
<Command Palette>
├─ Close Window
├─ Toggle fullscreen mode
├─ Open new tab dropdown
├─ Open settings
├─ Open default settings file
├─ Toggle the search box
├─ New Tab
├─ New Tab, profile index: 0
├─ New Tab, profile index: 1
├─ New Tab, profile index: 2
├─ New Tab, profile index: 3
├─ New Tab, profile index: 4
├─ New Tab, profile index: 5
├─ New Tab, profile index: 6
├─ New Tab, profile index: 7
├─ New Tab, profile index: 8
├─ Duplicate tab
├─ Switch to the next tab
├─ Switch to the previous tab
├─ Switch to tab 0
├─ Switch to tab 1
├─ Switch to tab 2
├─ Switch to tab 3
├─ Switch to tab 4
├─ Switch to tab 5
├─ Switch to tab 6
├─ Switch to tab 7
├─ Switch to tab 8
├─ Close pane
├─ Open a new pane, split: horizontal
├─ Open a new pane, split: vertical
├─ Duplicate the current pane
├─ Resize pane down
├─ Resize pane left
├─ Resize pane right
├─ Resize pane up
├─ Move focus down
├─ Move focus left
├─ Move focus right
├─ Move focus up
├─ Copy Text
├─ Paste Text
├─ Scroll down a line
├─ Scroll down a page
├─ Scroll up a line
├─ Scroll up a page
├─ Increase the font size
├─ Decrease the font size
├─ Reset the font size
├─ New Tab With Profile...
│ ├─ Profile 1
│ ├─ Profile 2
│ └─ Profile 3
├─ Select Color Scheme...
│ ├─ Scheme 1
│ ├─ Scheme 2
│ └─ Scheme 3
└─ New Pane...
├─ Profile 1...
| ├─ Split Automatically
| ├─ Split Vertically
| └─ Split Horizontally
├─ Profile 2...
| ├─ Split Automatically
| ├─ Split Vertically
| └─ Split Horizontally
└─ Profile 3...
├─ Split Automatically
├─ Split Vertically
└─ Split Horizontally
```
<!-- Footnotes -->
[Command Palette Spec]: https://github.com/microsoft/terminal/blob/master/doc/specs/%232046%20-%20Command%20Palette.md

View File

@@ -7,7 +7,7 @@ This document outlines the roadmap towards delivering Windows Terminal 2.0 by Sp
## Milestones
The Windows Terminal project is engineered and delivered as a set of 4-week milestones. New features will go into [Windows Terminal Preview](https://aka.ms/terminal-preview) first, then a month after they've been in Preview, those features will move into [Windows Terminal](https://aka.ms/terminal).
The Windows Terminal project is engineered and delivered as a set of 4-week milestones. New features will go into [Windows Terminal Preview](https://aka.ms/terminal-preview) first, then a month after they been in Preview, those features will move into [Windows Terminal](https://aka.ms/terminal).
| Duration | Activity | Releases |
| --- | --- | --- |
@@ -21,7 +21,7 @@ Below is the schedule for when milestones will be included in release builds of
| Milestone End Date | Milestone Name | Preview Release Blog Post |
| ------------------ | -------------- | ------------------------- |
| 2020-06-18 | [1.1] in Windows Terminal Preview | [Windows Terminal Preview 1.1 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-1-release/) |
| 2020-06-30 | [1.1] in Windows Terminal Preview | |
| 2020-07-31 | [1.2] in Windows Terminal Preview<br>[1.1] in Windows Terminal | |
| 2020-08-31 | 1.3 in Windows Terminal Preview<br>[1.2] in Windows Terminal | |
| 2020-09-30 | 1.4 in Windows Terminal Preview<br>1.3 in Windows Terminal | |

Binary file not shown.

Binary file not shown.

View File

@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
### Fonts Included
* Cascadia Code, Cascadia Mono (2005.15)
* from microsoft/cascadia-code@0610f2df4356200adb93cb5bca2221b92ad6ee7e
* Cascadia Code, Cascadia Mono (2007.15)
* from microsoft/cascadia-code@2a54363b2c867f7ae811b9a034c0024cef67de96

View File

@@ -0,0 +1,181 @@
#
# Generate-TerminalAssets.ps1
#
# Typical usage:
# .\Generate-TerminalAssets.ps1 -Path .\Terminal.svg -HighContrastPath .\Terminal_HC.svg -Destination .\images
# .\Generate-TerminalAssets.ps1 -Path .\Terminal_Pre.svg -HighContrastPath .\Terminal_Pre_HC.svg -Destination .\images-Pre
# .\Generate-TerminalAssets.ps1 -Path .\Terminal_Dev.svg -HighContrastPath .\Terminal_Dev_HC.svg -Destination .\images-Dev
#
# Some icons benefit from manual hints. The most efficient way to do that is to run the script twice:
#
# 1. Run .\Generate-TerminalAssets.ps1 ...args... -Destination .\images -KeepIntermediates
# 2. Manually hint the intermediate images under .\images\_intermediate*.png
# 3. Run .\Generate-TerminalAssets.ps1 ...args... -Destination .\images -UseExistingIntermediates
#
# Hinting the intermediate files minimizes the number of times you'll have to
# hint the same image. You may want to hint just the _intermediate.*.png and
# _intermediate.black.*.png files, and delete _intermediate.white.*.png. The
# script will then automatically derive _intermediate.white.*.png from
# _intermediate.black.*.png.
#
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string]$Path,
[string]$Destination,
[int[]]$Altforms = (16, 20, 24, 30, 32, 36, 40, 48, 60, 64, 72, 80, 96, 256),
[switch]$Unplated = $true,
[float[]]$Scales = (1.0, 1.25, 1.5, 2.0, 4.0),
[string]$HighContrastPath = "",
[switch]$UseExistingIntermediates = $false,
[switch]$KeepIntermediates = $false
)
$assetTypes = @(
[pscustomobject]@{Name="LargeTile"; W=310; H=310; IconSize=96}
[pscustomobject]@{Name="LockScreenLogo"; W=24; H=24; IconSize=24}
[pscustomobject]@{Name="SmallTile"; W=71; H=71; IconSize=36}
[pscustomobject]@{Name="SplashScreen"; W=620; H=300; IconSize=96}
[pscustomobject]@{Name="Square44x44Logo"; W=44; H=44; IconSize=32}
[pscustomobject]@{Name="Square150x150Logo"; W=150; H=150; IconSize=48}
[pscustomobject]@{Name="StoreLogo"; W=50; H=50; IconSize=36}
[pscustomobject]@{Name="Wide310x150Logo"; W=310; H=150; IconSize=48}
)
function CeilToEven ([int]$i) { if ($i % 2 -eq 0) { [int]($i) } else { [int]($i + 1) } }
$inflatedAssetSizes = $assetTypes | ForEach-Object {
$as = $_;
$scales | ForEach-Object {
[pscustomobject]@{
Name = $as.Name + ".scale-$($_*100)"
W = [math]::Round($as.W * $_, [System.MidpointRounding]::ToPositiveInfinity)
H = [math]::Round($as.H * $_, [System.MidpointRounding]::ToPositiveInfinity)
IconSize = CeilToEven ($as.IconSize * $_)
}
}
}
$allAssetSizes = $inflatedAssetSizes + ($Altforms | ForEach-Object {
[pscustomobject]@{
Name = "Square44x44Logo.targetsize-${_}"
W = [int]$_
H = [int]$_
IconSize = [int]$_
}
If ($Unplated) {
[pscustomobject]@{
Name = "Square44x44Logo.targetsize-${_}_altform-unplated"
W = [int]$_
H = [int]$_
IconSize = [int]$_
}
}
})
# Cross product with the 3 high contrast modes
$allAssetSizes = $allAssetSizes | ForEach-Object {
$asset = $_
("standard", "black", "white") | ForEach-Object {
$contrast = $_
$name = $asset.Name
If ($contrast -Ne "standard") {
If ($HighContrastPath -Eq "") {
# "standard" is the default, so we can omit it in filenames
return
}
$name += "_contrast-" + $contrast
}
[pscustomobject]@{
Name = $name
W = $asset.W
H = $asset.H
IconSize = $asset.IconSize
Contrast = $_
}
}
}
$allSizes = $allAssetSizes.IconSize | Group-Object | Select-Object -Expand Name
$TranslatedSVGPath = & wsl wslpath -u ((Get-Item $Path -ErrorAction:Stop).FullName -Replace "\\","/")
$TranslatedSVGContrastPath = $null
If ($HighContrastPath -Ne "") {
$TranslatedSVGContrastPath = & wsl wslpath -u ((Get-Item $HighContrastPath -ErrorAction:Stop).FullName -Replace "\\","/")
}
& wsl which inkscape | Out-Null
If ($LASTEXITCODE -Ne 0) { throw "Inkscape is not installed in WSL" }
& wsl which convert | Out-Null
If ($LASTEXITCODE -Ne 0) { throw "imagemagick is not installed in WSL" }
If (-Not [string]::IsNullOrEmpty($Destination)) {
New-Item -Type Directory $Destination -EA:Ignore
$TranslatedOutDir = & wsl wslpath -u ((Get-Item $Destination -EA:Stop).FullName -Replace "\\","/")
} Else {
$TranslatedOutDir = "."
}
$intermediateFiles = [System.Collections.Concurrent.ConcurrentBag[string]]::new()
# Generate the base icons
$allSizes | ForEach-Object -Parallel {
$sz = $_;
$destinationNt = $using:Destination
$destinationWsl = $using:TranslatedOutDir
$svgStandardWsl = $using:TranslatedSVGPath
$svgContrastWsl = $using:TranslatedSVGContrastPath
$intermediateStandardNt = "$destinationNt\_intermediate.standard.$($sz).png"
$intermediateStandardWsl = "$destinationWsl/_intermediate.standard.$($sz).png"
If (($using:UseExistingIntermediates -Eq $false) -Or (-Not (Test-Path $intermediateStandardNt))) {
wsl inkscape -z -e "$intermediateStandardWsl" -w $sz -h $sz $svgStandardWsl
} Else {
Write-Host "Using existing $intermediateStandardNt"
}
($using:intermediateFiles).Add($intermediateStandardNt)
If ($svgContrastWsl -Ne $null) {
$intermediateBlackNt = "$destinationNt\_intermediate.black.$($sz).png"
$intermediateWhiteNt = "$destinationNt\_intermediate.white.$($sz).png"
$intermediateBlackWsl = "$destinationWsl/_intermediate.black.$($sz).png"
$intermediateWhiteWsl = "$destinationWsl/_intermediate.white.$($sz).png"
If (($using:UseExistingIntermediates -Eq $false) -Or (-Not (Test-Path $intermediateBlackNt))) {
wsl inkscape -z -e "$intermediateBlackWsl" -w $sz -h $sz $svgContrastWsl
} Else {
Write-Host "Using existing $intermediateBlackNt"
}
If (($using:UseExistingIntermediates -Eq $false) -Or (-Not (Test-Path $intermediateWhiteNt))) {
# The HC white icon is just a negative image of the HC black one
wsl convert "$intermediateBlackWsl" -channel RGB -negate "$intermediateWhiteWsl"
} Else {
Write-Host "Using existing $intermediateWhiteNt"
}
($using:intermediateFiles).Add($intermediateBlackNt)
($using:intermediateFiles).Add($intermediateWhiteNt)
}
}
# Once the base icons are done, splat them into the middles of larger canvases.
$allAssetSizes | ForEach-Object -Parallel {
$asset = $_
If ($asset.W -Eq $asset.H -And $asset.IconSize -eq $asset.W) {
Write-Host "Copying base icon for size=$($asset.IconSize), contrast=$($asset.Contrast) to $($asset.Name)"
Copy-Item "${using:Destination}\_intermediate.$($asset.Contrast).$($asset.IconSize).png" "${using:Destination}\$($asset.Name).png" -Force
} Else {
wsl convert "$($using:TranslatedOutDir)/_intermediate.$($asset.Contrast).$($asset.IconSize).png" -gravity center -background transparent -extent "$($asset.W)x$($asset.H)" "$($using:TranslatedOutDir)/$($asset.Name).png"
}
}
If ($KeepIntermediates -Eq $false) {
$intermediateFiles | ForEach-Object {
Write-Host "Cleaning up intermediate file $_"
Remove-Item $_
}
}

View File

@@ -0,0 +1,17 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>
<linearGradient id="background"><stop stop-color="#ffffff"/></linearGradient>
</defs>
<!-- background rounded rectangle -->
<path d="M2 6C0.9 6 0 6.9 0 8L0 12L0 13L0 40C0 41.1 0.9 42 2 42L46 42C47.1 42 48 41.1 48 40L48 13L48 12L48 8C48 6.9 47.1 6 46 6L32 6L16 6L2 6Z" fill="url(#background)"/>
<!-- tab outlines -->
<rect y="12" x="0" height="1" width="48" fill="url(#foreground)"/>
<rect y="6" x="15.33" height="7" width="1" fill="url(#foreground)"/>
<rect y="6" x="31.66" height="7" width="1" fill="url(#foreground)"/>
<!-- > -->
<path d="M15.2 24.3L6.4 33.1C5.9 33.6 5.9 34.3 6.4 34.7L8.2 36.5C8.7 37 9.4 37 9.8 36.5L18.6 27.7C19.1 27.2 19.1 26.5 18.6 26.1L16.8 24.3C16.4 23.9 15.6 23.9 15.2 24.3Z" fill="url(#foreground)"/>
<path d="M9.8 17.3L18.6 26.1C19.1 26.6 19.1 27.3 18.6 27.7L16.8 29.5C16.3 30 15.6 30 15.2 29.5L6.4 20.7C5.9 20.2 5.9 19.5 6.4 19.1L8.2 17.3C8.6 16.9 9.4 16.9 9.8 17.3Z" fill="url(#foreground)"/>
<!-- "DEV" -->
<path d="m 26.810547,33.324219 c 0,2.468099 0,4.936198 0,7.404297 1.111732,-0.02994 2.235898,0.08435 3.335087,-0.120798 1.520803,-0.279494 2.813448,-1.588029 2.928427,-3.15363 0.151833,-1.302795 -0.211075,-2.834413 -1.424476,-3.530575 -1.34461,-0.797858 -2.951172,-0.55964 -4.443327,-0.599294 -0.131903,0 -0.263807,0 -0.395711,0 z m 7.201172,0 c 0,2.468099 0,4.936198 0,7.404297 1.440104,0 2.880208,0 4.320312,0 0,-0.454427 0,-0.908855 0,-1.363282 -0.925781,0 -1.851562,0 -2.777343,0 0,-0.566406 0,-1.132812 0,-1.699218 0.805338,0 1.610677,0 2.416015,0 0,-0.454427 0,-0.908855 0,-1.363282 -0.805338,0 -1.610677,0 -2.416015,0 0,-0.536458 0,-1.072917 0,-1.609375 0.873698,0 1.747395,0 2.621093,0 0,-0.45638 0,-0.91276 0,-1.36914 -1.388021,0 -2.776041,0 -4.164062,0 z m 4.314453,0 c 0.871094,2.468099 1.742187,4.936198 2.613281,7.404297 0.522786,0 1.045573,0 1.568359,0 0.886719,-2.468099 1.773438,-4.936198 2.660157,-7.404297 -0.546875,0 -1.09375,0 -1.640625,0 -0.594401,1.782552 -1.188802,3.565104 -1.783203,5.347656 -0.580078,-1.782552 -1.160157,-3.565104 -1.740235,-5.347656 -0.559245,0 -1.118489,0 -1.677734,0 z m -9.976563,1.36914 c 0.937345,0.0013 2.047245,-0.09251 2.704846,0.710677 0.572414,0.79802 0.561189,1.924696 0.194439,2.806167 -0.413758,0.922712 -1.501024,1.222925 -2.434219,1.155031 -0.245561,-0.0018 -0.579112,0.08432 -0.465066,-0.277122 0,-1.464917 0,-2.929835 0,-4.394753 z" fill="url(#foreground)"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,17 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>
<linearGradient id="background"><stop stop-color="#ffffff"/></linearGradient>
</defs>
<!-- background rounded rectangle -->
<path d="M2 6C0.9 6 0 6.9 0 8L0 12L0 13L0 40C0 41.1 0.9 42 2 42L46 42C47.1 42 48 41.1 48 40L48 13L48 12L48 8C48 6.9 47.1 6 46 6L32 6L16 6L2 6Z" fill="url(#background)"/>
<!-- tab outlines -->
<rect y="12" x="0" height="1" width="48" fill="url(#foreground)"/>
<rect y="6" x="15.33" height="7" width="1" fill="url(#foreground)"/>
<rect y="6" x="31.66" height="7" width="1" fill="url(#foreground)"/>
<!-- > -->
<path d="M15.2 24.3L6.4 33.1C5.9 33.6 5.9 34.3 6.4 34.7L8.2 36.5C8.7 37 9.4 37 9.8 36.5L18.6 27.7C19.1 27.2 19.1 26.5 18.6 26.1L16.8 24.3C16.4 23.9 15.6 23.9 15.2 24.3Z" fill="url(#foreground)"/>
<path d="M9.8 17.3L18.6 26.1C19.1 26.6 19.1 27.3 18.6 27.7L16.8 29.5C16.3 30 15.6 30 15.2 29.5L6.4 20.7C5.9 20.2 5.9 19.5 6.4 19.1L8.2 17.3C8.6 16.9 9.4 16.9 9.8 17.3Z" fill="url(#foreground)"/>
<!-- _ -->
<path d="M40 32H24C23.4 32 23 32.4 23 33V36C23 36.6 23.4 37 24 37H40C40.6 37 41 36.6 41 36V33C41 32.4 40.6 32 40 32Z" fill="url(#foreground)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,17 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>
<linearGradient id="background"><stop stop-color="#ffffff"/></linearGradient>
</defs>
<!-- background rounded rectangle -->
<path d="M2 6C0.9 6 0 6.9 0 8L0 12L0 13L0 40C0 41.1 0.9 42 2 42L46 42C47.1 42 48 41.1 48 40L48 13L48 12L48 8C48 6.9 47.1 6 46 6L32 6L16 6L2 6Z" fill="url(#background)"/>
<!-- tab outlines -->
<rect y="12" x="0" height="1" width="48" fill="url(#foreground)"/>
<rect y="6" x="15.33" height="7" width="1" fill="url(#foreground)"/>
<rect y="6" x="31.66" height="7" width="1" fill="url(#foreground)"/>
<!-- > -->
<path d="M15.2 24.3L6.4 33.1C5.9 33.6 5.9 34.3 6.4 34.7L8.2 36.5C8.7 37 9.4 37 9.8 36.5L18.6 27.7C19.1 27.2 19.1 26.5 18.6 26.1L16.8 24.3C16.4 23.9 15.6 23.9 15.2 24.3Z" fill="url(#foreground)"/>
<path d="M9.8 17.3L18.6 26.1C19.1 26.6 19.1 27.3 18.6 27.7L16.8 29.5C16.3 30 15.6 30 15.2 29.5L6.4 20.7C5.9 20.2 5.9 19.5 6.4 19.1L8.2 17.3C8.6 16.9 9.4 16.9 9.8 17.3Z" fill="url(#foreground)"/>
<!-- "PRE" -->
<path d="m 27.279297,33.324219 c 0,2.468099 0,4.936198 0,7.404297 0.513672,0 1.027343,0 1.541015,0 0,-0.848958 0,-1.697917 0,-2.546875 0.917549,0.01484 1.927453,0.03299 2.65875,-0.616892 1.172278,-0.927905 1.176118,-3.073122 -0.17547,-3.840777 -0.981472,-0.568217 -2.13908,-0.358412 -3.218649,-0.399753 -0.268549,0 -0.537097,0 -0.805646,0 z m 5.869141,0 c 0,2.468099 0,4.936198 0,7.404297 0.513021,0 1.026041,0 1.539062,0 0,-0.950521 0,-1.901042 0,-2.851563 0.431639,-0.03621 0.908827,0.05394 1.148438,0.458985 0.525553,0.771736 0.970414,1.596546 1.458984,2.392578 0.604167,0 1.208333,0 1.8125,0 -0.638695,-0.976785 -1.211177,-1.999445 -1.914561,-2.931671 -0.241051,-0.276681 -0.636923,-0.466649 -0.07177,-0.574189 1.274395,-0.677377 1.378019,-2.774051 0.102268,-3.504493 -0.922467,-0.560429 -2.029423,-0.352936 -3.053526,-0.393944 -0.340466,0 -0.680932,0 -1.021398,0 z m 6.25,0 c 0,2.468099 0,4.936198 0,7.404297 1.440755,0 2.88151,0 4.322265,0 0,-0.454427 0,-0.908855 0,-1.363282 -0.925781,0 -1.851563,0 -2.777344,0 0,-0.566406 0,-1.132812 0,-1.699218 0.804688,0 1.609375,0 2.414063,0 0,-0.454427 0,-0.908855 0,-1.363282 -0.804688,0 -1.609375,0 -2.414063,0 0,-0.536458 0,-1.072917 0,-1.609375 0.873047,0 1.746094,0 2.619141,0 0,-0.45638 0,-0.91276 0,-1.36914 -1.388021,0 -2.776041,0 -4.164062,0 z M 34.6875,34.648438 c 0.570781,0.0371 1.28319,-0.150767 1.691507,0.358943 0.370833,0.564952 0.02331,1.464787 -0.684396,1.526855 -0.334157,0.03512 -0.67147,0.0117 -1.007111,0.0185 0,-0.634765 0,-1.269531 0,-1.904296 z m -5.867188,0.01562 c 0.61736,0.02299 1.42501,-0.135196 1.79395,0.492096 0.333428,0.657753 -0.0252,1.619083 -0.829192,1.664678 -0.319151,0.05007 -0.643164,0.0243 -0.964758,0.03073 0,-0.729167 0,-1.458333 0,-2.1875 z" fill="url(#foreground)"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Some files were not shown because too many files have changed in this diff Show More