terminal icon indicating copy to clipboard operation
terminal copied to clipboard

Theme-controlled color scheme switch

Open alkaitagi opened this issue 5 years ago • 15 comments

Description of the new feature/enhancement

It would be nice to be able to specify two schemes per profile; one for light mode and one for dark. I imagine that would be especially useful with "requestedTheme": "system".

Proposed technical implementation details (optional)

Could be implemented in the form of:

"colorScheme": {
      "light": "BlulocoLight",
      "dark": "BlulocoDark"
}

or:

"colorSchemeLight": "BlulocoLight",
"colorSchemeDark": "BlulocoDark"

alkaitagi avatar Dec 27 '19 08:12 alkaitagi

I like the idea. It might make sense to instead of colorSchemeLight and colorSchemeDark preserve the existing colorScheme attribute and add an alternateScheme attribute which could be switchable to. Otherwise, an additional attribute would be required to specify which theme mode to startup with.

Alternatively, this feature could be implemented by making the color schemes themselves have dual palettes: one for light and one for dark. So you could assign to a profile a scheme, like Campbell, and have a hotkey to switch between light and dark palette.

mkitzan avatar Dec 27 '19 20:12 mkitzan

I like the object idea since it doesn't use any other new name and instead uses current one. I'll elaborate on this bit

In vscode it was implemented as a 2 additional keys: preferredDarkColorTheme and preferredLightColorTheme. colorScheme key remain unchanged and the way it works, when vscode switches to appropriate theme, it changes the value of colorScheme in user settings (therefore updating the application visuals). This mechanism probably has something to do with the fact that vscode can't update settings in runtime without updating the file (https://github.com/microsoft/vscode/issues/43226), but I digress. I'm using a dotfiles repo on multiple workstations and automatic system theme switching (based on time of the day). And I'm constantly stumbling across situations when settings.json was updated remotely (by some meaningful stuff) and locally (colorScheme) which makes me do stash then pull again, then stash pop. It's also breaks automatic pull on dotfiles repo.

Of course it doesn't concern terminal since it doesn't update its settings in runtime when profiles.json is updated, but I still find it neat having string literal or object as config).

Having additional keys also increases ambiguity of (what will be selected?)

"colorSchemeLight": "BlulocoLight",
"colorSchemeDark": "BlulocoDark",
"colorScheme": "Campbell"

Adding alternateColorScheme is ambiguous in a sense that it's not clear what is dark and what is light, so I don't think it should be pursued

saitonakamura avatar Jan 06 '21 11:01 saitonakamura

Alternatively, this feature could be implemented by making the color schemes themselves have dual palettes: one for light and one for dark. So you could assign to a profile a scheme, like Campbell, and have a hotkey to switch between light and dark palette.

Unless someone builds a custom tuned color schema, this doesn't sound right to me. People's perception of light vs dark best colors might differ substantially (mine do), so IMHO making it a specific choice in the schema object makes sense.

I'm unsure of how Windows Terminal config schema backward compatibility is treated, but my suggestions would then be:

If it's OK to change the schema for an existing key: "colorScheme": { "light": "Foo Light", "dark": "Foo Dark" }

And if it's not really OK to change the key type, then I'd introduce a new keyword (colorSchemes?) and deprecate the old one, maybe migrating the user configs on the fly with a new build version.

vriesk avatar May 25 '21 10:05 vriesk

Any progress on this?

henry-js avatar Jun 01 '21 10:06 henry-js

Nope. We'll make sure to update this thread when there is. In the meantime, might I recommend the Subscribe button? image That way you'll be notified of any updates to this thread, without needlessly pinging everyone on this thread ☺️

zadjii-msft avatar Jun 01 '21 12:06 zadjii-msft

Workaround:

I have an AutoHotKey script that I run at sun-up and sun-down that changes the themes for programs that don't change automatically like GitKraken and KeyWeb. Windows Terminal thankfully automatically changes if you change its settings file, so this Powershell script does what I want:

$from = $args[0]
$to   = $args[1]

