Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Use light/dark theme base on terminal theme or system variable. #447

Closed
tshu-w opened this issue Dec 15, 2020 · 44 comments · Fixed by #581
Closed

🚀 Use light/dark theme base on terminal theme or system variable. #447

tshu-w opened this issue Dec 15, 2020 · 44 comments · Fixed by #581

Comments

@tshu-w
Copy link

tshu-w commented Dec 15, 2020

Hi, I have set my terminal to auto change themes based on macOS appearance. It would be much appreciated if delta can use light/dark theme base on terminal theme or system variable.

@lourenci
Copy link

I second that. I'm struggling with the same issue.

I don't care about the delta theme at all, I wish delta could just alternate between --light and --dark colors according to the terminal colors automatically.

Out of the curiosity, what terminal emulator are you using @tshu-w? Does it support this out-of-the-box?

Thank you.

@tshu-w
Copy link
Author

tshu-w commented Dec 17, 2020

@lourenci Hi, I'm using iTerm2 on macOS, unlike the terminal that comes with the system, it requires a script to make the automatic switch possible.

@dandavison
Copy link
Owner

Hi @tshu-w and @lourenci, yes let's figure out a good way to do this. What do you think of this proposal:

  • There are two parts to this: (1) detecting the relevant system state (e.g. OS dark mode, or current terminal color theme etc) and (2) getting delta to respond to the relevant system state.

  • I suggest we address those separately. Specifically, I suggest that we say that (1) is not Delta's responsibility. Instead, Delta will expose an environment variable named DELTA_FEATURES. This environment variable does exactly the same thing as the existing --features flag. (see custom features and --help output)

  • Thus it is the user's responsibility to populate the DELTA_FEATURES environment variable with the names of the custom features they want to be active, in response to changes in their system state. One way to do this might be to use shell prompt code (i.e. shell code that is executed every time the prompt is displayed).

  • So, for example, one might define custom features something like below, and then arrange for DELTA_FEATURES to contain either "my-light-mode" or "my-dark-mode" as appropriate.

[delta "my-light-mode"]
    light = true
    file-style = magenta # or whatever other settings you want; this is just an example

[delta "my-dark-mode"]
    dark = true
    file-style = green # or whatever other settings you want; this is just an example

If you're able to build delta from source then you can play around with this now as I've added support for the DELTA_FEATURES env var. cc @ulwlu ref #340 (comment)

If this seems like a good way forward then let's put together a concrete example of doing this with a particular OS/terminal emulator/shell combination.

@dandavison
Copy link
Owner

dandavison commented Dec 18, 2020

@tshu-w @lourenci here's a proof-of-principle that uses iTerm2 proprietary escape codes and zsh's preexec hook. Unfortunately the iterm2 escape codes do not work in tmux. The DELTA_FEATURES env var is available in master but not yet released.

Instructions:

  1. Set up Delta features for your light and dark modes:

    [delta "my-light-mode"]
        light = true
        syntax-theme = GitHub
    
    [delta "my-dark-mode"]
        dark = true
        syntax-theme = Dracula
  2. Arrange for the DELTA_FEATURES env var to be populated correctly according to the current terminal background color, before running any command (This is for zsh; in bash one can use PROMPT_COMMAND which runs before displaying the prompt).

    __dan_preexec_function () {
        export DELTA_FEATURES="my-$(iterm2-get-light-or-dark-bg)-mode"
    }
    typeset -ag preexec_functions;
    preexec_functions=( __dan_preexec_function ${preexec_functions[@]} )
  3. Put the python script below on your $PATH with the name iterm2-get-light-or-dark-bg and make it executable.

    #!/usr/bin/env python
    import subprocess
    import sys
    
    def gamma_correct(rgb: int) -> float:
        rgb = rgb / 255
        return rgb / 12.92 if rgb <= 0.03928 else ((rgb + 0.055) / 1.055) ** 2.4
    
    
    def luminance(r: int, g: int, b: int) -> float:
        """
        https://www.w3.org/TR/WCAG20/#relativeluminancedef
        """
        r, g, b = map(gamma_correct, [r, g, b])
        return 0.2126 * r + 0.7152 * g + 0.0722 * b
    
    # https://iterm2.com/documentation-escape-codes.html
    cmd = "read -s -N 26 -p $'\x1b]4;-2;?\x07' RESPONSE; echo \"$RESPONSE\""
    
    response = subprocess.check_output(["bash", "-c", cmd]).strip().decode('utf-8')
    r, g, b = map(lambda hex: int(hex[:2], 16), response.split(":")[1].split('/'))
    
    sys.stdout.write("light\n" if luminance(r, g, b) > 0.5 else "dark\n")

