Appearance now opens on Themes: light and dark side by side, each remembering its own choice, over galleries of ten shipped themes — Tokyo Moon and Day joined by Moon Rose, Catppuccin, Nord, Gruvbox and Everforest in both modes. A theme is a complete palette: the catalog lives in themes.json, Theme.qml reads every color token from the active record, and one render pipeline carries it to kitty, tmux, btop, GTK, Vicinae, Firefox's chrome, and the lock screen. The Theme editor builds new ones from four wells — wheel, hex, or eyedropper — with derived surfaces, a saturation slider, debounced fine-tune, and effects that save with the theme. Custom edits finally keep GNOME's accent, kitty's border, and hyprlock in sync. Wallpapers can be video: mpvpaper per output, hardware-decoded, muted and looped, supervised and respawned. Panama owns the pausing — games, battery, and a bar pill for right now — because the compositor rebuilds full-screen blur for every frame a video wallpaper draws. The lock screen gets a still frame. Titlebars stop lying. GNOME apps get close-only on your chosen side, the maximize and double-click settings are gone, the Settings window obeys the same rules, and its titlebar can be turned off entirely. Typography becomes five labeled dropdowns instead of a wall of samples. Contracts updated and written throughout (165 now); per the redesign workflow none were executed — the full sweep runs once at the end. Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
502 lines
21 KiB
Bash
Executable File
502 lines
21 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Strict: a failed mv or ln here used to fall through and exit 0, so a machine
|
|
# could end up half-linked while the installer's summary reported the stage as
|
|
# fine. Anything genuinely optional below carries its own `|| true`.
|
|
set -euo pipefail
|
|
|
|
# --- Helper functions ---
|
|
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
|
|
|
# Define paths as they have not been defined by new bashrc yet!
|
|
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
|
PANAMA_BASH="${PANAMA_BASH:-$PANAMA_PATH/config/bash}"
|
|
PANAMA_DOT="$PANAMA_PATH/config/dot"
|
|
PANAMA_OLD="$PANAMA_PATH/config/old"
|
|
CONFIG="$HOME/.config"
|
|
|
|
# Make backup folder if it doesn't exist -- and ~/.config itself, which a
|
|
# truly fresh HOME does not have yet. Before set -e above, its absence made
|
|
# every symlink below fail silently while the stage still reported success.
|
|
mkdir -p "$PANAMA_OLD" "$CONFIG"
|
|
|
|
# --- Bashrc ---
|
|
echo -e "\n--- Replacing .bashrc ---"
|
|
# Backup existing .bashrc if it's a regular file
|
|
if [ -f "$HOME/.bashrc" ] && [ ! -L "$HOME/.bashrc" ]; then
|
|
log "Backing up existing .bashrc"
|
|
mv "$HOME/.bashrc" "$PANAMA_OLD/.bashrc"
|
|
fi
|
|
# Remove old symlink if it exists and points somewhere else
|
|
if [ -L "$HOME/.bashrc" ]; then
|
|
log "Removing old .bashrc symlink"
|
|
rm "$HOME/.bashrc"
|
|
fi
|
|
# Symlink Panama .bashrc file to ~/.bashrc
|
|
log "Symlinking Panama .bashrc file to ~/.bashrc"
|
|
ln -s "$PANAMA_BASH/.bashrc" "$HOME/.bashrc"
|
|
|
|
# Each entry is symlinked as ~/.config/<name> -> $PANAMA_DOT/<name>.
|
|
#
|
|
# One desktop. "forge" was here while the GNOME session was still the fallback
|
|
# and Hyprland was being built beside it -- that hedge has been paid off, and a
|
|
# configuration for an extension nothing installs is worse than no configuration
|
|
# at all, because it reads as a supported thing.
|
|
#
|
|
# gnome-control-center is a separate matter and stays declared: Panama's own
|
|
# Settings hands off to it for the panels it deliberately does not own.
|
|
dirs=("espanso" "ghostty" "gtk-3.0" "gtk-4.0" "hypr" "kitty" "nvim" \
|
|
"quickshell" "tmux" "uwsm" "vicinae" "wofi" "xdg-desktop-portal")
|
|
|
|
# --- Vim vimrc ---
|
|
echo -e "\n--- Setting up vim ---"
|
|
mkdir -p "$HOME/.vim"
|
|
if [ -L "$HOME/.vim/vimrc" ]; then
|
|
rm "$HOME/.vim/vimrc"
|
|
log "Removed old symlink at ~/.vim/vimrc"
|
|
fi
|
|
if [ -f "$HOME/.vim/vimrc" ]; then
|
|
mv "$HOME/.vim/vimrc" "$PANAMA_OLD/vimrc"
|
|
log "Moved existing ~/.vim/vimrc to $PANAMA_OLD/vimrc"
|
|
fi
|
|
ln -s "$PANAMA_DOT/vim/vimrc" "$HOME/.vim/vimrc"
|
|
log "Linked $PANAMA_DOT/vim/vimrc → ~/.vim/vimrc"
|
|
|
|
for dir in "${dirs[@]}"; do
|
|
echo -e "\n--- Setting up $dir ---"
|
|
|
|
# Remove old symlink if it exists
|
|
if [ -L "$CONFIG/$dir" ]; then
|
|
rm "$CONFIG/$dir"
|
|
log "Removed old symlink at $CONFIG/$dir"
|
|
fi
|
|
|
|
# Backup existing directory if it exists
|
|
if [ -d "$CONFIG/$dir" ]; then
|
|
mv "$CONFIG/$dir" "$PANAMA_OLD/$dir"
|
|
log "Moved existing $dir config to $PANAMA_OLD/$dir"
|
|
# A regular file at this path (not a directory, not a symlink) is missed by
|
|
# the guards above, so `ln -s` below would fail with "File exists" -- back
|
|
# it up the same way.
|
|
elif [ -f "$CONFIG/$dir" ]; then
|
|
mv "$CONFIG/$dir" "$PANAMA_OLD/$dir"
|
|
log "Moved existing $dir file to $PANAMA_OLD/$dir"
|
|
fi
|
|
|
|
# Create symlink
|
|
ln -s "$PANAMA_DOT/$dir" "$CONFIG/$dir"
|
|
log "Linked $PANAMA_DOT/$dir → $CONFIG/$dir"
|
|
done
|
|
|
|
# tmux.conf ends with a source-file of current-theme.conf, generated from the
|
|
# color scheme rather than committed. Seed it so a fresh checkout starts themed
|
|
# -- the source-file is -q, so a missing file is silent, which would leave tmux
|
|
# unstyled with nothing to explain it.
|
|
TMUX_THEME="$PANAMA_DOT/tmux/current-theme.conf"
|
|
if [ -e "$TMUX_THEME" ]; then
|
|
log "Keeping existing tmux theme at $TMUX_THEME"
|
|
elif [ -d "$PANAMA_DOT/tmux/themes" ]; then
|
|
tmux_scheme="dark"
|
|
tmux_prefs="${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json"
|
|
if [ -r "$tmux_prefs" ]; then
|
|
tmux_stored="$(jq -r '.colorScheme // "dark"' "$tmux_prefs" 2>/dev/null || echo dark)"
|
|
[ "$tmux_stored" = "light" ] && tmux_scheme="light"
|
|
fi
|
|
[ "$tmux_scheme" = "light" ] && tmux_name="tokyonight-day" || tmux_name="tokyonight-moon"
|
|
cp "$PANAMA_DOT/tmux/themes/$tmux_name.conf" "$TMUX_THEME"
|
|
log "Seeded tmux $tmux_scheme theme ($tmux_name) → $TMUX_THEME"
|
|
fi
|
|
|
|
# hyprlock.conf is generated from a template on every color scheme change and
|
|
# is not committed. Seed it so the FIRST lock of a fresh install is themed --
|
|
# without it hyprlock falls back to its own defaults, which is a bare gray
|
|
# screen with none of this desktop's identity, and the first time anyone would
|
|
# find out is when they walked away from the machine.
|
|
HYPRLOCK_TEMPLATE="$PANAMA_DOT/hypr/hyprlock.conf.template"
|
|
HYPRLOCK_CONF="$PANAMA_DOT/hypr/hyprlock.conf"
|
|
if [ -e "$HYPRLOCK_CONF" ]; then
|
|
log "Keeping existing hyprlock config at $HYPRLOCK_CONF"
|
|
elif [ -r "$HYPRLOCK_TEMPLATE" ]; then
|
|
lock_scheme="dark"
|
|
lock_prefs="${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json"
|
|
if [ -r "$lock_prefs" ]; then
|
|
lock_stored="$(jq -r '.colorScheme // "dark"' "$lock_prefs" 2>/dev/null || echo dark)"
|
|
[ "$lock_stored" = "light" ] && lock_scheme="light"
|
|
fi
|
|
# Rendered by panama-palette's render_hyprlock, the same function
|
|
# panama-theme-apps and panama-lock use. This block used to carry its own
|
|
# table of literal rgba triples -- a third copy of the same eight colours --
|
|
# and a new theme meant remembering to edit all three. The one that got
|
|
# missed was always this one, because a wrong seed only shows up the first
|
|
# time somebody locks a fresh machine.
|
|
# shellcheck source=../../config/dot/quickshell/scripts/panama-palette
|
|
source "$PANAMA_DOT/quickshell/scripts/panama-palette"
|
|
lock_accent="$(jq -r '.accentName // "blue"' "$lock_prefs" 2>/dev/null || echo blue)"
|
|
if render_hyprlock "$lock_scheme" "$HYPRLOCK_TEMPLATE" "$HYPRLOCK_CONF" "$lock_accent"; then
|
|
log "Seeded hyprlock $lock_scheme theme → $HYPRLOCK_CONF"
|
|
else
|
|
log "Could not render the hyprlock theme; the first lock will use hyprlock's defaults"
|
|
fi
|
|
fi
|
|
|
|
# btop reads themes from its own config directory, but OWNS btop.conf -- it
|
|
# rewrites that file on exit -- so only the theme files are exposed, per file,
|
|
# and the config itself is left to btop. panama-theme-apps edits the single
|
|
# color_theme line in place.
|
|
BTOP_THEME_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/btop/themes"
|
|
mkdir -p "$BTOP_THEME_DIR"
|
|
for btop_theme_src in "$PANAMA_DOT"/btop/themes/*.theme; do
|
|
[ -e "$btop_theme_src" ] || continue
|
|
btop_theme_dst="$BTOP_THEME_DIR/$(basename "$btop_theme_src")"
|
|
if [ -L "$btop_theme_dst" ]; then
|
|
rm "$btop_theme_dst"
|
|
fi
|
|
if [ -e "$btop_theme_dst" ]; then
|
|
log "Keeping existing btop theme at $btop_theme_dst"
|
|
else
|
|
ln -s "$btop_theme_src" "$btop_theme_dst"
|
|
log "Linked btop theme → $btop_theme_dst"
|
|
fi
|
|
done
|
|
|
|
# GTK3 has no include mechanism, so its settings.ini is generated whole from a
|
|
# template rather than layered. Without this, a fresh checkout has a template
|
|
# and no settings.ini, and GTK3 applications fall back to their built-in theme.
|
|
# panama-theme-apps rewrites both files on every scheme change after this.
|
|
for gtk_version in 3.0 4.0; do
|
|
gtk_template="$PANAMA_DOT/gtk-$gtk_version/settings.ini.template"
|
|
gtk_settings="$PANAMA_DOT/gtk-$gtk_version/settings.ini"
|
|
[ -r "$gtk_template" ] || continue
|
|
if [ -e "$gtk_settings" ]; then
|
|
log "Keeping existing GTK settings at $gtk_settings"
|
|
else
|
|
gtk_scheme="dark"
|
|
gtk_prefs="${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json"
|
|
if [ -r "$gtk_prefs" ]; then
|
|
gtk_stored="$(jq -r '.colorScheme // "dark"' "$gtk_prefs" 2>/dev/null || echo dark)"
|
|
[ "$gtk_stored" = "light" ] && gtk_scheme="light"
|
|
fi
|
|
if [ "$gtk_scheme" = "light" ]; then
|
|
gtk_name="adw-gtk3"; gtk_dark=0
|
|
else
|
|
gtk_name="adw-gtk3-dark"; gtk_dark=1
|
|
fi
|
|
sed -e "s/@GTK_THEME@/$gtk_name/" -e "s/@PREFER_DARK@/$gtk_dark/" \
|
|
"$gtk_template" > "$gtk_settings"
|
|
log "Generated GTK $gtk_version settings ($gtk_name) → $gtk_settings"
|
|
fi
|
|
done
|
|
|
|
# kitty.conf ends with `include current-theme.conf`, and that file is generated
|
|
# from the desktop color scheme rather than committed -- it is machine state.
|
|
# A fresh checkout therefore has no such file, and kitty starts by complaining
|
|
# about a missing include and falling back to its stock colors. Seed it from
|
|
# the scheme in settings.json (dark unless the user has chosen otherwise) so a
|
|
# first launch is themed; ColorScheme.qml overwrites it on every change after.
|
|
KITTY_THEME="$PANAMA_DOT/kitty/current-theme.conf"
|
|
if [ -e "$KITTY_THEME" ]; then
|
|
log "Keeping existing kitty theme at $KITTY_THEME"
|
|
else
|
|
scheme="dark"
|
|
settings="${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json"
|
|
if [ -r "$settings" ]; then
|
|
stored="$(jq -r '.colorScheme // "dark"' "$settings" 2>/dev/null || echo dark)"
|
|
[ "$stored" = "light" ] && scheme="light"
|
|
fi
|
|
[ "$scheme" = "light" ] && theme="tokyonight-day" || theme="tokyonight-moon"
|
|
cp "$PANAMA_DOT/kitty/themes/$theme.conf" "$KITTY_THEME"
|
|
log "Seeded kitty $scheme theme ($theme) → $KITTY_THEME"
|
|
fi
|
|
|
|
# Vicinae 0.26 discovers user themes from its XDG data directory rather than
|
|
# from ~/.config/vicinae. Keep the authored theme in Panama with the rest of
|
|
# the launcher config and expose only that file at Vicinae's runtime path.
|
|
VICINAE_THEME_DIR="$HOME/.local/share/vicinae/themes"
|
|
mkdir -p "$VICINAE_THEME_DIR"
|
|
|
|
# Every authored theme, not just the dark one: vicinae.json selects a theme per
|
|
# system appearance, so linking only Moon left the launcher falling back to its
|
|
# stock palette whenever Panama was in light mode.
|
|
for theme_src in "$PANAMA_DOT"/vicinae/themes/*.toml; do
|
|
[ -e "$theme_src" ] || continue
|
|
theme_dst="$VICINAE_THEME_DIR/$(basename "$theme_src")"
|
|
|
|
if [ -L "$theme_dst" ]; then
|
|
rm "$theme_dst"
|
|
fi
|
|
|
|
if [ -e "$theme_dst" ]; then
|
|
log "Keeping existing Vicinae theme at $theme_dst"
|
|
else
|
|
ln -s "$theme_src" "$theme_dst"
|
|
log "Linked Vicinae theme → $theme_dst"
|
|
fi
|
|
done
|
|
|
|
# Panama-native applications live in the user data directory so launchers can
|
|
# discover them alongside system desktop entries. Keep each authored file in
|
|
# the repository and expose it with a narrow per-file symlink.
|
|
# Panama ships its own application icons. Without these the desktop entries fall
|
|
# back to a generic symbolic glyph, which is drawn for 16px toolbar use and
|
|
# looks wrong beside full-color application icons in the dock.
|
|
PANAMA_ICON_DIR="$PANAMA_PATH/config/local/share/icons/hicolor/scalable/apps"
|
|
USER_ICON_DIR="$HOME/.local/share/icons/hicolor/scalable/apps"
|
|
mkdir -p "$USER_ICON_DIR"
|
|
for icon_file in "$PANAMA_ICON_DIR"/*.svg; do
|
|
[[ -e "$icon_file" ]] || continue
|
|
icon_name="$(basename "$icon_file")"
|
|
icon_target="$USER_ICON_DIR/$icon_name"
|
|
if [[ -L "$icon_target" ]]; then
|
|
rm "$icon_target"
|
|
elif [[ -e "$icon_target" ]]; then
|
|
mv "$icon_target" "$icon_target.bak"
|
|
fi
|
|
ln -s "$icon_file" "$icon_target"
|
|
done
|
|
gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
|
|
|
|
# File-manager sidebar bookmarks, generated per machine from the template.
|
|
# The tracked file used to carry absolute /home/<author> paths -- seven dead
|
|
# sidebar entries in every file dialog for anyone else. Nautilus edits the
|
|
# generated instance in place (Ctrl+D writes this file through the symlink),
|
|
# which is exactly why it is gitignored: bookmarks are user data, not config.
|
|
BOOKMARKS_TEMPLATE="$PANAMA_DOT/gtk-3.0/bookmarks.template"
|
|
BOOKMARKS_FILE="$PANAMA_DOT/gtk-3.0/bookmarks"
|
|
if [ -r "$BOOKMARKS_TEMPLATE" ]; then
|
|
if [ -e "$BOOKMARKS_FILE" ]; then
|
|
log "Keeping existing GTK bookmarks at $BOOKMARKS_FILE"
|
|
else
|
|
sed "s|\$HOME|$HOME|g" "$BOOKMARKS_TEMPLATE" > "$BOOKMARKS_FILE"
|
|
log "Generated GTK bookmarks for $HOME"
|
|
fi
|
|
fi
|
|
|
|
# The pointer theme. oreo_blue_cursors is packaged by no Fedora repository -- it
|
|
# is a GitHub project -- so rather than download it at install time and depend on
|
|
# a URL that can rot, the theme is vendored into this repository and exposed the
|
|
# same way Panama's application icons are. Without it, every gsettings value,
|
|
# GTK settings.ini and XCURSOR_THEME naming it resolves to nothing and the
|
|
# session falls back to the default pointer.
|
|
PANAMA_CURSOR_DIR="$PANAMA_PATH/config/local/share/icons"
|
|
USER_CURSOR_DIR="$HOME/.local/share/icons"
|
|
mkdir -p "$USER_CURSOR_DIR"
|
|
for cursor_src in "$PANAMA_CURSOR_DIR"/*_cursors; do
|
|
[ -d "$cursor_src" ] || continue
|
|
cursor_name="$(basename "$cursor_src")"
|
|
cursor_dst="$USER_CURSOR_DIR/$cursor_name"
|
|
if [ -L "$cursor_dst" ]; then
|
|
rm "$cursor_dst"
|
|
elif [ -e "$cursor_dst" ]; then
|
|
log "Keeping existing cursor theme at $cursor_dst"
|
|
continue
|
|
fi
|
|
ln -s "$cursor_src" "$cursor_dst"
|
|
log "Linked cursor theme → $cursor_dst"
|
|
done
|
|
|
|
# The wallpaper the desktop already believes it ships. Wallpaper.qml calls this
|
|
# path `shippedPath` and substitutes it whenever wallpaperPath is unset, which is
|
|
# its default; panama-lock falls back to the same file, and hyprpaper.conf names
|
|
# it directly. Copied rather than symlinked, because ~/Pictures/Wallpapers is a
|
|
# directory the user owns and adds their own images to -- a symlink there would
|
|
# be a repository file masquerading as one of their own.
|
|
PANAMA_WALLPAPER_DIR="$PANAMA_PATH/config/wallpapers"
|
|
USER_WALLPAPER_DIR="$HOME/Pictures/Wallpapers"
|
|
if [ -d "$PANAMA_WALLPAPER_DIR" ]; then
|
|
mkdir -p "$USER_WALLPAPER_DIR"
|
|
for wallpaper_src in "$PANAMA_WALLPAPER_DIR"/*; do
|
|
[ -f "$wallpaper_src" ] || continue
|
|
wallpaper_dst="$USER_WALLPAPER_DIR/$(basename "$wallpaper_src")"
|
|
if [ -e "$wallpaper_dst" ]; then
|
|
log "Keeping existing wallpaper at $wallpaper_dst"
|
|
else
|
|
cp "$wallpaper_src" "$wallpaper_dst"
|
|
log "Copied wallpaper → $wallpaper_dst"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# The Firefox chrome. Edge-Frfox, vendored in config/firefox -- see the README
|
|
# there for what it is and whose it is. Both halves have to land or neither
|
|
# works: chrome/ holds the CSS, and user.js sets the preference that makes
|
|
# Firefox read chrome/ at all.
|
|
#
|
|
# Firefox has moved to the XDG directories, so a current build keeps profiles in
|
|
# ~/.config/mozilla/firefox and an older one in ~/.mozilla/firefox. Both are
|
|
# looked for rather than assumed -- assuming picked the wrong one on the machine
|
|
# this was written on.
|
|
PANAMA_FIREFOX_DIR="$PANAMA_PATH/config/firefox"
|
|
firefox_root=""
|
|
find_firefox_root() {
|
|
local candidate
|
|
for candidate in "${XDG_CONFIG_HOME:-$HOME/.config}/mozilla/firefox" "$HOME/.mozilla/firefox"; do
|
|
if [ -f "$candidate/profiles.ini" ]; then
|
|
firefox_root="$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
if [ -d "$PANAMA_FIREFOX_DIR" ] && command -v firefox >/dev/null 2>&1; then
|
|
echo -e "\n--- Setting up Firefox chrome ---"
|
|
|
|
# A profile does not exist until Firefox has been run once. -CreateProfile is
|
|
# a documented flag that creates one and exits rather than opening a window,
|
|
# so a fresh machine gets the theme on the first install instead of the
|
|
# second. MOZ_HEADLESS in case a future build decides otherwise.
|
|
if ! find_firefox_root; then
|
|
log "No Firefox profile yet; creating one"
|
|
MOZ_HEADLESS=1 firefox -CreateProfile panama >/dev/null 2>&1 || true
|
|
find_firefox_root || true
|
|
fi
|
|
|
|
if [ -z "$firefox_root" ]; then
|
|
log "Could not find or create a Firefox profile; skipping the chrome"
|
|
else
|
|
# Every profile, not just the default. profiles.ini records each one as a
|
|
# path that is relative to the root unless it says otherwise.
|
|
while read -r profile; do
|
|
[ -n "$profile" ] || continue
|
|
[ -d "$profile" ] || continue
|
|
|
|
for piece in chrome user.js; do
|
|
target="$profile/$piece"
|
|
if [ -L "$target" ]; then
|
|
rm "$target"
|
|
elif [ -e "$target" ]; then
|
|
backup="$PANAMA_OLD/firefox-$(basename "$profile")-$piece"
|
|
mv "$target" "$backup"
|
|
log "Moved existing $target to $backup"
|
|
fi
|
|
ln -s "$PANAMA_FIREFOX_DIR/$piece" "$target"
|
|
log "Linked $PANAMA_FIREFOX_DIR/$piece → $target"
|
|
done
|
|
done < <(awk -F= -v root="$firefox_root" '
|
|
/^\[/ { relative = 1; next }
|
|
/^IsRelative=/ { relative = $2; next }
|
|
/^Path=/ { print (relative == 1 ? root "/" $2 : $2) }
|
|
' "$firefox_root/profiles.ini")
|
|
fi
|
|
fi
|
|
|
|
PANAMA_APPLICATION_DIR="$PANAMA_PATH/config/local/share/applications"
|
|
USER_APPLICATION_DIR="$HOME/.local/share/applications"
|
|
mkdir -p "$USER_APPLICATION_DIR"
|
|
for desktop_file in "$PANAMA_APPLICATION_DIR"/*.desktop; do
|
|
[[ -e "$desktop_file" ]] || continue
|
|
desktop_name="$(basename "$desktop_file")"
|
|
desktop_target="$USER_APPLICATION_DIR/$desktop_name"
|
|
if [[ -L "$desktop_target" ]]; then
|
|
rm "$desktop_target"
|
|
elif [[ -e "$desktop_target" ]]; then
|
|
log "Keeping existing desktop entry at $desktop_target"
|
|
continue
|
|
fi
|
|
ln -s "$desktop_file" "$desktop_target"
|
|
log "Linked $desktop_name → $desktop_target"
|
|
done
|
|
|
|
# Curate what files open with, now that Panama's own entries are linked.
|
|
#
|
|
# Left alone, a machine decides this by installation order -- whichever
|
|
# application registered for image/png last wins it -- which is how a pixel-art
|
|
# editor ends up owning screenshots and a video transcoder ends up owning MP3s.
|
|
# Seeding only fills roles nobody has chosen for; an existing "Open With" choice
|
|
# is always kept.
|
|
# Panama's own systemd user units -- currently the Flatpak update timer, which
|
|
# the Updates page switches on and off. Linked rather than copied so an edit in
|
|
# the repository is the edit that runs.
|
|
PANAMA_UNIT_DIR="$PANAMA_PATH/config/local/share/systemd/user"
|
|
USER_UNIT_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/systemd/user"
|
|
if [[ -d "$PANAMA_UNIT_DIR" ]]; then
|
|
mkdir -p "$USER_UNIT_DIR"
|
|
for unit_file in "$PANAMA_UNIT_DIR"/*; do
|
|
[[ -e "$unit_file" ]] || continue
|
|
unit_name="$(basename "$unit_file")"
|
|
unit_target="$USER_UNIT_DIR/$unit_name"
|
|
if [[ -L "$unit_target" ]]; then
|
|
rm "$unit_target"
|
|
elif [[ -e "$unit_target" ]]; then
|
|
log "Keeping existing unit at $unit_target"
|
|
continue
|
|
fi
|
|
ln -s "$unit_file" "$unit_target"
|
|
log "Linked $unit_name → $unit_target"
|
|
done
|
|
systemctl --user daemon-reload 2>/dev/null || true
|
|
fi
|
|
|
|
# Quadlets: container definitions systemd turns into units at boot. Linked
|
|
# per-file into the directory the podman generator reads, rather than by
|
|
# symlinking ~/.config/containers wholesale -- podman keeps its registries,
|
|
# storage and login credentials in there, and that directory is the user's.
|
|
PANAMA_QUADLET_DIR="$PANAMA_PATH/config/containers"
|
|
USER_QUADLET_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/containers/systemd"
|
|
if [ -d "$PANAMA_QUADLET_DIR" ]; then
|
|
mkdir -p "$USER_QUADLET_DIR"
|
|
for quadlet in "$PANAMA_QUADLET_DIR"/*/*.container; do
|
|
[ -e "$quadlet" ] || continue
|
|
quadlet_dst="$USER_QUADLET_DIR/$(basename "$quadlet")"
|
|
if [ -L "$quadlet_dst" ]; then
|
|
rm "$quadlet_dst"
|
|
elif [ -e "$quadlet_dst" ]; then
|
|
log "Keeping existing quadlet at $quadlet_dst"
|
|
continue
|
|
fi
|
|
ln -s "$quadlet" "$quadlet_dst"
|
|
log "Linked quadlet → $quadlet_dst"
|
|
done
|
|
systemctl --user daemon-reload 2>/dev/null || true
|
|
fi
|
|
|
|
# Nautilus loads Python extensions from this directory. Linked per file rather
|
|
# than by symlinking the directory itself, the same way the quadlets and
|
|
# desktop entries are: Nautilus writes nothing here today, but a directory
|
|
# symlink into the repository is how machine state ends up in a tracked path.
|
|
# Hook samples. Copied rather than symlinked, and only when absent: hooks are
|
|
# the user's own scripts, and ~/.config/panama is theirs too -- settings.json
|
|
# lives there. A symlinked directory would put their scripts in the repository
|
|
# working tree, which is the mistake the gtk bookmarks made.
|
|
PANAMA_HOOK_SAMPLES="$PANAMA_DOT/panama/hooks"
|
|
USER_HOOK_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/panama/hooks"
|
|
if [ -d "$PANAMA_HOOK_SAMPLES" ]; then
|
|
mkdir -p "$USER_HOOK_DIR"
|
|
for sample in "$PANAMA_HOOK_SAMPLES"/*.sample; do
|
|
[ -e "$sample" ] || continue
|
|
sample_dst="$USER_HOOK_DIR/$(basename "$sample")"
|
|
if [ -e "$sample_dst" ]; then
|
|
log "Keeping existing hook sample at $sample_dst"
|
|
else
|
|
cp "$sample" "$sample_dst"
|
|
log "Copied hook sample → $sample_dst"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
PANAMA_NAUTILUS_DIR="$PANAMA_PATH/config/local/share/nautilus-python/extensions"
|
|
USER_NAUTILUS_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/nautilus-python/extensions"
|
|
if [ -d "$PANAMA_NAUTILUS_DIR" ]; then
|
|
mkdir -p "$USER_NAUTILUS_DIR"
|
|
for extension in "$PANAMA_NAUTILUS_DIR"/*.py; do
|
|
[ -e "$extension" ] || continue
|
|
extension_dst="$USER_NAUTILUS_DIR/$(basename "$extension")"
|
|
if [ -L "$extension_dst" ]; then
|
|
rm "$extension_dst"
|
|
elif [ -e "$extension_dst" ]; then
|
|
log "Keeping existing Nautilus extension at $extension_dst"
|
|
continue
|
|
fi
|
|
ln -s "$extension" "$extension_dst"
|
|
log "Linked Nautilus extension → $extension_dst"
|
|
done
|
|
fi
|
|
|
|
DEFAULT_APPS_HELPER="$PANAMA_PATH/config/dot/quickshell/scripts/panama-default-apps"
|
|
if [[ -x "$DEFAULT_APPS_HELPER" ]] && command -v xdg-mime >/dev/null 2>&1; then
|
|
update-desktop-database "$USER_APPLICATION_DIR" >/dev/null 2>&1 || true
|
|
while IFS= read -r seeded_line; do
|
|
log "Default applications: $seeded_line"
|
|
done < <("$DEFAULT_APPS_HELPER" seed 2>&1 || true)
|
|
fi
|