$settingsPath = $env:LocalAppData + "\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json";
# https://stackoverflow.com/a/30893960
$content = [System.IO.File]::ReadAllText($settingsPath).Replace(
    "`"colorScheme`": `"" + $from + "`",",
    "`"colorScheme`": `"" + $to   + "`",");
[System.IO.File]::WriteAllText($settingsPath, $content);

It's blunt and fragile - you could probably improve it by using JQ or actually parsing the JSON. But I'm lazy and this simple string replace works. I recommend you backup your settings.json before trying out this script. I didn't use AHK's string replace because that language gives me conniptions. I'm not sure my $settingsPath works for anyone else - find yours by hitting ctrl+shift+comma in WT.

I have the following function in AHK to make running Powershell scripts easier:

; https://www.autohotkey.com/boards/viewtopic.php?p=224271#p224271
PowerShell(Script, WorkingDir := "", Options := "", Params := "-ExecutionPolicy Bypass") {
    Run % "PowerShell.exe " . Params . " -Command &{" . Script . "}", % WorkingDir == "" ? A_ScriptDir : WorkingDir, % Options
}

Finally, with the following, I can hit ctrl+alt+win+PageDown to go to dark mode, and PageUp for light mode.

^#!PgDn::
PowerShell("./wt-theme-switch.ps1 'One Half Light' 'Campbell'")
Return

^#!PgUp::
PowerShell("./wt-theme-switch.ps1 'Campbell' 'One Half Light'")
Return

This assumes the PowerShell script is called ./wt-theme-switch.ps1 and it lives in the same directory as your AHK script. I use two separate keys because sometimes one of my other programs fails to switch themes and I need to run it a second time, so my hotkeys need to be idempotent. You may want to make the PowerShell script more intelligent and toggle to the other theme, based on the current contents, and save yourself a hotkey.

dharmaturtle avatar Dec 11 '21 20:12 dharmaturtle

I need to ~steal~ borrow this to add to my systray app which does the flippity on dark/light on command (for when I decide that I should work outside for a while to provide this meat suit with vitamin d). Good Jeorb!

fluffynuts avatar Dec 12 '21 21:12 fluffynuts

@dharmaturtle, awesome! Just created a Windows schedule based on your powershell, so the theme auto update a 7AM and 7PM

https://gist.github.com/biutas/2a6132170f81319a282c7135abb3dce9

biutas avatar Feb 04 '22 21:02 biutas

Added a specification file for this feature request.

#12613

arkthur avatar Mar 03 '22 01:03 arkthur

This is now the only app I use that doesn't match theme up with Windows light/dark mode (VS Code recently added it). So I'm slightly eager to hear when this feature is shipped 👍

sdegutis avatar Apr 07 '22 17:04 sdegutis

For those who are using Auto Dark Mode: you can use its newly added script execution feature to automatically execute a script like the suggested above. I am successfully using it to automatically switch the colour scheme when my Windows 11 Dark Mode is toggled. It's still rather hacky, but better than the AHK method suggested, in my opinion.

Ririshi avatar Apr 20 '22 13:04 Ririshi

I had trouble getting the powershell script to work with Auto Dark Mode, so I wrote a new (slightly more powerful) script in node. For anyone interested here is the script:

const { readFileSync, writeFileSync } = require("fs");
const { join } = require("path");

// color scheme configurations
// The dark scheme will be swapped with the light scheme and vice versa
const colorSchemes = [
  { dark: "Campbell", light: "Campbell Powershell" },
  { dark: "One Half Dark", light: "One Half Light" },
  { dark: "Solarized Dark", light: "Solarized Light" },
  { dark: "Tango Dark", light: "Tango Light" },
];

// parse argument to determine which mode to switch to
const modeArg = process.argv[2];
if (modeArg !== "-dark" && modeArg !== "-light") {
  console.error(`Invalid Argument. Argument must be '-dark' or '-light'. Received '${modeArg}'`);
  process.exit(-1);
}
const isDarkMode = modeArg === "-dark";

// read Windows Terminal config
const pathToWinTermConfig = join(
  process.env.LocalAppData,
  "/Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState/settings.json"
);
const configFileContents = readFileSync(pathToWinTermConfig).toString("utf-8");
const config = JSON.parse(configFileContents);

// update Windows Terminal config
config.profiles.list.forEach((profile) => {
  const color = colorSchemes.find((c) => profile.colorScheme === c[isDarkMode ? "light" : "dark"]);
  if (color) profile.colorScheme = color[isDarkMode ? "dark" : "light"];
});

// write Windows Terminal config changes to file system
writeFileSync(pathToWinTermConfig, JSON.stringify(config, null, 2));

Below is the Auto Dark Mode script config. The Auto Dark Mode script file should be located in C:\Users\<username>\AppData\Roaming\AutoDarkMode.

Note: I saved the node script in the root of my C drive and named it toggle-win-term-color.js

Enabled: true
Component:
  TimeoutMillis: 10000
  Scripts:
    - Name: WindowsTerminal
      Command: node
      ArgsLight: [C:\toggle-win-term-color.js, -light]
      ArgsDark: [C:\toggle-win-term-color.js, -dark]
      AllowedSources: [Any]

AndrewSmithDev avatar May 14 '22 03:05 AndrewSmithDev

Since powershell can parse JSON I binge made a slightly more polished implementation of the ADM route.

Running

powershell -nologo -noprofile -executionpolicy bypass -command "(iwr `"https://gist.githubusercontent.com/Gravifer/6511126e6c174c3ab7c647be43735dcc/raw/windowsTerminal_themeToggler.ps1`").content | out-file themeToggler.tmp.ps1; .\themeToggler.tmp.ps1; remove-item themeToggler.tmp.ps1"

anywhere will have the script initialized (which can be put into ADM's scripts.yaml). Modify themetoggler.config.json in the same folder where settings.json is to pair the Light-Dark theme colorSchemes manually. A simple fix for PowerShell's grammar highlighting palette is provided in the gist; samples of themetoggler.config.json and scripts.yaml of Auto Dark Mode are supplied there as well.

  • Ideas are taken from @dharmaturtle @biutas @Ririshi @AndrewSmithDev

Update: Now using Powershell 5.1 is OK.

For those who are using Auto Dark Mode: you can use its newly added script execution feature to automatically execute a script like the suggested above. I am successfully using it to automatically switch the colour scheme when my Windows 11 Dark Mode is toggled. It's still rather hacky, but better than the AHK method suggested, in my opinion.

Gravifer avatar May 20 '22 21:05 Gravifer

Just to help clarify: a spec was accepted in #12613. We probably won't have time to get to this one soon, but I don't think it'd be a terribly difficult change to make. I'd be happy to give pointers if anyone's interested in contributing the code ☺️

implementation roadmap:

zadjii-msft avatar Aug 25 '22 17:08 zadjii-msft

@zadjii-msft I will start work on this feature (Sep 19th) for MSFT hackathon. ☺️ https://hackbox.microsoft.com/project/5766

bennettnicholas avatar Sep 15 '22 14:09 bennettnicholas

Hey All, attached is a draft PR of the work done now and the code cleaned up a bit. Right now there is two big bugs that are holding me back from doing the PR. https://github.com/microsoft/terminal/pull/14064

First is there is no current way to trigger a settings refresh when OS Theme switches. Any advice on how to trigger an event when the OS Theme switches would be useful. WM_THEMECHANGED does not seem to be working in the message handler.

Second, the profile appearance control preview does not update to the "selected" choice for color scheme. I am still trying to debug this a bit, but any suggestions are accepted. I think this is because the preview pulls directly from the profile.defaultappearance, but we never offically save the defaultappearance "ColorSchemeName" because the user hasnt pressed save yet. So some future research will be needed to discover how to force this up.

There is future work needed with the GUI, but will not be apart of this PR as @carlos-zamora mentioned this would need some design considerations from the team.

For now, you can pull the branch and set the dark and light color schemes under the profile though and play around with it. Can accept either single string or object.

example: "colorScheme" : "One Half Dark" or "colorScheme": { "dark": "One Half Dark", "light": "One Half Light" },

bennettnicholas avatar Sep 23 '22 00:09 bennettnicholas

Ill be traveling (Sep 23 2022) and will pick up work and considerations / conversations (Sep 24 2022)

  • Update: Got covid, will resume when better

bennettnicholas avatar Sep 23 '22 00:09 bennettnicholas

Hey everybody. We've got a bit of a quandry here, that #14064 helped draw attention to.

Do folks want the scheme to sync to the Terminal's theme, or the OS's theme? These each come with pro's and cons.

For clarity, the Terminal theme is the window's applicationTheme, which controls the appearance of almost all controls in the app - the command palette, the tabs, the menus, etc. In 1.16, there are also Theme objects for more customization, but ultimately we're interested in the window.applicationTheme property (which is just theme before 1.16).

The Scheme is the colors of the box of text itself. Importantly, this controls the main background of the window.

  • OS theme:
    • If the user is automatically changing the OS theme based off time of day, the Terminal can automatically pick up on that
    • If the user is changing the terminal theme frequently, between light and dark themes, the scheme of the terminal won't reflect this. The user would need to change schemes to match.
  • Terminal theme:
    • If the user wants the scheme to match the OS theme, they need to set applicationTheme to system.
    • The default theme for the Terminal on 1.16 sets applicationTheme to dark, so users would necessarily need to also change the terminal theme if they want to use this feature.
      • It's a little wacky to get a dark titlebar, with OS theme set to light, and applicationTheme set to system: MicrosoftTeams-image (15)

We're kinda at an impasse here, and want to know what folks think. I suspect most people who are looking for this feature want the scheme to match the OS theme, always, regardless of terminal theme. But I don't want to make assumptions! Maybe folks are using a tool to swap the Terminal theme based on time of day instead!

There's also a mind to have the Terminal's theme match the OS theme with a similar syntax (draft spec), but I don't think that actually helps any of our problems here.

zadjii-msft avatar Nov 11 '22 16:11 zadjii-msft

If the user is automatically changing the OS theme based off time of day, the Terminal can automatically pick up on that

This is all I care about. I'm not a friend of dark mode at day at all. And vice versa. It hurts my eyes.

boop5 avatar Nov 11 '22 16:11 boop5

If the user is automatically changing the OS theme based off time of day, the Terminal can automatically pick up on that

This is all I care about. I'm not a friend of dark mode at day at all. And vice versa. It hurts my eyes.

Same thing, I use a script to change the OS theme and some applications that don't have an option to follow it.

Just one addition: I would also like the option to change the scheme based on the OS theme but I am not sure if that should be the default or if there's enough info to have matching dark and light schemes without user input. I think someone above mentioned VSCode having an explicit setting for picking a dark and a light scheme that should be used in this case.

fabianlupa avatar Nov 11 '22 16:11 fabianlupa

To me the most complete solution would look like this:

  • Application theme lets me choose light, dark, or system. This controls the "chrome" of the app (title bar, tabs, command palette)
  • Scheme lets me choose two schemes: one for dark theme and one for light theme. Then, however I change the application theme (directly in Terminal settings, or by changing my system theme) I get a scheme that works for me. I can even set both schemes to be the same if I don't want them to change depending on app theme.

jerivas avatar Nov 11 '22 16:11 jerivas

real quick clarity:

image

The difference between Theme and Scheme is very important here, lets try to not mix them up

zadjii-msft avatar Nov 11 '22 16:11 zadjii-msft

This would be my ideal behavior:

  • OS switches to dark mode (either due to time of day, or user has explicitly switched system theme)
    • Terminal theme (i.e. title bar, controls, menus, etc) switches to match
    • Terminal scheme switches to some custom scheme which I have explicitly designated as my "dark mode scheme", e.g. `
  • OS switches to light mode
    • Terminal theme switches to light
    • Terminal scheme switches to my custom "light mode scheme"

reynoldsbd avatar Nov 11 '22 16:11 reynoldsbd

Wow overwhelming initial feedback seems like folks want theme AND scheme to match the OS theme. That kinda sounds like an endorsement for "the scheme matches the Terminal's theme", and anyone who wants this feature manually changes their Terminal theme to system (which will mean the Terminal's UI and the color scheme will both reflect the OS theme).

Right?

zadjii-msft avatar Nov 11 '22 16:11 zadjii-msft

will mean the Terminal's UI and the color scheme will both reflect the OS theme

@zadjii-msft well, yes and no. We want to be able to manually specify a scheme for each theme. That could mean a light scheme with a dark theme, or the more usual dark on dark and light on light. Being able to set a scheme per theme is the important bit IMO

jerivas avatar Nov 11 '22 16:11 jerivas

Rough proposal: two new config properties: colorSchemeLight and colorSchemeDark. To preserve previous behavior, if the original colorScheme is explicitly set, then it is always used, regardless of the current theme. But if colorScheme is not set, and both colorSchemeLight/Dark are set, then we activate the "auto switching" behavior

reynoldsbd avatar Nov 11 '22 16:11 reynoldsbd

Er, yes. I was too short there. More verbosely:

The user can set both a colorScheme.light and a colorScheme.dark. When evaluating the settings, the Terminal will pick one of those two, depending on which applicationTheme the Terminal is currently using. Users who want this feature to match the OS theme, should set their theme to system, and the Terminal will automatically switch those schemes based on the OS theme.

If the user's only got a colorScheme set, then that acts as the same value for both colorScheme.light and a colorScheme.dark.

FWIW, this is how #14064 is currently implemented, but there was some confusion because the spec wasn't exactly clear as to which theme (terminal or OS) the scheme should be following.

zadjii-msft avatar Nov 11 '22 16:11 zadjii-msft

@zadjii-msft I like the interpretation you just posted. It's the most straightforward / expected default behavior. However, are you sure you are linking to the correct PR?

khuongduybui avatar Nov 11 '22 18:11 khuongduybui

@zadjii-msft I like the interpretation you just posted. It's the most straightforward / expected default behavior. However, are you sure you are linking to the correct PR?

SURE DIDN'T. Fatfingered that one. The correct PR is #14064.

zadjii-msft avatar Nov 11 '22 19:11 zadjii-msft

:tada:This issue was addressed in #14064, which has now been successfully released as Windows Terminal Preview v1.17.1023.:tada:

Handy links:

ghost avatar Jan 24 '23 18:01 ghost