Now run exec zsh to reload zsh config and Delta will automatically switch between your light/dark modes according to the relative luminance value of the background color used by your iTerm2 color theme.

@ghost
Copy link

ghost commented Dec 18, 2020

Hi, I agree with dandavison that this is not delta's responsibility, but I feel not a few people want this.
I knew this library can solve this issue.

https://github.com/dalance/termbg

If we'll approach as delta side will solve this, I will fix.

@dandavison
Copy link
Owner

@ulwlu oh, that library looks perfect. I am absolutely happy for us to build in background color detection to Delta. You're right: that would be a much better solution. Terminal color detection is important enough for delta to do it. And I see it has some tmux support... the code I posted above doesn't work in tmux.

@ghost
Copy link

ghost commented Dec 18, 2020

Yes it works in tmux. There are many informations about this library in Japanese articles, so I'm going to give it a try in a few days!

@dandavison
Copy link
Owner

Yes it works in tmux. There are many informations about this library in Japanese articles, so I'm going to give it a try in a few days!

Ok, sounds great -- I'll leave this one for you to work on.

@dandavison
Copy link
Owner

dandavison commented Dec 22, 2020

I've been trying to think about how we want this to work ultimately. Basically, termbg (if it works as we hope) is going to give us two states: light mode and dark mode. Initially, I think we can just have that switch between delta's existing --light and --dark settings.

Ultimately, I think it would be nice if users could have two features defined in their gitconfig, one for their light mode settings, and one for their dark mode settings. These would contain the syntax-theme and colors, etc. I'm not sure whether those feature names should be fixed, e.g. [delta "light"] and [delta "dark"] or whether users should be able to specify the name of the custom feature that holds their light-mode and dark-mode settings.

