delta icon indicating copy to clipboard operation
delta copied to clipboard

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

Open tshu-w opened this issue 3 years ago • 33 comments

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.

tshu-w avatar Dec 15 '20 06:12 tshu-w

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.

lourenci avatar Dec 17 '20 16:12 lourenci

@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.

tshu-w avatar Dec 17 '20 16:12 tshu-w

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 https://github.com/dandavison/delta/pull/340#issuecomment-744257639

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 avatar Dec 17 '20 17:12 dandavison

@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.

dandavison avatar Dec 18 '20 16:12 dandavison

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.

ghost avatar Dec 18 '20 16:12 ghost

@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.

dandavison avatar Dec 18 '20 16:12 dandavison

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!

ghost avatar Dec 18 '20 16:12 ghost

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 avatar Dec 18 '20 22:12 dandavison

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/*).

dandavison avatar Dec 22 '20 17:12 dandavison

@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.

tshu-w avatar Jan 09 '21 11:01 tshu-w

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?

sscherfke avatar Jan 21 '21 19:01 sscherfke

@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.

ghost avatar Jan 25 '21 02:01 ghost

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. https://github.com/sharkdp/bat/issues/1104

Could delta do the same?

skyfaller avatar Mar 06 '21 19:03 skyfaller

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.

dandavison avatar Mar 28 '21 00:03 dandavison

@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!

skyfaller avatar Mar 28 '21 00:03 skyfaller

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.)

dandavison avatar Mar 28 '21 00:03 dandavison

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.

jtroo avatar Apr 29 '21 19:04 jtroo

Thanks very much @jtroo. Should be fixed in https://github.com/dandavison/delta/pull/581. Any chance you could confirm that's behaving as expected for you?

dandavison avatar Apr 29 '21 21:04 dandavison

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.

dandavison avatar Apr 29 '21 21:04 dandavison

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

jtroo avatar Apr 29 '21 21:04 jtroo

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 avatar Apr 30 '21 02:04 dandavison

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

My config:

[delta]
  navigate = true
  syntax-theme = ansi

Output (light terminal): image

lourenci avatar May 20 '21 12:05 lourenci

@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).

jtroo avatar May 20 '21 21:05 jtroo

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 avatar May 20 '21 21:05 dandavison

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.

dandavison avatar May 20 '21 21:05 dandavison

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 avatar Mar 30 '22 07:03 chtenb

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

chtenb avatar Mar 30 '22 07:03 chtenb

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

floriankisser avatar Mar 30 '22 07:03 floriankisser

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.

dandavison avatar Mar 30 '22 22:03 dandavison

@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.

chtenb avatar Mar 31 '22 08:03 chtenb

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).

cben avatar Mar 31 '22 20:03 cben

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-ansi16. 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.

https://user-images.githubusercontent.com/1030961/163631100-50241cdb-eb00-4f93-9736-8cfadfbcea5b.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.

chtenb avatar Apr 15 '22 21:04 chtenb