aboutsummaryrefslogtreecommitdiff
path: root/dotfiles/.local/bin/shtheme
diff options
context:
space:
mode:
authorYaroslav de la Peña Smirnov <yps@yaroslavps.com>2022-09-20 21:26:44 +0300
committerYaroslav de la Peña Smirnov <yps@yaroslavps.com>2022-09-20 21:26:44 +0300
commit24ac3cd2e11c114ef4c8ae1284953c8928f4a820 (patch)
treec43823a23d02dca5f57568b9167d7a37e1ad2fbf /dotfiles/.local/bin/shtheme
parent2b1eac8bc54dbc514e24c64143dc34ef5f1caca5 (diff)
downloadswayrice-24ac3cd2e11c114ef4c8ae1284953c8928f4a820.tar.gz
swayrice-24ac3cd2e11c114ef4c8ae1284953c8928f4a820.zip
A way to change colors dynamically with foot
Also some cleanup
Diffstat (limited to 'dotfiles/.local/bin/shtheme')
-rwxr-xr-xdotfiles/.local/bin/shtheme669
1 files changed, 669 insertions, 0 deletions
diff --git a/dotfiles/.local/bin/shtheme b/dotfiles/.local/bin/shtheme
new file mode 100755
index 0000000..2071019
--- /dev/null
+++ b/dotfiles/.local/bin/shtheme
@@ -0,0 +1,669 @@
+#!/bin/sh
+
+# Written by Aetnaeus.
+# Source: https://github.com/lemnos/theme.sh.
+# Licensed under the WTFPL provided this notice is preserved.
+
+# Find a broken theme? Want to add a missing one? PRs are welcome.
+
+VERSION=v1.1.5
+
+# Use truecolor sequences to simulate the end result.
+
+preview() {
+ awk -F": " -v target="$1" '
+ BEGIN {
+ "tput cols" | getline nc
+ "tput lines" | getline nr
+ nc = int(nc)
+ nr = int(nr)
+ }
+
+ /^# Themes/ { start++;next }
+ !start { next }
+
+ function hextorgb(s) {
+ hexchars = "0123456789abcdef"
+ s = tolower(s)
+
+ r = (index(hexchars, substr(s, 2, 1))-1)*16+(index(hexchars, substr(s, 3, 1))-1)
+ g = (index(hexchars, substr(s, 4, 1))-1)*16+(index(hexchars, substr(s, 5, 1))-1)
+ b = (index(hexchars, substr(s, 6, 1))-1)*16+(index(hexchars, substr(s, 7, 1))-1)
+ }
+
+ function fgesc(col) {
+ hextorgb(col)
+ return sprintf("\x1b[38;2;%d;%d;%dm", r, g, b)
+ }
+
+ function bgesc(col) {
+ hextorgb(col)
+ return sprintf("\x1b[48;2;%d;%d;%dm", r, g, b)
+ }
+
+ $0 == target {s++}
+
+ s && /^foreground:/ { fg = $2 }
+ s && /^background:/ { bg = $2 }
+ s && /^[0-9]+:/ { a[$1] = $2 }
+
+ /^ *$/ {s=0}
+
+ function puts(s, len, i, normesc, filling) {
+ normesc = sprintf("\x1b[0m%s%s", fgesc(fg), bgesc(bg))
+
+ len=s
+ gsub(/\033\[[^m]*m/, "", len)
+ len=length(len)
+
+ filling=""
+ for(i=0;i<(nc-len);i++) filling=filling" "
+
+ printf "%s%s%s%s\n", normesc, s, normesc, filling, ""
+ nr--
+ }
+
+ END {
+ puts("")
+ for (i = 0;i<16;i++)
+ puts(sprintf(" %s Color %d\x1b[0m", fgesc(a[i]), i))
+
+ # Note: Some terminals use different colors for bolded text and may produce slightly different ls output.
+
+ puts("")
+ puts(" # ls --color -F")
+ puts(sprintf(" file"))
+ puts(sprintf(" \x1b[1m%sdir/", fgesc(a[4])))
+ puts(sprintf(" \x1b[1m%sexecutable", fgesc(a[10])))
+ puts(sprintf(" \x1b[1m%ssymlink\x1b[0m%s%s", fgesc(a[6]), fgesc(fg), bgesc(bg)))
+
+
+ while(nr > 0) puts("")
+
+ printf "\x1b[0m"
+ }
+ ' < "$0"
+}
+
+# Alphabetize and dedupe theme list.
+
+normalize_themes() {
+ awk '
+ # We could eliminate the sorting logic by using gnu extensions but that would reduce portability.
+
+ function cmp(a,b,ordTbl, i,c1,c2,n) {
+ n = length(a) > length(b) ? length(b) : length(a)
+ for(i = 1;i <= n;i++) {
+ c1 = substr(a, i, 1)
+ c2 = substr(b, i, 1)
+
+ if(c1 != c2)
+ return ordTbl[c1] < ordTbl[c2]
+ }
+
+ return length(a) < length(b)
+ }
+
+ function sort(a,n, i,j,tmp,ordTbl) {
+ for(i = 0;i < 256;i++) ordTbl[sprintf("%c", i)] = i
+
+ for(i = 0;i < n;i++) {
+ tmp = a[i]
+ j = i-1
+ while(j >= 0 && cmp(tmp, a[j], ordTbl)) {
+ a[j+1] = a[j]
+ j--
+ }
+
+ a[j+1] = tmp
+ }
+ }
+
+ function sortKeys(a,keys, n,k) {
+ for(k in a)
+ keys[n++] = k
+ sort(keys, n)
+ return n
+ }
+
+ /^ *$/ { inTheme = 0;next }
+ !inTheme { name = $0;inTheme=1;themes[name] = "";next }
+ inTheme { themes[name] = themes[name]$0"\n" }
+
+ END {
+ n = sortKeys(themes, names)
+
+ print ""
+ for(i = 0;i < n;i++) {
+ print names[i]
+ print tolower(themes[names[i]])
+ }
+ }
+ ' "$@"
+}
+
+# Generate themes from one or more supplied kitty config files.
+
+generate_themes() {
+ awk -v argc=$# '
+ function chkProp(prop) {
+ if(!props[prop]) {
+ printf "ABORTING: %s is missing required property '\''%s'\''\n", currentFile, prop > "/dev/stderr"
+ aborted++
+ exit -1
+ }
+ }
+
+ function printTheme( name,i,prop) {
+ name = currentFile
+ gsub(/.*\//, "", name)
+ gsub(/\.conf$/, "", name)
+
+ print name
+
+ for (i = 0;i < 16;i++) {
+ prop = sprintf("color%d", i)
+
+ chkProp(prop)
+ printf "%d: %s\n", i, props[prop]
+ }
+
+ chkProp("foreground")
+ chkProp("background")
+ chkProp("cursor")
+
+ print "foreground: "props["foreground"]
+ print "background: "props["background"]
+ print "cursor: "props["cursor"]
+ print ""
+ }
+
+ FILENAME != currentFile {
+ if(currentFile)
+ printTheme()
+
+ currentFile = FILENAME
+ delete props
+ }
+
+ { props[$1] = $2 }
+
+ END { if(!aborted) printTheme() }
+ ' "$@"
+}
+
+# Add themes to the script from one or more supplied kitty config files.
+
+add() {
+ tmp1="$(mktemp)"
+ tmp2="$(mktemp)"
+
+ awk 'i { print } /^# Themes/ { i++ }' "$0" > "$tmp1"
+ echo "" >> "$tmp1"
+ generate_themes "$@" >> "$tmp1" || exit $?
+
+ awk '{print} /^# Themes/ { exit }' "$0" > "$tmp2"
+ normalize_themes "$tmp1" >> "$tmp2"
+
+
+ rm "$tmp1"
+ cat "$tmp2" > "$0" || exit $?
+
+ printf 'Successfully annexed %d themes. More! Feed me more!\n' $#
+}
+
+preview2() {
+ INHIBIT_THEME_HIST=1 "$0" "$1"
+
+ printf '\033[30mColor 0\n'
+ printf '\033[31mColor 1\n'
+ printf '\033[32mColor 2\n'
+ printf '\033[33mColor 3\n'
+ printf '\033[34mColor 4\n'
+ printf '\033[35mColor 5\n'
+ printf '\033[36mColor 6\n'
+ printf '\033[37mColor 7\n'
+
+ printf '\033[90mColor 8\n'
+ printf '\033[91mColor 9\n'
+ printf '\033[92mColor 10\n'
+ printf '\033[93mColor 11\n'
+ printf '\033[94mColor 12\n'
+ printf '\033[95mColor 13\n'
+ printf '\033[96mColor 14\n'
+ printf '\033[97mColor 15\n'
+
+ printf '\n\033[0m'
+ printf '# ls --color -F\n'
+ printf ' file\n'
+ printf ' \033[01;34mdir/\033[0m\n'
+ printf ' \033[01;32mexecutable\033[0m*\n'
+ printf ' \033[01;36msymlink\033[0m\n'
+
+ printf '\033[0m'
+}
+
+# Consumes a theme.sh definition from STDIN and applies it.
+
+apply_theme() {
+awk '
+ function tmuxesc(s) { return sprintf("\033Ptmux;\033%s\033\\", s) }
+ function normalize_term() {
+ # Term detection voodoo
+
+ if(ENVIRON["TERM_PROGRAM"] == "iTerm.app")
+ term="iterm"
+ else if(ENVIRON["TMUX"]) {
+ "tmux display-message -p \"#{client_termname}\"" | getline term
+ "tmux display-message -p \"#{client_termtype}\"" | getline termname
+
+ if(substr(termname, 1, 5) == "iTerm")
+ term="iterm"
+ is_tmux++
+ } else
+ term=ENVIRON["TERM"]
+ }
+
+ BEGIN {
+ normalize_term()
+
+ if(term == "iterm") {
+ bgesc="\033]Ph%s\033\\"
+ fgesc="\033]Pg%s\033\\"
+ colesc="\033]P%x%s\033\\"
+ curesc="\033]Pl%s\033\\"
+ } else {
+ #Terms that play nice :)
+
+ fgesc="\033]10;#%s\007"
+ bgesc="\033]11;#%s\007"
+ curesc="\033]12;#%s\007"
+ colesc="\033]4;%d;#%s\007"
+ }
+
+ if(is_tmux) {
+ fgesc=tmuxesc(fgesc)
+ bgesc=tmuxesc(bgesc)
+ curesc=tmuxesc(curesc)
+ colesc=tmuxesc(colesc)
+ }
+ }
+
+ /^foreground:/ { printf fgesc, substr($2, 2) > "/dev/tty" }
+ /^background:/ { printf bgesc, substr($2, 2) > "/dev/tty" }
+ /^cursor:/ { printf curesc, substr($2, 2) > "/dev/tty" }
+ /^[0-9]+:/ { printf colesc, $1, substr($2, 2) > "/dev/tty" }
+'
+}
+
+# Sets the current theme given a name and does the requisite bookkeeping.
+
+set_current_theme() {
+ awk -F": " -v target="$1" -v script="$0" '
+ /^# Themes/ { start++;next; }
+ !start { next }
+
+ $0 == target { found++;next; }
+
+ found { theme = theme $0 "\n" }
+ found && /^ *$/ { exit }
+
+ END {
+ if(found) {
+ printf "%s", theme | script
+
+ config_dir = (ENVIRON["XDG_CONFIG_HOME"] ? ENVIRON["XDG_CONFIG_HOME"] : ENVIRON["HOME"])
+
+ histfile = config_dir"/.theme_history"
+ inhibit_hist=ENVIRON["INHIBIT_THEME_HIST"]
+
+ if(!inhibit_hist) {
+ while((getline < histfile) > 0)
+ if($0 != target)
+ out = out $0 "\n"
+ close(histfile)
+
+ out = out target
+ print out > histfile
+ }
+ } else {
+ printf "Theme not found: %s\n", target > "/dev/stderr"
+ exit(-1)
+ }
+ }
+ ' < "$0"
+}
+
+# Dump the current theme in a format consumable by theme.sh
+# by attempting to read it from the terminal.
+#
+# NOTE: Many terms don't support this properly (e.g alacritty)
+
+# Refs
+
+# https://github.com/microsoft/terminal/issues/3718
+# https://github.com/alacritty/alacritty/blob/master/alacritty_terminal/src/ansi.rs#L972
+
+print_current_theme() {
+ awk '
+ function print_response(s) {
+ names["10;"] = "foreground"
+ names["11;"] = "background"
+ names["12;"] = "cursor"
+ for (i = 0; i < 16; i++)
+ names[sprintf("4;%d;", i)] = i
+
+ split(s, a, "]")
+ for (i in a) {
+ if (match(a[i], /rgb:/)) {
+ key = substr(a[i], 1, RSTART-1)
+
+ r=substr(a[i], RSTART+4, 2)
+ g=substr(a[i], RSTART+9, 2)
+ b=substr(a[i], RSTART+14, 2)
+
+ printf "%s: %s\n", names[key], "#"r g b
+ }
+ }
+ }
+
+ # We cant just use RS/getline for this since
+ # mawk does input buffering :(.
+
+ function read_response() {
+ buf = ""
+
+ # Accrue data until we encounter the terminating CSI response
+ while ((end=index(buf,"[")) == 0) {
+ # poor POSIX mans read :/
+ cmd="dd if=/dev/tty bs=1024 count=1 2> /dev/null"
+
+ while (cmd|getline data)
+ buf = buf data
+
+ close(cmd)
+ }
+
+ buf = substr(buf, 1, end-1)
+ return buf
+ }
+
+ BEGIN {
+ system("stty cbreak -echo")
+
+ tty = "/dev/tty"
+
+ # Yo dawg, I heard you like multiplexers...
+ if (ENVIRON["TMUX"]) {
+ # If we are running inside tmux we sent the request sequences
+ # to the currently attached terminal. Note that we still
+ # read the result from the virtual terminal.
+
+ # Flow:
+ # theme.sh (request) -> tty (response) -> pts (response) -> theme.sh
+ # where pts is the tmux pseudoterminal.
+
+ "tmux display-message -p \"#{client_tty}\""|getline tty
+ }
+
+ # Terminals may ignore these.
+
+ for(i=0;i<16;i++)
+ printf "\033]4;%d;?\007", i > tty
+
+ printf "\033]10;?\007" > tty
+ printf "\033]11;?\007" > tty
+ printf "\033]12;?\007" > tty
+
+ # Use a CSI DA1 sequence (supported by all terms)
+ # as a sentinel value to indicate end-of-response.
+ # (assumes request-response order is fifo)
+
+ printf "\033[c" > tty
+
+ print_response(read_response())
+
+ system("stty -cbreak echo")
+ }
+ '
+}
+
+isColorTerm() {
+ if [ -z "$TMUX" ]; then
+ [ -n "$COLORTERM" ]
+ else
+ tmux display-message -p '#{client_termfeatures}'|grep -q RGB
+ fi
+}
+
+list() {
+ case "$filterFlag" in
+ --light) filter=2 ;;
+ --dark) filter=1 ;;
+ *) filter=0 ;;
+ esac
+
+ awk -v filter="$filter" -F": " '
+ BEGIN {
+ config_dir = ENVIRON["XDG_CONFIG_HOME"] ? ENVIRON["XDG_CONFIG_HOME"] : ENVIRON["HOME"]
+
+ histfile = config_dir"/.theme_history"
+ while((getline < histfile) > 0) {
+ mru[nmru++] = $0
+ mruIndex[$0] = 1
+ }
+ }
+
+ function luma(s, r,g,b,hexchars) {
+ hexchars = "0123456789abcdef"
+ s = tolower(s)
+
+ r = (index(hexchars, substr(s, 2, 1))-1)*16+(index(hexchars, substr(s, 3, 1))-1)
+ g = (index(hexchars, substr(s, 4, 1))-1)*16+(index(hexchars, substr(s, 5, 1))-1)
+ b = (index(hexchars, substr(s, 6, 1))-1)*16+(index(hexchars, substr(s, 7, 1))-1)
+
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
+ }
+
+ /^# Theme/ { st++;next }
+ !st { next }
+
+ /^ *$/ { inner = 0;next }
+ !inner { name = $0;inner++;next }
+
+ /^background/ {
+ if((filter == 1 && luma($2) > 130) ||
+ (filter == 2 && luma($2) <= 130))
+ next
+
+ candidates[name] = 1
+ }
+
+ END {
+ for(c in candidates) {
+ if(!mruIndex[c])
+ print(c)
+ }
+
+ for(i = 0;i < nmru;i++)
+ if(candidates[mru[i]])
+ print(mru[i])
+ }
+ ' < "$0"
+}
+
+
+if [ -z "$1" ]; then
+ if [ -t 0 ]; then
+ echo "usage: $(basename "$0") [-v] [-h] <option>|<theme>"
+ exit 255
+ else
+ apply_theme
+ exit 0
+ fi
+fi
+
+case "$1" in
+ --dark|--light)
+ filterFlag=$1
+ shift
+ ;;
+esac
+
+case "$1" in
+-h|--help)
+ cat << "!"
+usage: theme.sh [--light] | [--dark] <option> | <theme>
+
+ If <theme> is provided it will immediately be set. Otherwise --dark or
+ --light optionally act as filters on the supplied option. Theme history is
+ stored in ~/.theme_history or ($XDG_CONFIG_HOME/.theme_history if set) by
+ default and will be used for ordering the otherwise alphabetical theme list
+ in the relevant options (-l/-i/-i2).
+
+ E.G:
+ 'theme.sh --dark -i'
+
+ will start an interactive selection of dark themes with the user's
+ most recently selected themes at the bottom of the list.
+
+ Theme definitions consistent with the internal format can also be piped
+ directly into the script.
+
+ E.G:
+
+ # theme.sh < input
+
+ Where input has the form:
+
+ 0: #4d4d4d
+ 1: #4d4d4d
+ ...
+ foreground: #dcdccc
+ background: #3f3f3f
+ cursor: #dcdccc
+
+OPTIONS
+ -l,--list Print all available themes.
+ -i,--interactive Start the interactive selection mode (requires fzf).
+ -i2,--interactive2 Interactive mode #2. This shows the theme immediately
+ instead of showing it in the preview window. Useful
+ if your terminal does have TRUECOLOR support.
+ -r,--random Set a random theme and print its name to stdout.
+ -a,--add <kitty config> Annexes the given kitty config file.
+ -p,--print-theme Attempt to read the current theme from the terminal
+ and print it to stdout in a format consumable by theme.sh.
+ NOTE: not all terminals support this option,
+ do not rely on it in scripts.
+ -v,--version Print the version and exit.
+
+SCRIPTING
+ If used from within a script, you will probably want to set
+ INHIBIT_THEME_HIST=1 to avoid mangling the user's theme history.
+!
+ ;;
+-p|--print-theme)
+ print_current_theme
+ ;;
+-i2|--interactive2)
+ command -v fzf > /dev/null 2>&1 || { echo "ERROR: -i requires fzf" >&2; exit 1; }
+ "$0" $filterFlag -l|fzf\
+ --tac\
+ --bind "enter:execute-silent($0 {})+accept"\
+ --bind "ctrl-c:execute($0 -l|tail -n1|xargs $0)+abort"\
+ --bind "esc:execute($0 {};echo {})+abort"\
+ --exact\
+ --no-sort\
+ --preview "$0 --preview2 {}"
+ ;;
+-r|--random)
+ # Sort -R is not portable :/
+
+ theme=$($0 $filterFlag -l|awk '{a[n++]=$0};END{srand();print(a[int(rand()*n)])}')
+ $0 "$theme"
+ echo "Theme: $theme"
+ ;;
+-i|--interactive)
+ command -v fzf > /dev/null 2>&1 || { echo "ERROR: -i requires fzf" >&2; exit 1; }
+ if ! isColorTerm; then
+ printf "WARNING: This does not appear to be a truecolor terminal, falling back to -i2
+ (use -i2 explicitly to get rid of this message or set COLORTERM)\n\n" >&2
+ "$0" $filterFlag -i2
+ else
+ "$0" $filterFlag -l|fzf\
+ --tac\
+ --exact\
+ --bind "ctrl-c:abort"\
+ --bind "esc:execute(echo {})+abort"\
+ --bind "enter:execute-silent($0 {})+accept"\
+ --no-sort\
+ --preview "$0 --preview {}"
+ fi
+ ;;
+-l|--list)
+ list
+ ;;
+-a|--add)
+ shift
+ add "$@"
+ ;;
+--preview2)
+ preview2 "$2"
+ ;;
+--preview)
+ preview "$2"
+ ;;
+-v|--version)
+ echo "$VERSION (original source https://github.com/lemnos/theme.sh)"
+ ;;
+*)
+ set_current_theme "$1"
+ ;;
+esac
+
+exit $?
+
+# Themes start here (avoid editing by hand)
+
+ultramar-dark
+0: #33333a
+1: #b73030
+2: #6d974b
+3: #b2872f
+4: #3f6e90
+5: #9c6992
+6: #5b8277
+7: #b0afa8
+8: #676775
+9: #c45c5c
+10: #92b078
+11: #e2b55a
+12: #81acc1
+13: #b48ead
+14: #7fac96
+15: #faf6e5
+foreground: #fcf8e2
+background: #151517
+cursor: #ffffff
+
+ultramar-light
+0: #151517
+1: #b73030
+2: #6d974b
+3: #b2872f
+4: #3f6e90
+5: #9c6992
+6: #5b8277
+7: #91908d
+8: #33333a
+9: #c45c5c
+10: #92b078
+11: #e2b55a
+12: #75a2b8
+13: #b48ead
+14: #7fac96
+15: #b0afa8
+foreground: #1b1e25
+background: #faf6e5
+cursor: #000000
+