A related issue is that the code for --light and --dark should probably be refactored to use the "builtin feature" pattern (src/features/*).

@tshu-w
Copy link
Author

tshu-w commented Jan 9, 2021

@dandavison @ulwlu Thank you for your efforts, I've been a bit busy recently. Although the solution provided by @dandavison is sufficient, I agree that it would be great for delta to have built-in color detection, so I will leave this issue open.

@sscherfke
Copy link

Basically, termbg (if it works as we hope) is going to give us two states: light mode and dark mode.

I have a custom script to set/get/toggle darkmode states on different OS’. Maybe delta could expose an environment variable (e.g., DELTA_TERMBG_CMD="dm get") that can be used if/when termbg is not available/does not work as expected?

@ghost
Copy link

ghost commented Jan 25, 2021

@sscherfke
oh sorry for repling late. As my status said I'm currently quite busy with my job until March.
If you can create PR, it would be better.

@skyfaller
Copy link

bat "solved" this by providing an "ansi" theme, replacing ansi-dark and ansi-light, which automatically switches colors to work in either a dark or light theme. sharkdp/bat#1104

Could delta do the same?

@dandavison
Copy link
Owner

Hi @skyfaller I'm sorry, I was on the verge of replying to this 3 weeks ago when you said it and then somehow didn't! What I was going to say is thanks, and you prompted me to update delta to bat's latest themes, so ansi will be available in delta (and ansi-light and ansi-dark will be gone), since delta follows bat here. However, delta users also (mostly) specify background colors for the different sorts of highlighting of plus and minus lines, and this is typically done with 256 colors or 24-bit colors, and so the question of automated choice between two modes of those colors would still be unsolved, as I'm understanding things.

@skyfaller
Copy link

@dandavison Thank you! It doesn't solve the problem for people who want more advanced themes, no, but for people who just want legible text for now, the "ansi" theme is a fine workaround. I look forward to the next release!

Here's what I did to "solve" the problem for myself in the meantime, on Gnome with fish shell using sd.

I made a fish function that detects my Gnome theme and uses that to change my .gitconfig to my desired delta theme:

function theme-delta --description 'Set delta theme based on gtk-theme'
  switch (gsettings get org.gnome.desktop.interface gtk-theme)
  case "'Pop'"
    sd '^\tsyntax-theme = .*$' '\tsyntax-theme = gruvbox' ~/.gitconfig
  case "'Adwaita'"
    sd '^\tsyntax-theme = .*$' '\tsyntax-theme = GitHub' ~/.gitconfig
  case "'Pop-dark'"
    sd '^\tsyntax-theme = .*$' '\tsyntax-theme = Monokai Extended' ~/.gitconfig
  case '*'
    echo "error: unidentified GTK theme"
  end
end

Then I use the Night Theme Switcher GNOME Shell extension to run that fish function (and similar functions for Alacritty and bat) at the same time as it changes my theme between light and dark, like so:
/usr/bin/fish -c 'theme-bat; theme-alacritty; theme-delta'

I'm very interested to see how you finally solve this, termbg looks cool!

@dandavison
Copy link
Owner

Cool, thanks, that's a lot of helpful links and example code! (And I hadn't come across sd, I'll definitely look into that.)

@jtroo
Copy link

jtroo commented Apr 29, 2021

I just tried out the ansi theme in the latest master (1b3bd2877a3ba97a2fa10a328610edc945e993f4) and it doesn't behave as expected. Works on light themes, but not dark themes. I think the default text colour is set to black, when it actually should be left uncolored.

@dandavison
Copy link
Owner

dandavison commented Apr 29, 2021

Thanks very much @jtroo. Should be fixed in #581. Any chance you could confirm that's behaving as expected for you?

@dandavison
Copy link
Owner

Thanks very much @jtroo. Should be fixed in #581. Any chance you could confirm that's behaving as expected for you?

Ah I'm sorry, ignore that, there's more work to be done on that branch. I'll ping here again when I've merged that branch to master.

@jtroo
Copy link

jtroo commented Apr 29, 2021

I built and tested off of that branch. Works as expected now :)

dandavison added a commit that referenced this issue Apr 30, 2021
* Update to latest version of bat::terminal::to_ansi_color

Fixes #447

* Delete mention of historical syntax theme ansi-light

* Update tests
@dandavison
Copy link
Owner

I built and tested off of that branch. Works as expected now :)

Thanks @jtroo! There were just some fairly superficial test failures; in master now.

@dandavison dandavison reopened this Apr 30, 2021
@lourenci
Copy link

Just a feedback: ansi is not working for me on HEAD.

My config:

[delta]
  navigate = true
  syntax-theme = ansi

Output (light terminal):
image

@jtroo
Copy link

jtroo commented May 20, 2021

@lourenci that looks to be behaving correct to me. The issue is that the green/red bgs are too dark. You may want to adjust your terminal colours, choose a custom green/red for delta, or choose custom text output (as opposed to syntax highlighted).

@dandavison
Copy link
Owner

Actually, I think there might be an even easier fix. @lourenci, I think you need to add light = true:

[delta]
  light = true
  navigate = true
  syntax-theme = ansi

By default, delta assumes your terminal background is dark, since I think most people use dark (but I'm with you, mine is light too).

@dandavison
Copy link
Owner

For the record, I'll point out that syntax-theme only affects foreground colors (syntax highlighting). Background colors are not affected at all by setting syntax-theme; they are affected by things like plus-style etc.

bahlo added a commit to bahlo/dotfiles that referenced this issue Nov 2, 2021
@chtenb
Copy link
Contributor

chtenb commented Mar 30, 2022

Hi @skyfaller I'm sorry, I was on the verge of replying to this 3 weeks ago when you said it and then somehow didn't! What I was going to say is thanks, and you prompted me to update delta to bat's latest themes, so ansi will be available in delta (and ansi-light and ansi-dark will be gone), since delta follows bat here. However, delta users also (mostly) specify background colors for the different sorts of highlighting of plus and minus lines, and this is typically done with 256 colors or 24-bit colors, and so the question of automated choice between two modes of those colors would still be unsolved, as I'm understanding things.

Yes exactly. I use ansi compatible colors for syntax highlighting but still need to revert to 24-bit colors for background highlighting the plus and minus lines. So using ansi syntax is not a full workaround.

What is the status of this issue? Has the termbg support been released yet?

@chtenb
Copy link
Contributor

chtenb commented Mar 30, 2022

I also want to comment on the DELTA_FEATURES approach. I'm afraid that this is not going to solve all use cases. I'm using VSCode and my theme switches based on system settings. I believe that

  1. there is no way to hook into that change and set environment variables in this situation
  2. changing environment variables will not affect open terminals

@floriankisser
Copy link

I tried termbg, but it needs stdin and stdin is already in use for the pipe.

@dandavison
Copy link
Owner

changing environment variables will not affect open terminals

@chtenb right, that's true. But you can use some suitable mutable state such as a file on disk, and create a wrapper script around delta that looks at the file to see whether you're in light/dark mode, and invokes delta accordingly.

I tried termbg, but it needs stdin and stdin is already in use for the pipe.

@floriankisser thanks, yes, I looked into termbg also and came to that conclusion. Unless there's some trick we're missing, it seems to be a non-starter: delta uses stdin to receive its input.

@chtenb
Copy link
Contributor

chtenb commented Mar 31, 2022

@chtenb right, that's true. But you can use some suitable mutable state such as a file on disk, and create a wrapper script around delta that looks at the file to see whether you're in light/dark mode, and invokes delta accordingly.

That is true, but I'm not sure how I would have the filesystem mutated based on my VSCode theme. I'm sure I could hack something together like inspecting the VSCode config, but a generic solution that is able to inspect the background of the terminal sounds so much more clean and easy to use for everyone. But it sounds like that problem is not solvable in a generic way for every terminal?

@floriankisser thanks, yes, I looked into termbg also and came to that conclusion. Unless there's some trick we're missing, it seems to be a non-starter: delta uses stdin to receive its input.

It seems that VSCode is not supported so I wouldn't benefit from termbg.

@cben
Copy link

cben commented Mar 31, 2022

Fwiw, any of the standard file descriptors, when dhey ase connected to the terminal, are bidirictional.
For example I believe less reads keyboard input from stderr 🤯 (because stdin is taken for piping).

@chtenb
Copy link
Contributor

chtenb commented Apr 15, 2022

I explored solving this problem from a different angle: by only using ansi colors. I used the basic ansi colors (0-15 for syntax highlighting and repurposed some of the extended ansi colors (16-255) as background for colored lines as follows

[delta]
    syntax-theme = terminal-ansi16
    features = zebra-ansi

[delta "zebra-ansi"]
    minus-style = syntax 52
    minus-emph-style = syntax 88
    plus-style = syntax 22
    plus-emph-style = syntax 28

    map-styles = \
       bold purple => syntax 53, \
       bold blue => syntax 17, \
       bold cyan => syntax 23, \
       bold yellow => syntax 58

    zero-style = syntax
    whitespace-error-style = "#aaaaaa"

The syntax theme I used is a custom bat theme terminal-neo-ansi. It only uses ansi colors just like the bat ansi theme, but it's better than the default included ansi theme imho :).

If your terminal supports customization of the entire range of ansi colors (0-255) and is able to switch colorschemes based on your system darkmode setting, then you can leverage both these things to achieve an adaptive experience. Here is a video that shows a diff while I switch my system darkmode setting (on Windows 10) using wezterm as my terminal.

less.exe.2022-04-15.22-46-28.mp4

For the record, this is the wezterm configuration that I used. It's a bit hacky with all the hardcoded colors imho, but it works.

Furthermore, I have flux installed and configured to switch systemwide darkmode on/off based on the time of day.

@seansfkelley
Copy link

If you're on a Mac, you can defaults read the current theme and use that to configure delta features at runtime:

[core]
  pager = delta --features "$(defaults read -globalDomain AppleInterfaceStyle &> /dev/null && echo dark-mode || echo light-mode)"

[delta "light-mode"]
  light = true
  syntax-theme = GitHub

[delta "dark-mode"]
  light = false
  syntax-theme = Visual Studio Dark+

This precludes the need to wire up something to listen to changes to the system theme and to write that to some environment variable somewhere.

@eproxus
Copy link

eproxus commented Feb 15, 2023

@seansfkelley Thanks a lot for that snippet! I had to add the following for it to work for git add --patch too:

[interactive]
    diffFilter = delta --color-only --features "$(defaults read -globalDomain AppleInterfaceStyle &> /dev/null && echo dark-mode || echo light-mode)"

@connorjs
Copy link

The gitconfig gets executed with sh, not bash, so prefer the following defaults read command to avoid the &> bashism.

pager = delta --features "$(defaults read -globalDomain AppleInterfaceStyle > /dev/null 2>&1 && echo dark-mode || echo light-mode)"

macOS ships with sh symlinked to bash by default, but it also ships with dash built-in, so I’ve switched sh to dash to ensure POSIX-compliance in my scripting.

@anuanandpremji
Copy link

This is what works for me on GNOME (Ubuntu 22.10).
For context, I use the default dark/light on Ubuntu and everything including terminal theme switches as I switch the system-wide dark/light theme.

The below settings checks the system theme and if it sees "dark" in the theme name, delta will use the dark-theme; otherwise light-theme. Had to use case instead of a more readable if statement due to the command needing to be POSIX compliant.

[core]
    pager  = LESS=-R delta --features "$(case "$(gsettings get org.gnome.desktop.interface gtk-theme)" in *"dark"*) echo "dark-theme" ;; *) echo "light-theme" ;; esac)"

[delta]
    syntax-theme = none
    side-by-side = true
    line-numbers = true

[delta "dark-theme"]
    minus-style = syntax "#330f0f"
    minus-emph-style = syntax "#4f1917"
    plus-style = syntax "#0e2f19"
    plus-emph-style = syntax "#174525"
    map-styles = \
       bold purple => syntax "#330f29", \
       bold blue => syntax "#271344", \
       bold cyan => syntax "#0d3531", \
       bold yellow => syntax "#222f14"
    zero-style = syntax
    whitespace-error-style = "#aaaaaa"

[delta "light-theme"]
    minus-style = syntax "#fbdada"
    minus-emph-style = syntax "#f6b6b6"
    plus-style = syntax "#d6ffd6"
    plus-emph-style = syntax "#adffad"
    map-styles = \
       bold purple => syntax "#feecf7", \
       bold blue => syntax "#e5dff6", \
       bold cyan => syntax "#d8fdf6", \
       bold yellow => syntax "#f4ffe0"
    zero-style = syntax
    whitespace-error-style = "#aaaaaa"

@texastoland
Copy link

texastoland commented May 17, 2023

FWIW it can be simplified if you don't care which light/dark theme:

[core]
pager = git delta

[interactive]
diffFilter = git delta --color-only

[alias]
delta = !delta $(defaults read -g AppleInterfaceStyle &>/dev/null || echo --light)

# or to ensure dark is default in Codespaces (Linux)
delta = !delta $(defaults read -g AppleInterfaceStyle 2>&1 | grep 'does not exist' > /dev/null && echo --light)

nishigori added a commit to nishigori/dotfiles that referenced this issue Jul 2, 2023
aykevl added a commit to aykevl/delta that referenced this issue Jul 29, 2023
Use the termbg crate to try to read the current terminal background
theme (light or dark) and use it as the default if no --light or --dark
was specified (or light=true in .gitconfig).

This doesn't work in all cases: delta is frequently used with a pipe, in
which case it's not really possible to obtain the background color using
OSC 11. The fallback is the environment variable COLORFGBG which is
supported by most terminals, but it doesn't immediately update when the
system theme changes (it only applies to newly opened terminals). Still,
I think this is better than assuming a dark background.

See dandavison#447 for discussion.
@aykevl
Copy link

aykevl commented Jul 29, 2023

I did some investigating in #1493 (comment) and came to the conclusion that reading the terminal background color using the xterm trick is not reliably possible when used together with less, because of a race condition between less and delta (see that comment for details). I guess @dandavison came to the same conclusion.

(stdin is not actually the issue, we can use stderr for read and write: the real issue is a race condition).

However, it would be possible to implement support for $COLORFGBG which is supported in a number of terminals. The main downside of that is that it is an environment variable which doesn't reflect dynamic changes to dark mode, but at least it is correct at the time the terminal starts (which may be more often than just assuming a dark background).

@dandavison
Copy link
Owner

dandavison commented Mar 12, 2024

Hi all, I'm about to merge a solution for this issue contributed by @bash in #1615. I'd really appreciate it if you could test out that branch and confirm it's working for you.

EDIT: now merged, so please test main

@texastoland
Copy link

texastoland commented Mar 15, 2024

now merged, so please test main

WFM with Lazygit integration in latest macOS on Intel ✅

@dandavison
Copy link
Owner

Thanks all. This is now fixed and released in delta 0.17.0 thanks to @bash's work in #1615.

(tmux users: we need to detach and reattach in order for delta to respond to a change in terminal background color. If anyone has solutions there please let this thread know)

@texastoland
Copy link

WFM with Lazygit integration in latest macOS on Intel ✅

I must have been using my old config. delta --detect-dark-light always (or auto) is always dark for me.

@bash
Copy link
Contributor

bash commented Mar 25, 2024

@texastoland Yepp, unfortunately automatic detection does not work inside of lazygit, see also #1664.

@texastoland
Copy link

Informative thread thank you 🙏🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.