summaryrefslogtreecommitdiff
path: root/bin/theme
diff options
context:
space:
mode:
Diffstat (limited to 'bin/theme')
-rwxr-xr-xbin/theme554
1 files changed, 554 insertions, 0 deletions
diff --git a/bin/theme b/bin/theme
new file mode 100755
index 0000000..39f25d1
--- /dev/null
+++ b/bin/theme
@@ -0,0 +1,554 @@
+#!/bin/bash
+
+set -euo pipefail
+
+: ${HOME:="/Users/$(whoami)"}
+: ${XDG_CONFIG_HOME:=$HOME/.config}
+: ${XDG_DATA_HOME:=$HOME/.local/share}
+
+
+_sed_i() {
+ if [[ "$(uname)" == "Darwin" ]]; then
+ sed -i '' "$@"
+ else
+ sed -i'' "$@"
+ fi
+}
+
+is_auto_dark_mode() {
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ defaults read -g AppleInterfaceStyleSwitchesAutomatically >/dev/null 2>&1
+ else
+ true
+ fi
+}
+is_ubuntu_desktop() {
+ [[ "$(lsb_release -is)" == "Ubuntu" ]] \
+ && dpkg -l | grep -q desktop
+}
+is_dark_mode() {
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ defaults read -g AppleInterfaceStyle >/dev/null 2>&1
+ elif [[ -n "${SSH_CONNECTION:-}" ]]; then
+ true
+ elif is_ubuntu_desktop ; then
+ [[ "$(gsettings get org.gnome.desktop.interface color-scheme 2>&1)" != *'light'* ]]
+ else
+ true
+ fi
+}
+set_macos_dark_mode_to() {
+ osascript -e 'tell application "System Events" to tell appearance preferences to set dark mode to '"$1"
+}
+set_dark_mode() {
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ set_macos_dark_mode_to "true"
+ elif is_ubuntu_desktop ; then
+ gsettings set org.gnome.desktop.interface color-scheme prefer-dark > /dev/null || true
+ fi
+}
+unset_dark_mode() {
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ set_macos_dark_mode_to "false"
+ elif is_ubuntu_desktop ; then
+ gsettings set org.gnome.desktop.interface color-scheme prefer-light > /dev/null || true
+ fi
+}
+get_mode() {
+ local mode="${1:-}"
+ if [[ -z "$mode" ]]; then
+ if is_dark_mode ; then
+ mode="dark"
+ else
+ mode="light"
+ fi
+ fi
+ if [[ "$mode" != "dark" && "$mode" != "light" ]]
+ then
+ >&2 echo "ERROR: unsupported mode '$mode'"
+ exit 1
+ fi
+ if [[ "${mode}" == 'dark' ]] ; then
+ echo "dark"
+ else
+ echo "light"
+ fi
+}
+set_mode() {
+ local mode="${1:-}"
+ if [[ "$(get_mode "$mode")" == 'dark' ]]
+ then
+ set_dark_mode
+ else
+ unset_dark_mode
+ fi
+}
+
+
+
+# List the available color config options
+list_colors() {
+ local ext="$1"
+ local dir="$2"
+ if [ ! -e "$dir" ] ; then
+ echo "(none)"
+ return
+ fi
+ find "${dir}/" -mindepth 1 -type f -print0 \
+ | xargs -r -0 -n 1 basename \
+ | sed 's/'"$ext"'$//' \
+ | sort
+}
+# Select the first readable file from a list of filepaths
+# specified as arguments.
+first_file_in() {
+ cd "$1"
+ shift
+ while [[ $# -ne 0 ]]
+ do
+ if [[ -r "$1" ]]
+ then
+ echo "$(pwd)/$1"
+ return 0
+ else
+ shift
+ fi
+ done
+ return 1
+}
+color_file() {
+ local dir="$1"
+ local ext="$2"
+ local colors="$3"
+ local mode="$4"
+ if ! first_file_in "$dir" \
+ "${colors}-${mode}${ext}" \
+ "${colors}${mode}${ext}" \
+ "${colors}${ext}" \
+ "${mode}${ext}"
+ then
+ >&2 echo "ERROR: unable to load $ext colors for $colors $mode from $dir"
+ return 1
+ fi
+}
+# Create or replace colors in a given config file from another
+# file specifying the color config content.
+set_colors_in_config() {
+ config_file="$1"
+ colors_file="$2"
+ line_comment_prefix="${3:-## }"
+ if [[ -L "$config_file" ]]
+ then
+ config_file="$(readlink "$config_file")"
+ fi
+ mkdir -p "$(dirname "$config_file")"
+ if [[ -r "$config_file" ]]
+ then
+ _sed_i '/GEN COLORS START/,/GEN COLORS END/d' "$config_file"
+ fi
+ cat >> "$config_file" <<EOF
+$line_comment_prefix GEN COLORS START
+$line_comment_prefix from: $colors_file
+$(cat "$colors_file")
+$line_comment_prefix GEN COLORS END
+EOF
+}
+
+alacritty_ext='.toml'
+alacritty_cfg="$XDG_DATA_HOME/alacritty/color${alacritty_ext}"
+alacritty_colors_dir1="$XDG_CONFIG_HOME/alacritty/colors"
+alacritty_colors_dir2="$HOME/src/alacritty/alacritty-theme/themes"
+alacritty_colors() {
+ {
+ list_colors "${alacritty_ext}" "$alacritty_colors_dir1"
+ list_colors "${alacritty_ext}" "$alacritty_colors_dir2"
+ } | sort
+}
+alacritty_color_file() {
+ color_file "${alacritty_colors_dir1}" "${alacritty_ext}" "$1" "$2" 2>/dev/null || \
+ color_file "${alacritty_colors_dir2}" "${alacritty_ext}" "$1" "$2" 2>/dev/null || \
+ color_file "${alacritty_colors_dir1}" "${alacritty_ext}" "$1" "$2"
+}
+alacritty_color_check_exists() {
+ alacritty_color_file "$1" "$2" > /dev/null
+}
+alacritty_colors_set() {
+ set_colors_in_config "${alacritty_cfg}" "$(alacritty_color_file "$1" "$2")"
+ touch "$XDG_CONFIG_HOME/alacritty/alacritty${alacritty_ext}"
+}
+
+bat_ext='.sh'
+bat_cfg="$XDG_DATA_HOME/bat/color.sh"
+bat_colors_dir="$XDG_CONFIG_HOME/bat/colors"
+bat_colors() {
+ # list_colors "${bat_ext}" "$bat_colors_dir"
+ bat --list-themes | cat
+}
+bat_color_file() {
+ color_file "${bat_colors_dir}" "${bat_ext}" "$1" "$2"
+}
+bat_color_check_exists() {
+ bat_color_file "$1" "$2" > /dev/null
+}
+bat_colors_set() {
+ local mode="$2"
+
+ # best for light: Coldark-Cold, Github, Monokai Extended Light, OneHalfLight, gruvbox-light
+ # best for dark: 1337, Coldark-Dark, Monokai Extended, OneHalfDark, gruvbox-dark
+ # works well with both: ansi
+ local theme="1337"
+ if [[ "$mode" == "light" ]]
+ then
+ theme="gruvbox-light"
+ fi
+
+ [[ -d "$(dirname "${bat_cfg}")" ]] || mkdir -p "$(dirname "${bat_cfg}")"
+ echo "--theme='$theme'" > "${bat_cfg}"
+}
+
+tmux_ext='.conf'
+tmux_cfg="$XDG_DATA_HOME/tmux/color.conf"
+tmux_colors_dir="$XDG_CONFIG_HOME/tmux/colors"
+tmux_colors() {
+ list_colors "${tmux_ext}" "$tmux_colors_dir"
+}
+tmux_color_file() {
+ color_file "${tmux_colors_dir}" "${tmux_ext}" "$1" "$2"
+}
+tmux_color_check_exists() {
+ tmux_color_file "$1" "$2" > /dev/null
+}
+tmux_colors_set() {
+ set_colors_in_config "${tmux_cfg}" "$(tmux_color_file "$1" "$2")"
+ tmux source-file "${tmux_cfg}"
+}
+
+neovim_ext='.lua'
+neovim_cfg="${XDG_DATA_HOME}/nvim/colors.lua"
+neovim_colors_dir="$XDG_CONFIG_HOME/nvim/colors"
+neovim_colors() {
+ list_colors "${neovim_ext}" "$neovim_colors_dir"
+}
+neovim_color_file() {
+ color_file "${neovim_colors_dir}" "${neovim_ext}" "$1" "$2"
+}
+neovim_color_check_exists() {
+ neovim_color_file "$1" "$2" 2>/dev/null > /dev/null
+}
+neovim_colors_set() {
+ colors="$1"
+ mode="$2"
+ if neovim_color_check_exists "${colors}" "${mode}"
+ then
+ set_colors_in_config "${neovim_cfg}" "$(neovim_color_file "$1" "$2")" '-- '
+ else
+ cat <<EOF > "${neovim_cfg}"
+-- GEN COLORS START
+local theme_mode = "$mode"
+local theme_colors = "$colors"
+$(cat "${neovim_colors_dir}/default.lua")
+-- GEN COLORS END
+EOF
+ fi
+}
+
+ghostty_cfg="$XDG_DATA_HOME/ghostty/color"
+ghostty_colors_dir="$XDG_CONFIG_HOME/ghostty/colors"
+# Check if a custom Ghostty color file exists
+ghostty_custom_color_file() {
+ local colors="$1"
+ local mode="$2"
+
+ # Try to find custom color file (similar to other tools)
+ if ! first_file_in "$ghostty_colors_dir" \
+ "${colors}-${mode}" \
+ "${colors}${mode}" \
+ "${colors}"
+ then
+ return 1
+ fi
+}
+# Map colorscheme names to Ghostty built-in theme names
+ghostty_theme_name() {
+ local colors="$1"
+ local mode="$2"
+
+ # Direct mappings for common colorschemes
+ case "$colors" in
+ molokai)
+ # echo "Monokai Soda"
+ # echo "Monokai Pro Spectrum"
+ echo "Brogrammer"
+ ;;
+ gruvbox)
+ if [[ "$mode" == "dark" ]]; then
+ echo "Gruvbox Dark"
+ else
+ echo "Gruvbox Light"
+ fi
+ ;;
+ papercolor)
+ if [[ "$mode" == "dark" ]]; then
+ echo "GitHub Dark Default"
+ else
+ echo "GitHub Light Default"
+ fi
+ ;;
+ solarized)
+ if [[ "$mode" == "dark" ]]; then
+ echo "Solarized Dark Patched"
+ else
+ echo "Builtin Solarized Light"
+ fi
+ ;;
+ nord)
+ echo "Nord"
+ ;;
+ dracula)
+ echo "Dracula"
+ ;;
+ *)
+ # Fallback: try to find a matching theme name (case-insensitive)
+ local search_term="$colors"
+ ghostty +list-themes --plain 2>/dev/null | sed 's/ (resources)//' | grep -i "^${search_term}$" | head -n 1 || echo ""
+ ;;
+ esac
+}
+ghostty_colors() {
+ {
+ # List custom color files first
+ if [[ -d "$ghostty_colors_dir" ]]; then
+ find "$ghostty_colors_dir" -mindepth 1 -type f -print0 \
+ | xargs -r -0 -n 1 basename \
+ | sort
+ fi
+ # Then list built-in themes
+ ghostty +list-themes --plain 2>/dev/null | sed 's/ (resources)//'
+ } | sort -u || echo "(ghostty not found)"
+}
+ghostty_color_check_exists() {
+ local colors="$1"
+ local mode="$2"
+
+ # # First check for custom color file
+ # if ghostty_custom_color_file "$colors" "$mode" >/dev/null 2>&1; then
+ # return 0
+ # fi
+
+ # Then check for built-in theme
+ local theme_name
+ theme_name="$(ghostty_theme_name "$colors" "$mode")"
+ if [[ -n "$theme_name" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+ghostty_colors_set() {
+ local colors="$1"
+ local mode="$2"
+ local custom_file
+ local theme_name
+
+ mkdir -p "$(dirname "$ghostty_cfg")"
+
+ # First priority: use custom color file if it exists
+ if custom_file="$(ghostty_custom_color_file "$colors" "$mode" 2>/dev/null)"; then
+ cat > "$ghostty_cfg" <<EOF
+# Generated by theme script
+# Colorscheme: $colors ($mode)
+# Using custom color file: $custom_file
+$(cat "$custom_file")
+EOF
+ else
+ # Second priority: use built-in theme
+ theme_name="$(ghostty_theme_name "$colors" "$mode")"
+
+ if [[ -z "$theme_name" ]]; then
+ >&2 echo "WARNING: No Ghostty theme found for '$colors' in $mode mode, skipping Ghostty theme update"
+ return 0
+ fi
+
+ cat > "$ghostty_cfg" <<EOF
+# Generated by theme script
+# Colorscheme: $colors ($mode)
+# Using built-in theme: $theme_name
+theme = $theme_name
+EOF
+ fi
+
+ # Trigger Ghostty reload
+ local script_dir
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+ "$script_dir/ghostty-reload"
+}
+
+
+
+# default colors file saves the local colors preference for each mode
+default_colors_file_dark="${XDG_DATA_HOME}/dark-colors"
+default_colors_file_light="${XDG_DATA_HOME}/light-colors"
+get_default_colors_file() {
+ local mode="${1-:}"
+ if [[ "$(get_mode "$mode")" == "dark" ]] ; then
+ echo "${default_colors_file_dark}"
+ else
+ echo "${default_colors_file_light}"
+ fi
+}
+get_colors() {
+ local mode="${1:-}"
+ local file
+ file="$(get_default_colors_file "$mode" )"
+ if [[ -r "$file" ]]
+ then
+ cat "$file"
+ else
+ is_dark_mode && echo "molokai" || echo "papercolor"
+ fi
+}
+common_colors() {
+ local col com aux types
+ col="$(mktemp)"
+ com="$(mktemp)"
+ aux="$(mktemp)"
+ types=(
+ bat
+ tmux
+ neovim
+ alacritty
+ ghostty
+ )
+ for typ in "${types[@]}"; do
+ echo "[[ $typ ]]"
+ "${typ}_colors" | sort | tee "${col}"
+ if [[ "$(wc -l < "${com}")" -gt 0 ]]; then
+ comm -12 "${com}" "${col}" > "${aux}"
+ cp "${aux}" "${com}"
+ else
+ cp "${col}" "${com}"
+ fi
+ echo ""
+ done
+ echo "[[ common ]]"
+ cat "${com}"
+ rm "${com}" "${aux}"
+}
+color_check_exists() {
+ local colors="$1"
+ local mode="$2"
+ if ! tmux_color_check_exists "$colors" "$mode"; then
+ >&2 echo "ERROR: tmux colors '$colors' not found for mode '$mode'"
+ return 1
+ fi
+ if ! alacritty_color_check_exists "$colors" "$mode"; then
+ >&2 echo "ERROR: alacritty colors '$colors' not found for mode '$mode'"
+ return 1
+ fi
+ # neovim and bat have fallback mechanisms, so don't require strict checking
+ return 0
+}
+set_colors() {
+ local mode="$1"
+ local colors="$2"
+ if [[ -z "$mode" ]]; then
+ mode="$(get_mode)"
+ fi
+ if [[ -z "$colors" ]]; then
+ colors="$(get_colors "$mode")"
+ fi
+ color_check_exists "$colors" "$mode"
+
+ bat_colors_set "$colors" "$mode"
+ tmux_colors_set "$colors" "$mode"
+ neovim_colors_set "$colors" "$mode"
+ alacritty_colors_set "$colors" "$mode"
+ ghostty_colors_set "$colors" "$mode"
+
+ local colors_file
+ colors_file="$(get_default_colors_file "$mode")"
+ mkdir -p "$(dirname "$colors_file")"
+ echo "$colors" > "$colors_file" 2>/dev/null || true
+}
+
+set_theme() {
+ local mode=$1
+ local colors=$2
+
+ set_mode "$mode"
+ set_colors "$mode" "$colors"
+
+ echo "Set theme to $(get_mode "$mode") $(get_colors "$mode")"
+}
+
+print_help() {
+ local s_name
+ s_name="$(basename $0)"
+ cat <<EOF
+Help
+
+Current set theme:
+
+ $s_name
+
+Current mode
+
+ $s_name --mode
+
+Current colorscheme
+
+ $s_name --colors
+
+Set theme
+
+ $s_name --set [light|dark] [colorscheme]
+
+List available colorschemes
+
+ $s_name --list
+ $s_name --list-bat
+ $s_name --list-tmux
+ $s_name --list-neovim
+ $s_name --list-alacritty
+ $s_name --list-ghostty
+EOF
+}
+
+if test $# -eq 0
+then
+ echo "$(get_mode) $(get_colors)"
+ exit 0
+fi
+
+while test $# -gt 0
+do
+ case $1 in
+ --mode)
+ get_mode; exit 0; ;;
+ --colors)
+ get_colors; exit 0; ;;
+ --set)
+ set_theme "${2:-}" "${3:-}"; exit 0; ;;
+ --list-alacritty)
+ alacritty_colors; exit 0; ;;
+ --list-tmux)
+ tmux_colors; exit 0; ;;
+ --list-neovim)
+ neovim_colors; exit 0; ;;
+ --list-bat)
+ bat_colors; exit 0; ;;
+ --list-ghostty)
+ ghostty_colors; exit 0; ;;
+ --list)
+ common_colors; exit 0; ;;
+ --help|-h)
+ print_help; exit 0; ;;
+ *)
+ print_help; exit 1; ;;
+ esac
+ shift
+done
+
+>&2 echo "ERROR: Unexpected state!"
+exit 1
+