888 lines
44 KiB
Bash
Executable File
888 lines
44 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Propagates the active THEME to applications that do not read the desktop
|
|
# portal.
|
|
#
|
|
# Most modern applications DO follow org.freedesktop.portal.Settings and need
|
|
# nothing from us for light-or-dark: GTK4/libadwaita, Qt6, Chromium and
|
|
# Electron all read org.freedesktop.appearance color-scheme, which
|
|
# xdg-desktop-portal-gtk serves from gsettings. Panama sets that, so those
|
|
# follow automatically.
|
|
#
|
|
# A scheme is not a theme, though. The portal can say "dark"; it cannot say
|
|
# "Catppuccin Mocha". Everything below is an application that carries its own
|
|
# palette, and every one of them used to be handed a copy of a Tokyo Night file
|
|
# -- which is why choosing any other theme restyled the shell and nothing else.
|
|
# They are now RENDERED from the active theme's palette instead:
|
|
#
|
|
# kitty current-theme.conf, plus a live `set-colors` over its socket
|
|
# tmux current-theme.conf, re-sourced into running servers
|
|
# btop a generated panama.theme, named by btop.conf's color_theme
|
|
# hyprlock the eight placeholders in hyprlock.conf.template
|
|
# GTK 3/4 the @define-color block in gtk.css, between the markers
|
|
# Vicinae panama-dark.toml / panama-light.toml in its data directory
|
|
# Firefox an Edge-Frfox override sheet, both schemes in one file
|
|
#
|
|
# panama-theme-apps dark|light [accent]
|
|
#
|
|
# The two arguments are the desktop's scheme and its nearest curated accent
|
|
# name. They remain the interface because that is what ColorScheme.qml knows
|
|
# and what GNOME's accent-color enum needs -- but the palette no longer comes
|
|
# from them. This script resolves the active theme itself, from settings.json
|
|
# and config/themes.json, through scripts/panama-palette. panama-lock resolves
|
|
# it exactly the same way, from the same function.
|
|
#
|
|
# kitty gets it twice: the generated include file so terminals opened later
|
|
# start correct, and a live `set-colors` over its control socket so terminals
|
|
# already open change now. Without the second, a theme change appears to do
|
|
# nothing until you open a new window.
|
|
#
|
|
# The tokyonight-* files under kitty/themes, tmux/themes and btop/themes are
|
|
# still shipped -- a user may name one directly -- but nothing copies them any
|
|
# more.
|
|
|
|
set -euo pipefail
|
|
|
|
# The accents and the theme resolver, from the one file that holds them.
|
|
# shellcheck source=./panama-palette
|
|
script_dir="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
|
|
source "$script_dir/panama-palette"
|
|
|
|
scheme="${1:-dark}"
|
|
case "$scheme" in
|
|
dark|light) ;;
|
|
*) printf 'usage: panama-theme-apps [dark|light] [accent]\n' >&2; exit 2 ;;
|
|
esac
|
|
|
|
# Falls back to blue for an unknown or omitted name, matching Theme.qml's own
|
|
# accentPair fallback -- so a caller that only ever knew about scheme (a stale
|
|
# ColorScheme.qml, a manual invocation) still gets a real accent instead of an
|
|
# empty string reaching sed.
|
|
accent="${2:-blue}"
|
|
case "$accent" in
|
|
blue|orchid|teal|green|amber|orange|rose|slate) ;;
|
|
*) accent=blue ;;
|
|
esac
|
|
|
|
config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
|
|
data_home="${XDG_DATA_HOME:-$HOME/.local/share}"
|
|
|
|
# ── The theme ────────────────────────────────────────────────────────────────
|
|
# The active one for everything that renders a single palette, and BOTH schemes
|
|
# for Firefox -- its stylesheet carries light-dark() pairs so the browser flips
|
|
# on its own, without this script being re-run.
|
|
eval "$(theme_vars "$(resolve_theme "$scheme")")"
|
|
eval "$(theme_vars "$(resolve_theme dark)" dark_)"
|
|
eval "$(theme_vars "$(resolve_theme light)" light_)"
|
|
|
|
# The accent pair falls back to the curated accent named on the command line.
|
|
# A stored custom theme is allowed to carry an accent and no palette; one that
|
|
# carries neither is still a real selection and should not render an empty
|
|
# colour into a config file.
|
|
theme_accent="$(hex_or "${theme_accent:-}" "$(accent_hex "$accent" "$scheme")")"
|
|
theme_secondary="$(hex_or "${theme_secondary:-}" "$(accent_secondary_hex "$accent" "$scheme")")"
|
|
dark_theme_accent="$(hex_or "${dark_theme_accent:-}" "$(accent_hex "$accent" dark)")"
|
|
light_theme_accent="$(hex_or "${light_theme_accent:-}" "$(accent_hex "$accent" light)")"
|
|
|
|
# Whether there is enough of a palette to render from at all. An unreadable
|
|
# themes.json is the only way to get here, and the honest answer is then to
|
|
# leave every application's existing colours alone rather than to write half a
|
|
# theme over them.
|
|
palette_ok=true
|
|
for token in bg bgDark bgHighlight bgPanel bgPopover fg fgDim fgMuted gutter \
|
|
accentAlt cyan teal green yellow orange red redDeep magenta pink; do
|
|
name="pal_$token"
|
|
[[ -n "${!name:-}" ]] || palette_ok=false
|
|
done
|
|
|
|
ansi_ok=true
|
|
for token in black red green yellow blue magenta cyan white \
|
|
brightBlack brightRed brightGreen brightYellow brightBlue \
|
|
brightMagenta brightCyan brightWhite; do
|
|
name="ansi_$token"
|
|
[[ -n "${!name:-}" ]] || ansi_ok=false
|
|
done
|
|
|
|
# Every token render_vicinae reads, in both schemes. The renderer runs under
|
|
# `set -u` in a subshell, so a token missing from one side is not a slightly
|
|
# duller launcher theme -- it aborts that render and the file is left as it was.
|
|
# The gate has to cover what the renderer actually reads, or it passes and the
|
|
# render fails anyway.
|
|
both_schemes_ok=true
|
|
for token in bg bgDark bgHighlight bgPanel bgPopover fg fgDim fgMuted gutter red \
|
|
accentAlt green magenta orange pink yellow cyan; do
|
|
for side in dark light; do
|
|
name="${side}_pal_$token"
|
|
[[ -n "${!name:-}" ]] || both_schemes_ok=false
|
|
done
|
|
done
|
|
|
|
# GTK and Firefox want rgba(), not hex, wherever a colour carries transparency.
|
|
rgba_of() {
|
|
printf 'rgba(%s, %s)' "$(hex_to_rgb "$1")" "$2"
|
|
}
|
|
|
|
# Text on a filled accent, in this theme's own grounds rather than in black and
|
|
# white: a pastel accent takes the theme's darkest surface, a saturated one
|
|
# takes its lightest text.
|
|
accent_fg="$(on_hex "$theme_accent" "${pal_bgDark:-}" "${pal_fg:-}")"
|
|
|
|
# Replace one file in place, but only when the result actually differs, and say
|
|
# which happened: "written", "unchanged", or "failed". Several of these targets
|
|
# are tracked files reached through the dotfile symlinks, and rewriting them
|
|
# with identical bytes on every accent sample would show up as a dirty working
|
|
# tree for no change at all.
|
|
#
|
|
# A word rather than an exit status because every caller is under `set -e`,
|
|
# where a helper that reports by returning 2 aborts the script instead.
|
|
replace_if_changed() {
|
|
local destination="$1" temporary="$2"
|
|
if [[ -e "$destination" ]] && cmp -s "$temporary" "$destination"; then
|
|
rm -f "$temporary"
|
|
printf 'unchanged'
|
|
return 0
|
|
fi
|
|
if mv "$temporary" "$destination" 2>/dev/null; then
|
|
printf 'written'
|
|
else
|
|
rm -f "$temporary"
|
|
printf 'failed'
|
|
fi
|
|
}
|
|
|
|
# ── hyprlock ─────────────────────────────────────────────────────────────────
|
|
# The lock screen. hyprlock is launched fresh on every lock (`pidof hyprlock ||
|
|
# hyprlock`), so it reads this file each time and needs no restart.
|
|
#
|
|
# The substitutions themselves live in panama-palette's render_hyprlock: this
|
|
# script, panama-lock and setup/scripts/link-dotfiles all need them, and when
|
|
# each carried its own copy the lock screen was the one that silently kept the
|
|
# old colours.
|
|
lock_dir="$config_home/hypr"
|
|
lock_template="$lock_dir/hyprlock.conf.template"
|
|
|
|
status_hyprlock="skipped"
|
|
if [[ -r "$lock_template" ]]; then
|
|
if render_hyprlock "$scheme" "$lock_template" "$lock_dir/hyprlock.conf" "$accent"; then
|
|
status_hyprlock="written"
|
|
else
|
|
status_hyprlock="failed"
|
|
fi
|
|
fi
|
|
|
|
# ── tmux ─────────────────────────────────────────────────────────────────────
|
|
# tmux.conf sources current-theme.conf, and that file is machine state rather
|
|
# than configuration. Running servers are re-sourced so an open session changes
|
|
# now instead of at next launch -- tmux applies a source-file to every attached
|
|
# client immediately.
|
|
tmux_dir="$config_home/tmux"
|
|
|
|
render_tmux() {
|
|
printf '# Generated by Panama from the "%s" theme. Do not edit.\n' "${theme_id:-custom}"
|
|
printf '#\n# tmux.conf sources this file; panama-theme-apps rewrites it on every theme\n'
|
|
printf '# change. The tokyonight-* files beside it are shipped for anyone who wants\n'
|
|
printf '# to source one directly, and are no longer copied here.\n\n'
|
|
|
|
printf 'set -g mode-style "fg=#%s,bg=#%s"\n\n' "$theme_accent" "$pal_gutter"
|
|
|
|
printf 'set -g message-style "fg=#%s,bg=#%s"\n' "$theme_accent" "$pal_gutter"
|
|
printf 'set -g message-command-style "fg=#%s,bg=#%s"\n\n' "$theme_accent" "$pal_gutter"
|
|
|
|
printf 'set -g pane-border-style "fg=#%s"\n' "$theme_accent"
|
|
printf 'set -g pane-active-border-style "fg=#%s"\n\n' "$theme_secondary"
|
|
|
|
printf 'set -g status-style "fg=#%s,bg=#%s"\n' "$theme_secondary" "$pal_gutter"
|
|
printf 'set -g status-bg "#%s"\n\n' "$pal_bg"
|
|
|
|
printf 'set -g status-left "#[fg=#%s,bg=#%s,bold] #S #[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]"\n' \
|
|
"$ansi_black" "$theme_secondary" "$ansi_black" "$theme_secondary"
|
|
printf 'set -g status-right "#[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]#[fg=#%s,bg=#%s] #{prefix_highlight} #[fg=#%s,bg=#%s]#[fg=#%s,bg=#%s] %%Y/%%m/%%d %%I:%%M %%p #[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]#[fg=#%s,bg=#%s,bold,italics] #h "\n\n' \
|
|
"$ansi_black" "$theme_accent" "$theme_secondary" "$pal_gutter" \
|
|
"$pal_gutter" "$pal_gutter" "$theme_secondary" "$pal_gutter" \
|
|
"$theme_accent" "$pal_gutter" "$ansi_black" "$theme_secondary"
|
|
|
|
printf 'setw -g window-status-format "#[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]#[fg=#%s,bg=#%s] #I #W #F #[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]"\n' \
|
|
"$ansi_black" "$pal_gutter" "$theme_accent" "$pal_gutter" "$ansi_black" "$pal_gutter"
|
|
printf 'setw -g window-status-current-format "#[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]#[fg=#%s,bg=#%s,bold] #I #W #F #[fg=#%s,bg=#%s,nobold,nounderscore,noitalics]"\n\n' \
|
|
"$ansi_black" "$pal_gutter" "$theme_secondary" "$pal_gutter" "$pal_gutter" "$pal_gutter"
|
|
|
|
printf 'setw -g window-status-separator ""\n'
|
|
}
|
|
|
|
status_tmux="skipped"
|
|
if [[ -d "$tmux_dir" ]] && [[ "$palette_ok" == true ]] && [[ "$ansi_ok" == true ]]; then
|
|
if render_tmux >"$tmux_dir/current-theme.conf.tmp" 2>/dev/null; then
|
|
status_tmux="$(replace_if_changed "$tmux_dir/current-theme.conf" "$tmux_dir/current-theme.conf.tmp")"
|
|
# Only if a server is actually running; `tmux source-file` would
|
|
# otherwise start one just to theme it.
|
|
if [[ "$status_tmux" != "failed" ]] \
|
|
&& command -v tmux >/dev/null 2>&1 && tmux has-session 2>/dev/null; then
|
|
tmux source-file "$tmux_dir/current-theme.conf" 2>/dev/null \
|
|
&& status_tmux="applied to running sessions"
|
|
fi
|
|
else
|
|
rm -f "$tmux_dir/current-theme.conf.tmp"
|
|
status_tmux="failed"
|
|
fi
|
|
fi
|
|
|
|
# ── btop ─────────────────────────────────────────────────────────────────────
|
|
# The theme is generated into btop's own themes directory and btop.conf is
|
|
# pointed at it by name. Only the color_theme line is rewritten, in place: btop
|
|
# OWNS btop.conf -- it rewrites the whole file on exit -- so the config is not
|
|
# symlinked into Panama and not replaced wholesale here.
|
|
#
|
|
# btop reads its theme once at startup, so a running instance keeps the old
|
|
# colors until it is restarted. That is acceptable for a monitor you open when
|
|
# you want it, and forcing a restart would kill a process the user is watching.
|
|
btop_conf="$config_home/btop/btop.conf"
|
|
btop_theme_dir="$config_home/btop/themes"
|
|
|
|
render_btop() {
|
|
printf '# Generated by Panama from the "%s" theme. Do not edit.\n' "${theme_id:-custom}"
|
|
printf '#\n# The *_start/_mid/_end triples are gradients btop draws meters with: low,\n'
|
|
printf '# middle, and high. They run cool -> warm -> hot so a saturated resource is\n'
|
|
printf '# obvious at a glance without reading the number.\n\n'
|
|
|
|
printf 'theme[main_bg]="#%s"\n' "$pal_bg"
|
|
printf 'theme[main_fg]="#%s"\n' "$pal_fg"
|
|
printf 'theme[title]="#%s"\n' "$pal_fg"
|
|
printf 'theme[hi_fg]="#%s"\n' "$theme_accent"
|
|
printf 'theme[selected_bg]="#%s"\n' "$pal_gutter"
|
|
printf 'theme[selected_fg]="#%s"\n' "$theme_accent"
|
|
printf 'theme[inactive_fg]="#%s"\n' "$pal_fgMuted"
|
|
printf 'theme[proc_misc]="#%s"\n\n' "$pal_green"
|
|
|
|
# The four boxes share one hairline, a step brighter than the gutter so the
|
|
# frame reads as structure rather than as content.
|
|
local box; box="$(mix_hex "$pal_gutter" "$pal_fgDim" 30)"
|
|
printf 'theme[cpu_box]="#%s"\n' "$box"
|
|
printf 'theme[mem_box]="#%s"\n' "$box"
|
|
printf 'theme[net_box]="#%s"\n' "$box"
|
|
printf 'theme[proc_box]="#%s"\n' "$box"
|
|
printf 'theme[div_line]="#%s"\n\n' "$pal_gutter"
|
|
|
|
printf 'theme[temp_start]="#%s"\n' "$theme_accent"
|
|
printf 'theme[temp_mid]="#%s"\n' "$pal_yellow"
|
|
printf 'theme[temp_end]="#%s"\n\n' "$pal_red"
|
|
|
|
printf 'theme[cpu_start]="#%s"\n' "$theme_accent"
|
|
printf 'theme[cpu_mid]="#%s"\n' "$pal_magenta"
|
|
printf 'theme[cpu_end]="#%s"\n\n' "$pal_red"
|
|
|
|
printf 'theme[free_start]="#%s"\n' "$pal_gutter"
|
|
printf 'theme[free_mid]="#%s"\n' "$pal_accentAlt"
|
|
printf 'theme[free_end]="#%s"\n\n' "$pal_cyan"
|
|
|
|
printf 'theme[cached_start]="#%s"\n' "$pal_cyan"
|
|
printf 'theme[cached_mid]="#%s"\n' "$theme_accent"
|
|
printf 'theme[cached_end]="#%s"\n\n' "$pal_magenta"
|
|
|
|
printf 'theme[available_start]="#%s"\n' "$pal_yellow"
|
|
printf 'theme[available_mid]="#%s"\n' "$pal_orange"
|
|
printf 'theme[available_end]="#%s"\n\n' "$pal_red"
|
|
|
|
printf 'theme[used_start]="#%s"\n' "$pal_green"
|
|
printf 'theme[used_mid]="#%s"\n' "$pal_yellow"
|
|
printf 'theme[used_end]="#%s"\n\n' "$pal_red"
|
|
|
|
printf 'theme[download_start]="#%s"\n' "$pal_gutter"
|
|
printf 'theme[download_mid]="#%s"\n' "$theme_accent"
|
|
printf 'theme[download_end]="#%s"\n\n' "$pal_cyan"
|
|
|
|
printf 'theme[upload_start]="#%s"\n' "$pal_gutter"
|
|
printf 'theme[upload_mid]="#%s"\n' "$pal_magenta"
|
|
printf 'theme[upload_end]="#%s"\n' "$pal_pink"
|
|
}
|
|
|
|
status_btop="skipped"
|
|
if [[ "$palette_ok" == true ]] && mkdir -p "$btop_theme_dir" 2>/dev/null; then
|
|
if render_btop >"$btop_theme_dir/panama.theme.tmp" 2>/dev/null; then
|
|
status_btop="$(replace_if_changed "$btop_theme_dir/panama.theme" "$btop_theme_dir/panama.theme.tmp")"
|
|
else
|
|
rm -f "$btop_theme_dir/panama.theme.tmp"
|
|
status_btop="failed"
|
|
fi
|
|
|
|
if [[ "$status_btop" != "failed" && -w "$btop_conf" ]]; then
|
|
sed -i 's|^color_theme *=.*|color_theme = "panama"|' "$btop_conf" 2>/dev/null \
|
|
|| status_btop="failed"
|
|
fi
|
|
fi
|
|
|
|
# ── GTK settings.ini ─────────────────────────────────────────────────────────
|
|
# adw-gtk3, not Adwaita: no Adwaita GTK theme is installed on Fedora 44, and
|
|
# naming a theme that does not exist makes GTK fall back to its light default --
|
|
# which made dark mode silently produce light windows.
|
|
#
|
|
# GTK3 is the reason this file is generated at all. Under GNOME,
|
|
# gnome-settings-daemon publishes the theme over XSETTINGS; under Hyprland
|
|
# nothing does, so ~/.config/gtk-3.0/settings.ini is authoritative for GTK3
|
|
# applications.
|
|
if [[ "$scheme" == "light" ]]; then
|
|
gtk_theme="adw-gtk3"
|
|
prefer_dark=0
|
|
else
|
|
gtk_theme="adw-gtk3-dark"
|
|
prefer_dark=1
|
|
fi
|
|
|
|
status_gtk="skipped"
|
|
for gtk_version in 3.0 4.0; do
|
|
gtk_dir="$config_home/gtk-$gtk_version"
|
|
template="$gtk_dir/settings.ini.template"
|
|
[[ -r "$template" ]] || continue
|
|
|
|
# Written atomically: a GTK application starting mid-write would otherwise
|
|
# read a truncated file and fall back to defaults.
|
|
if sed -e "s/@GTK_THEME@/$gtk_theme/" -e "s/@PREFER_DARK@/$prefer_dark/" "$template" \
|
|
>"$gtk_dir/settings.ini.tmp" 2>/dev/null \
|
|
&& mv "$gtk_dir/settings.ini.tmp" "$gtk_dir/settings.ini" 2>/dev/null; then
|
|
status_gtk="written"
|
|
else
|
|
rm -f "$gtk_dir/settings.ini.tmp"
|
|
status_gtk="failed"
|
|
fi
|
|
done
|
|
|
|
# ── GTK gtk.css ──────────────────────────────────────────────────────────────
|
|
# settings.ini names a theme; these are the colours that theme is overridden
|
|
# with. libadwaita reads @define-color from the user stylesheet, which is how
|
|
# Nautilus, Papers and every other GTK application end up wearing the Panama
|
|
# palette rather than adw-gtk3's stock greys.
|
|
#
|
|
# Only the marked block is rewritten. Everything else in these files is the
|
|
# structural CSS of a vendored theme -- a thousand lines of rounding, padding
|
|
# and window controls that has nothing to do with colour, and that regenerating
|
|
# the file wholesale would destroy.
|
|
#
|
|
# All three files get the ACTIVE theme rather than one being pinned light and
|
|
# another dark. gtk-4.0/gtk.css was a byte-for-byte copy of the dark one, so a
|
|
# light desktop drew dark GTK4 windows; rendering the active theme into each is
|
|
# what actually fixes that, whichever of the two GTK decides to read.
|
|
gtk_css_block() {
|
|
local shade sidebar_line
|
|
shade="$(rgba_of "$pal_bgDark" 0.25)"
|
|
sidebar_line="$(rgba_of "$pal_gutter" 0.36)"
|
|
|
|
printf '/* Generated from the "%s" theme by panama-theme-apps. Edit the\n' "${theme_id:-custom}"
|
|
printf ' * theme, not this block -- the next theme change overwrites it. */\n'
|
|
|
|
printf '@define-color accent_bg_color #%s;\n' "$theme_accent"
|
|
printf '@define-color accent_color #%s;\n' "$pal_accentAlt"
|
|
printf '@define-color accent_fg_color #%s;\n' "$accent_fg"
|
|
printf '@define-color destructive_bg_color #%s;\n' "$pal_redDeep"
|
|
printf '@define-color destructive_color #%s;\n' "$pal_red"
|
|
printf '@define-color destructive_fg_color #%s;\n' "$(on_hex "$pal_redDeep" "$pal_bgDark" "$pal_fg")"
|
|
printf '@define-color success_bg_color #%s;\n' "$pal_green"
|
|
printf '@define-color success_color #%s;\n' "$pal_green"
|
|
printf '@define-color success_fg_color #%s;\n' "$(on_hex "$pal_green" "$pal_bgDark" "$pal_fg")"
|
|
printf '@define-color warning_bg_color #%s;\n' "$pal_yellow"
|
|
printf '@define-color warning_color #%s;\n' "$pal_yellow"
|
|
printf '@define-color warning_fg_color #%s;\n' "$(on_hex "$pal_yellow" "$pal_bgDark" "$pal_fg")"
|
|
printf '@define-color error_bg_color #%s;\n' "$pal_redDeep"
|
|
printf '@define-color error_color #%s;\n' "$pal_red"
|
|
printf '@define-color error_fg_color #%s;\n' "$(on_hex "$pal_redDeep" "$pal_bgDark" "$pal_fg")"
|
|
|
|
printf '@define-color window_bg_color #%s;\n' "$pal_bgPanel"
|
|
printf '@define-color window_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color view_bg_color #%s;\n' "$pal_bg"
|
|
printf '@define-color view_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color headerbar_bg_color #%s;\n' "$pal_bgPanel"
|
|
printf '@define-color headerbar_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color headerbar_border_color #%s;\n' "$pal_gutter"
|
|
printf '@define-color headerbar_backdrop_color #%s;\n' "$pal_bgDark"
|
|
printf '@define-color headerbar_shade_color %s;\n' "$(rgba_of "$pal_bgDark" 0.08)"
|
|
printf '@define-color headerbar_darker_shade_color %s;\n' "$(rgba_of "$pal_bgDark" 0.9)"
|
|
|
|
printf '@define-color sidebar_bg_color #%s;\n' "$pal_bgDark"
|
|
printf '@define-color sidebar_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color sidebar_backdrop_color #%s;\n' "$pal_bgDark"
|
|
printf '@define-color sidebar_shade_color %s;\n' "$(rgba_of "$pal_gutter" 0.08)"
|
|
printf '@define-color sidebar_border_color %s;\n' "$sidebar_line"
|
|
printf '@define-color secondary_sidebar_bg_color #%s;\n' "$pal_bgDark"
|
|
printf '@define-color secondary_sidebar_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color secondary_sidebar_backdrop_color #%s;\n' "$pal_bgDark"
|
|
printf '@define-color secondary_sidebar_shade_color %s;\n' "$shade"
|
|
printf '@define-color secondary_sidebar_border_color %s;\n' "$sidebar_line"
|
|
|
|
printf '@define-color card_bg_color #%s;\n' "$pal_bgHighlight"
|
|
printf '@define-color card_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color card_shade_color %s;\n' "$shade"
|
|
printf '@define-color dialog_bg_color #%s;\n' "$pal_bgPopover"
|
|
printf '@define-color dialog_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color popover_bg_color #%s;\n' "$pal_bgPopover"
|
|
printf '@define-color popover_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color popover_shade_color %s;\n' "$shade"
|
|
printf '@define-color thumbnail_bg_color #%s;\n' "$pal_bgHighlight"
|
|
printf '@define-color thumbnail_fg_color #%s;\n' "$pal_fg"
|
|
printf '@define-color shade_color %s;\n' "$shade"
|
|
printf '@define-color scrollbar_outline_color %s;\n' "$(rgba_of "$pal_bgDark" 0.5)"
|
|
}
|
|
|
|
# Substitutes the block between the markers, leaving every other byte alone.
|
|
# The block is handed over in a file rather than through `awk -v`, which
|
|
# processes backslash escapes in the value it is given.
|
|
write_gtk_block() {
|
|
local target="$1" block_file="$2" temporary="$1.tmp"
|
|
if ! grep -q 'PANAMA THEME BEGIN' "$target" 2>/dev/null \
|
|
|| ! grep -q 'PANAMA THEME END' "$target" 2>/dev/null; then
|
|
printf 'no marker'
|
|
return 0
|
|
fi
|
|
|
|
awk -v block="$block_file" '
|
|
index($0, "PANAMA THEME BEGIN") {
|
|
print
|
|
while ((getline line < block) > 0) print line
|
|
close(block)
|
|
skipping = 1
|
|
next
|
|
}
|
|
index($0, "PANAMA THEME END") { skipping = 0; print; next }
|
|
!skipping { print }
|
|
' "$target" >"$temporary" 2>/dev/null || true
|
|
|
|
if [[ ! -s "$temporary" ]]; then
|
|
rm -f "$temporary"
|
|
printf 'failed'
|
|
return 0
|
|
fi
|
|
replace_if_changed "$target" "$temporary"
|
|
}
|
|
|
|
status_gtkcss="skipped"
|
|
if [[ "$palette_ok" == true ]]; then
|
|
gtk_block_file="$(mktemp "${TMPDIR:-/tmp}/panama-gtk-block.XXXXXX")"
|
|
if gtk_css_block >"$gtk_block_file" 2>/dev/null; then
|
|
for gtk_css in "$config_home/gtk-3.0/gtk.css" \
|
|
"$config_home/gtk-4.0/gtk.css" \
|
|
"$config_home/gtk-4.0/gtk-dark.css"; do
|
|
[[ -w "$gtk_css" ]] || continue
|
|
gtk_css_result="$(write_gtk_block "$gtk_css" "$gtk_block_file")"
|
|
# The worst outcome across the three files is the one worth
|
|
# reporting: two written and one missing its markers is not
|
|
# "written".
|
|
case "$gtk_css_result" in
|
|
failed) status_gtkcss="failed" ;;
|
|
"no marker") [[ "$status_gtkcss" == "failed" ]] || status_gtkcss="no marker" ;;
|
|
unchanged) [[ "$status_gtkcss" == "skipped" ]] && status_gtkcss="unchanged" || true ;;
|
|
written) [[ "$status_gtkcss" == "skipped" || "$status_gtkcss" == "unchanged" ]] \
|
|
&& status_gtkcss="written" || true ;;
|
|
esac
|
|
done
|
|
else
|
|
status_gtkcss="failed"
|
|
fi
|
|
rm -f "$gtk_block_file"
|
|
fi
|
|
|
|
# ── libadwaita accent ────────────────────────────────────────────────────────
|
|
# Files, Papers, Loupe and every other libadwaita application read their accent
|
|
# from the Settings portal, not from anything Panama draws, so without this they
|
|
# render in GNOME blue no matter which accent the desktop is set to.
|
|
#
|
|
# GNOME's accent-color is a FIXED enum of nine names -- there is no hex here to
|
|
# match, only a nearest member to pick. Which one each Panama accent maps to is
|
|
# recorded in config/palette.json alongside its colours, and this is the one
|
|
# place the command line's accent NAME is still the input rather than the
|
|
# theme's own hex: an enum cannot take a colour.
|
|
#
|
|
# The portal must route Settings to the gnome backend for this to reach
|
|
# anything; xdg-desktop-portal-gtk cannot serve accent-color at all. See
|
|
# config/dot/xdg-desktop-portal/hyprland-portals.conf.
|
|
|
|
status_adwaita="skipped"
|
|
if command -v gsettings >/dev/null 2>&1; then
|
|
adwaita_accent="$(accent_gnome "$accent")"
|
|
# Writing the same value emits no change signal, so applications already
|
|
# showing this accent are not asked to restyle for nothing.
|
|
if [[ "$(gsettings get org.gnome.desktop.interface accent-color 2>/dev/null)" == "'$adwaita_accent'" ]]; then
|
|
status_adwaita="unchanged"
|
|
elif gsettings set org.gnome.desktop.interface accent-color "$adwaita_accent" 2>/dev/null; then
|
|
status_adwaita="$adwaita_accent"
|
|
else
|
|
status_adwaita="failed"
|
|
fi
|
|
fi
|
|
|
|
# ── Vicinae ──────────────────────────────────────────────────────────────────
|
|
# The launcher selects a theme per system appearance, so both schemes are
|
|
# rendered every time and vicinae.json names them permanently. They are written
|
|
# straight into Vicinae's data directory rather than into the repository's
|
|
# themes folder: that folder is symlinked into ~/.local/share/vicinae/themes,
|
|
# and a generated file inside it would be a machine's colours showing up as a
|
|
# change to the repository.
|
|
vicinae_dir="$data_home/vicinae/themes"
|
|
|
|
render_vicinae() {
|
|
(
|
|
set -euo pipefail
|
|
eval "$(theme_vars "$(resolve_theme "$1")")"
|
|
local variant="$1"
|
|
local core_accent core_secondary selection secondary_selection tile on_accent
|
|
core_accent="$(hex_or "${theme_accent:-}" "$(accent_hex "$2" "$1")")"
|
|
core_secondary="$(hex_or "${theme_secondary:-}" "$(accent_secondary_hex "$2" "$1")")"
|
|
on_accent="$(on_hex "$core_accent" "$pal_bgDark" "$pal_fg")"
|
|
# The two halves of the Prism selection, mixed into the highlight
|
|
# surface rather than named as tokens -- no palette key describes
|
|
# "a row that is selected".
|
|
selection="$(mix_hex "$pal_bgHighlight" "$core_accent" 30)"
|
|
secondary_selection="$(mix_hex "$pal_bgHighlight" "$core_secondary" 30)"
|
|
tile="$(mix_hex "$pal_bg" "$pal_bgHighlight" 50)"
|
|
|
|
printf '# Generated by Panama from the "%s" theme. Do not edit.\n' "${theme_id:-custom}"
|
|
printf '# panama-theme-apps rewrites this file on every theme change.\n\n'
|
|
|
|
printf '[meta]\nversion = 1\n'
|
|
printf 'name = "Panama %s"\n' "$variant"
|
|
printf 'description = "The active Panama theme, rendered for Vicinae."\n'
|
|
printf 'variant = "%s"\n\n' "$variant"
|
|
|
|
printf '[colors.core]\n'
|
|
printf 'background = "#%s"\n' "$pal_bg"
|
|
printf 'foreground = "#%s"\n' "$pal_fg"
|
|
printf 'secondary_background = "#%s"\n' "$pal_bgDark"
|
|
printf 'border = "#%s"\n' "$pal_gutter"
|
|
printf 'accent = "#%s"\n' "$core_accent"
|
|
printf 'accent_foreground = "#%s"\n\n' "$on_accent"
|
|
|
|
printf '[colors.accents]\n'
|
|
# The terminal blue, not accentAlt: on a light theme accentAlt is often
|
|
# the same cyan the next line already names.
|
|
printf 'blue = "#%s"\n' "$(hex_or "${ansi_blue:-}" "$pal_accentAlt")"
|
|
printf 'green = "#%s"\n' "$pal_green"
|
|
printf 'magenta = "#%s"\n' "$pal_magenta"
|
|
printf 'orange = "#%s"\n' "$pal_orange"
|
|
printf 'purple = "#%s"\n' "$pal_pink"
|
|
printf 'red = "#%s"\n' "$pal_red"
|
|
printf 'yellow = "#%s"\n' "$pal_yellow"
|
|
printf 'cyan = "#%s"\n\n' "$pal_cyan"
|
|
|
|
printf '[colors.main_window]\n'
|
|
printf 'border = "#%s"\n' "$pal_gutter"
|
|
printf 'footer = { background = "colors.core.secondary_background" }\n\n'
|
|
|
|
printf '[colors.settings_window]\n'
|
|
printf 'border = "#%s"\n\n' "$pal_gutter"
|
|
|
|
printf '[colors.shortcut]\n'
|
|
printf 'border = "colors.core.border"\n\n'
|
|
|
|
printf '[colors.text]\n'
|
|
printf 'default = "colors.core.foreground"\n'
|
|
printf 'muted = "#%s"\n' "$pal_fgDim"
|
|
printf 'danger = "#%s"\n' "$pal_red"
|
|
printf 'success = "#%s"\n' "$pal_green"
|
|
printf 'placeholder = "#%s"\n' "$pal_fgMuted"
|
|
printf 'selection = { background = "#%s", foreground = "#%s" }\n\n' "$core_accent" "$on_accent"
|
|
|
|
printf '[colors.text.links]\n'
|
|
printf 'default = "#%s"\n' "$core_accent"
|
|
printf 'visited = "#%s"\n\n' "$pal_magenta"
|
|
|
|
printf '[colors.input]\n'
|
|
printf 'border = "#%s"\n' "$pal_gutter"
|
|
printf 'border_focus = "#%s"\n' "$core_accent"
|
|
printf 'border_error = "#%s"\n\n' "$pal_red"
|
|
|
|
printf '[colors.button.primary]\n'
|
|
printf 'background = "#%s"\n' "$pal_bgHighlight"
|
|
printf 'foreground = "#%s"\n' "$pal_fg"
|
|
printf 'hover = { background = "#%s" }\n' "$pal_gutter"
|
|
printf 'focus = { outline = "colors.core.accent" }\n\n'
|
|
|
|
printf '[colors.list.item.hover]\n'
|
|
printf 'foreground = "#%s"\n' "$pal_fg"
|
|
printf 'secondary_foreground = "#%s"\n\n' "$pal_fgDim"
|
|
|
|
printf '[colors.list.item.selection]\n'
|
|
printf 'background = "#%s"\n' "$selection"
|
|
printf 'foreground = "#%s"\n' "$pal_fg"
|
|
printf 'secondary_background = "#%s"\n' "$secondary_selection"
|
|
printf 'secondary_foreground = "#%s"\n\n' "$pal_fg"
|
|
|
|
printf '[colors.grid.item]\n'
|
|
printf 'background = "#%s"\n' "$tile"
|
|
printf 'hover = { outline = "#%s" }\n' "$core_accent"
|
|
printf 'selection = { outline = "#%s" }\n\n' "$core_secondary"
|
|
|
|
printf '[colors.scrollbars]\n'
|
|
printf 'background = "#%s"\n\n' "$pal_gutter"
|
|
|
|
printf '[colors.loading]\n'
|
|
printf 'bar = "#%s"\n' "$core_accent"
|
|
printf 'spinner = "#%s"\n' "$pal_fg"
|
|
)
|
|
}
|
|
|
|
status_vicinae="skipped"
|
|
if [[ "$both_schemes_ok" == true ]] && mkdir -p "$vicinae_dir" 2>/dev/null; then
|
|
status_vicinae="written"
|
|
for vicinae_scheme in dark light; do
|
|
target="$vicinae_dir/panama-$vicinae_scheme.toml"
|
|
if render_vicinae "$vicinae_scheme" "$accent" >"$target.tmp" 2>/dev/null; then
|
|
[[ "$(replace_if_changed "$target" "$target.tmp")" == "failed" ]] \
|
|
&& status_vicinae="failed" || true
|
|
else
|
|
rm -f "$target.tmp"
|
|
status_vicinae="failed"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ── Firefox ──────────────────────────────────────────────────────────────────
|
|
# Edge-Frfox, the vendored chrome theme, takes its colours from GTK system
|
|
# colours on Linux -- AccentColor, -moz-dialog, Field. Those follow the portal,
|
|
# which knows light from dark and nothing about which theme is on. So Firefox
|
|
# was the one surface that stayed grey-blue whatever Panama was wearing.
|
|
#
|
|
# This file is generated beside custom.css and imported by it, rather than being
|
|
# custom.css itself: custom.css is the seam a person edits, and regenerating it
|
|
# on every theme change would eat their overrides. It carries BOTH schemes as
|
|
# light-dark() pairs, so Firefox flips with the portal without this script
|
|
# running again -- and so a private window, which forces its own dark scheme,
|
|
# is themed too.
|
|
#
|
|
# Chrome only. about: pages are styled from userContent.css, which is an
|
|
# upstream file and does not import this one.
|
|
firefox_chrome="${PANAMA_FIREFOX_CHROME:-$(cd "$script_dir/../../../.." 2>/dev/null && pwd)/config/firefox/chrome}"
|
|
|
|
render_firefox() {
|
|
# light-dark() pairs, built from the two resolved palettes. Firefox picks
|
|
# the half that matches the chrome's own color-scheme.
|
|
pair() {
|
|
local light="light_pal_$1" dark="dark_pal_$1"
|
|
printf 'light-dark(#%s, #%s)' "${!light}" "${!dark}"
|
|
}
|
|
local accents on_light on_dark pressed
|
|
accents="light-dark(#$light_theme_accent, #$dark_theme_accent)"
|
|
on_light="$(on_hex "$light_theme_accent" "$light_pal_bgDark" "$light_pal_fg")"
|
|
on_dark="$(on_hex "$dark_theme_accent" "$dark_pal_bgDark" "$dark_pal_fg")"
|
|
# A pressed button has to read as a step past hover, and no palette token
|
|
# sits between the gutter and the text -- so it is mixed.
|
|
pressed="light-dark(#$(mix_hex "$light_pal_gutter" "$light_pal_fg" 15), #$(mix_hex "$dark_pal_gutter" "$dark_pal_fg" 15))"
|
|
|
|
printf '/* Generated by panama-theme-apps. Do not edit -- custom.css imports this,\n'
|
|
printf ' * and the next theme change overwrites it. Local Firefox tweaks belong in\n'
|
|
printf ' * custom.css, which is never regenerated.\n'
|
|
printf ' *\n'
|
|
printf ' * Both schemes are here as light-dark() pairs so the browser follows the\n'
|
|
printf ' * portal on its own. :root:root out-specifies Edge-Frfox colors.css, which\n'
|
|
printf ' * uses :root:not([lwtheme]) -- a plain :root would lose the tie. */\n\n'
|
|
|
|
printf ':root:root:not([lwtheme]),\n'
|
|
printf ':root:root[privatebrowsingmode="temporary"] {\n'
|
|
|
|
printf ' /* Toolbars */\n'
|
|
printf ' --lwt-accent-color: %s !important;\n' "$(pair bg)"
|
|
printf ' --lwt-accent-color-inactive: %s !important;\n' "$(pair bgDark)"
|
|
printf ' --lwt-text-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --lwt-text-color-inactive: %s !important;\n' "$(pair fgDim)"
|
|
printf ' --toolbar-bgcolor: %s !important;\n' "$(pair bgPanel)"
|
|
printf ' --toolbar-non-lwt-bgcolor: var(--toolbar-bgcolor) !important;\n'
|
|
printf ' --toolbar-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --toolbarbutton-icon-fill: var(--toolbar-color) !important;\n'
|
|
printf ' --toolbarbutton-icon-fill-attention: %s !important;\n' "$accents"
|
|
printf ' --chrome-content-separator-color: %s !important;\n' "$(pair gutter)"
|
|
printf ' --newtab-background-color: %s !important;\n' "$(pair bg)"
|
|
|
|
printf ' /* Tabs */\n'
|
|
printf ' --tab-selected-bgcolor: var(--toolbar-bgcolor) !important;\n'
|
|
printf ' --tab-selected-textcolor: %s !important;\n' "$(pair fg)"
|
|
printf ' --tab-selected-textcolor-inactive: %s !important;\n' "$(pair fgDim)"
|
|
printf ' --tab-attention-icon-color: %s !important;\n' "$accents"
|
|
|
|
printf ' /* URL bar */\n'
|
|
printf ' --toolbar-field-background-color: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --toolbar-field-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --toolbar-field-border-color: transparent !important;\n'
|
|
printf ' --toolbar-field-focus-background-color: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --toolbar-field-focus-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --toolbar-field-focus-border-color: %s !important;\n' "$accents"
|
|
printf ' --urlbar-box-bgcolor: %s !important;\n' "$(pair bgPopover)"
|
|
printf ' --urlbar-box-hover-bgcolor: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --urlbar-box-active-bgcolor: %s !important;\n' "$(pair gutter)"
|
|
printf ' --urlbar-box-focus-bgcolor: var(--urlbar-box-hover-bgcolor) !important;\n'
|
|
printf ' --link-color: %s !important;\n' "$accents"
|
|
printf ' --uc-urlbarView-accent-color: %s !important;\n' "$accents"
|
|
printf ' --urlbarView-highlight-background: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --urlbarView-highlight-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --urlbarView-hover-background: %s !important;\n' "$(pair gutter)"
|
|
printf ' --urlbarView-separator-color: %s !important;\n' "$(pair gutter)"
|
|
|
|
printf ' /* Menus and panels */\n'
|
|
printf ' --arrowpanel-background: %s !important;\n' "$(pair bgPopover)"
|
|
printf ' --arrowpanel-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --arrowpanel-border-color: %s !important;\n' "$(pair gutter)"
|
|
printf ' --panel-separator-color: %s !important;\n' "$(pair gutter)"
|
|
printf ' --panel-description-color: %s !important;\n' "$(pair fgDim)"
|
|
printf ' --panel-disabled-color: %s !important;\n' "$(pair fgMuted)"
|
|
printf ' --panel-item-hover-bgcolor: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --panel-item-active-bgcolor: %s !important;\n' "$(pair gutter)"
|
|
|
|
printf ' /* Buttons */\n'
|
|
printf ' --button-primary-bgcolor: %s !important;\n' "$accents"
|
|
printf ' --button-primary-color: light-dark(#%s, #%s) !important;\n' "$on_light" "$on_dark"
|
|
printf ' --button-bgcolor: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --button-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --button-hover-bgcolor: %s !important;\n' "$(pair gutter)"
|
|
printf ' --button-active-bgcolor: %s !important;\n' "$pressed"
|
|
|
|
printf ' /* Inputs */\n'
|
|
printf ' --focus-outline-color: %s !important;\n' "$accents"
|
|
printf ' --input-bgcolor: %s !important;\n' "$(pair bgPopover)"
|
|
printf ' --input-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --input-border-color: %s !important;\n' "$(pair gutter)"
|
|
printf ' --error-text-color: %s !important;\n' "$(pair red)"
|
|
printf ' --input-error-border-color: %s !important;\n' "$(pair red)"
|
|
|
|
printf ' /* Sidebar */\n'
|
|
printf ' --sidebar-background-color: %s !important;\n' "$(pair bgPanel)"
|
|
printf ' --sidebar-text-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --sidebar-border-color: %s !important;\n' "$(pair gutter)"
|
|
|
|
# Chrome-level dialogs -- the bookmark editor, the print sheet. Edge-Frfox
|
|
# points these at the panel colours; the in-content names are what the
|
|
# dialog itself reads.
|
|
printf '\n &[dialogroot] {\n'
|
|
printf ' --in-content-page-background: %s !important;\n' "$(pair bgPopover)"
|
|
printf ' --in-content-page-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --in-content-accent-color: %s !important;\n' "$accents"
|
|
printf ' --in-content-primary-button-background: %s !important;\n' "$accents"
|
|
printf ' --in-content-primary-button-text-color: light-dark(#%s, #%s) !important;\n' \
|
|
"$on_light" "$on_dark"
|
|
printf ' --in-content-button-background: %s !important;\n' "$(pair bgHighlight)"
|
|
printf ' --in-content-button-text-color: %s !important;\n' "$(pair fg)"
|
|
printf ' --in-content-box-background: %s !important;\n' "$(pair bg)"
|
|
printf ' --in-content-box-border-color: %s !important;\n' "$(pair gutter)"
|
|
printf ' }\n'
|
|
|
|
printf '}\n'
|
|
}
|
|
|
|
status_firefox="skipped"
|
|
if [[ "$both_schemes_ok" == true && -d "$firefox_chrome" && -w "$firefox_chrome" ]]; then
|
|
firefox_target="$firefox_chrome/panama-theme.css"
|
|
if render_firefox >"$firefox_target.tmp" 2>/dev/null; then
|
|
status_firefox="$(replace_if_changed "$firefox_target" "$firefox_target.tmp")"
|
|
else
|
|
rm -f "$firefox_target.tmp"
|
|
status_firefox="failed"
|
|
fi
|
|
fi
|
|
|
|
# ── kitty ────────────────────────────────────────────────────────────────────
|
|
# kitty.conf ends with `include current-theme.conf`; that file is machine state
|
|
# and is rendered here rather than copied from a per-scheme file, so any theme
|
|
# reaches the terminal rather than only the two Tokyo Nights.
|
|
kitty_dir="$config_home/kitty"
|
|
kitty_theme="$kitty_dir/current-theme.conf"
|
|
|
|
render_kitty() {
|
|
printf '# Generated by Panama from the "%s" theme. Do not edit.\n' "${theme_id:-custom}"
|
|
printf '#\n# kitty.conf includes this file; panama-theme-apps rewrites it on every theme\n'
|
|
printf '# change and pushes the same colours to running terminals over the kitty\n'
|
|
printf '# control socket. The tokyonight-* files beside it are shipped for anyone who\n'
|
|
printf '# wants to include one directly, and are no longer copied here.\n\n'
|
|
|
|
printf 'foreground #%s\n' "$pal_fg"
|
|
printf 'background #%s\n' "$pal_bg"
|
|
# The classic terminal inversion: selected text wears the foreground as its
|
|
# ground. Derived rather than named -- no palette token is "selection".
|
|
printf 'selection_foreground #%s\n' "$pal_bg"
|
|
printf 'selection_background #%s\n' "$pal_fg"
|
|
printf 'cursor #%s\n' "$pal_fg"
|
|
printf 'cursor_text_color #%s\n' "$pal_bg"
|
|
printf 'url_color #%s\n\n' "$pal_teal"
|
|
|
|
# The focused border is the accent role; the unfocused one is the gutter,
|
|
# the palette's neutral contrast surface.
|
|
printf 'active_border_color #%s\n' "$theme_accent"
|
|
printf 'inactive_border_color #%s\n\n' "$pal_gutter"
|
|
|
|
printf 'tab_bar_background #%s\n' "$pal_bg"
|
|
printf 'active_tab_foreground #%s\n' "$pal_bgDark"
|
|
printf 'active_tab_background #%s\n' "$theme_accent"
|
|
printf 'inactive_tab_foreground #%s\n' "$pal_fgDim"
|
|
printf 'inactive_tab_background #%s\n\n' "$pal_bgHighlight"
|
|
|
|
printf 'color0 #%s\n' "$ansi_black"
|
|
printf 'color8 #%s\n' "$ansi_brightBlack"
|
|
printf 'color1 #%s\n' "$ansi_red"
|
|
printf 'color9 #%s\n' "$ansi_brightRed"
|
|
printf 'color2 #%s\n' "$ansi_green"
|
|
printf 'color10 #%s\n' "$ansi_brightGreen"
|
|
printf 'color3 #%s\n' "$ansi_yellow"
|
|
printf 'color11 #%s\n' "$ansi_brightYellow"
|
|
printf 'color4 #%s\n' "$ansi_blue"
|
|
printf 'color12 #%s\n' "$ansi_brightBlue"
|
|
printf 'color5 #%s\n' "$ansi_magenta"
|
|
printf 'color13 #%s\n' "$ansi_brightMagenta"
|
|
printf 'color6 #%s\n' "$ansi_cyan"
|
|
printf 'color14 #%s\n' "$ansi_brightCyan"
|
|
printf 'color7 #%s\n' "$ansi_white"
|
|
printf 'color15 #%s\n' "$ansi_brightWhite"
|
|
# 16 and 17 are the extended slots tokyonight uses for orange and a deep
|
|
# red; some tools (delta, bat) reference them.
|
|
printf 'color16 #%s\n' "$pal_orange"
|
|
printf 'color17 #%s\n' "$pal_redDeep"
|
|
}
|
|
|
|
status_kitty="skipped"
|
|
if [[ -d "$kitty_dir" ]] && [[ "$palette_ok" == true ]] && [[ "$ansi_ok" == true ]]; then
|
|
# Written atomically: kitty may read this while a new window is starting.
|
|
if render_kitty >"$kitty_theme.tmp" 2>/dev/null; then
|
|
status_kitty="$(replace_if_changed "$kitty_theme" "$kitty_theme.tmp")"
|
|
else
|
|
rm -f "$kitty_theme.tmp"
|
|
status_kitty="failed"
|
|
fi
|
|
|
|
# Live-apply to running terminals. kitty appends its PID to the socket name
|
|
# from listen_on, so there is one socket per instance -- "unix:@mykitty"
|
|
# alone reaches nothing, which is exactly how this looked like remote
|
|
# control being disabled when it was not.
|
|
if [[ "$status_kitty" != "failed" ]] \
|
|
&& command -v kitty >/dev/null 2>&1 && command -v ss >/dev/null 2>&1; then
|
|
applied=0
|
|
while read -r socket; do
|
|
[[ -n "$socket" ]] || continue
|
|
if kitty @ --to "unix:@${socket#@}" set-colors --all --configured "$kitty_theme" >/dev/null 2>&1 \
|
|
&& kitty @ --to "unix:@${socket#@}" set-colors --override "active_border_color=#$theme_accent" >/dev/null 2>&1; then
|
|
applied=$((applied + 1))
|
|
fi
|
|
done < <(ss -xl 2>/dev/null | grep -oE '@mykitty[^[:space:]]*' | sort -u)
|
|
(( applied > 0 )) && status_kitty="applied to $applied"
|
|
fi
|
|
fi
|
|
|
|
# Anything else on this machine that wants to know. Runs after every generator
|
|
# above, so a hook sees the finished state rather than a half-applied one, and
|
|
# a broken hook cannot cost you a theme change -- panama-hook reports and steps
|
|
# over. See bin/panama-hook.
|
|
#
|
|
# It used to sit INSIDE the kitty branch's if-condition, which meant it fired
|
|
# in the middle of writing kitty's theme file and not at all on a machine
|
|
# without a kitty theme to write.
|
|
"${PANAMA_PATH:-$HOME/.local/share/Panama}/bin/panama-hook" theme-set "$scheme" "$accent" || true
|
|
|
|
# Every target reports what actually happened. A helper that says only
|
|
# "kitty: applied" while silently skipping three other applications is how a
|
|
# half-applied theme goes unnoticed.
|
|
jq -cn \
|
|
--arg scheme "$scheme" \
|
|
--arg theme "${theme_id:-}" \
|
|
--arg kitty "$status_kitty" \
|
|
--arg gtk "$status_gtk" \
|
|
--arg gtkcss "$status_gtkcss" \
|
|
--arg btop "$status_btop" \
|
|
--arg tmux "$status_tmux" \
|
|
--arg hyprlock "$status_hyprlock" \
|
|
--arg adwaita "$status_adwaita" \
|
|
--arg vicinae "$status_vicinae" \
|
|
--arg firefox "$status_firefox" \
|
|
'{scheme: $scheme, theme: $theme, kitty: $kitty, gtk: $gtk, gtkcss: $gtkcss,
|
|
btop: $btop, tmux: $tmux, hyprlock: $hyprlock, adwaita: $adwaita,
|
|
vicinae: $vicinae, firefox: $firefox}'
|