diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 16fe567..b830f3f --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ linux-provision/ │ ├── 03-toolchains.sh # nvm, Node, uv, Python │ ├── 04-shell.sh # zsh, oh-my-zsh, powerlevel10k, configs │ ├── 05-git.sh # Git config, SSH key setup -│ ├── 06-uv-projects.sh # Clone + install Julian's uv tools from ~/Development -│ ├── 07-scripts.sh # ~/.local/bin (bw, zoom, env, etc.) +│ ├── 06-scripts.sh # ~/.local/bin (bw, zoom, env, etc.) +│ ├── 07-uv-projects.sh # Clone + install Julian's uv tools from ~/Development │ ├── 08-systemd.sh # User systemd services (porridge, swayidle, etc.) │ ├── 09-desktop.sh # Keybindings, hotkeys, ghostty, fonts │ ├── 10-docker.sh # Docker CE setup @@ -78,8 +78,8 @@ linux-provision/ | 03 | toolchains | Install nvm + Node LTS, uv, Python | | 04 | shell | Install zsh, oh-my-zsh, p10k, deploy .zshrc, .p10k.zsh | | 05 | git | Deploy .gitconfig, generate SSH key | -| 06 | uv-projects | Clone all Julian's Python tool repos from GitHub, uv install | -| 07 | scripts | Deploy ~/.local/bin scripts | +| 06 | scripts | Deploy ~/.local/bin scripts (bw, bw-load-ssh, Zoom wrapper, etc.) | +| 07 | uv-projects | Clone all Julian's Python tool repos from GitHub, uv install (needs SSH keys) | | 08 | systemd | Deploy and enable user systemd services | | 09 | desktop | Configure keybindings, hotkeys, ghostty, fonts | | 10 | docker | Install Docker CE, add user to docker group | diff --git a/provision.sh b/provision.sh index 9ed2482..a657b80 100755 --- a/provision.sh +++ b/provision.sh @@ -42,8 +42,8 @@ STAGES=( "03-toolchains" # nvm, Node, uv, Python "04-shell" # Zsh, oh-my-zsh, p10k "05-git" # Git config, SSH - "06-uv-projects" # Clone + install Julian's Python tools - "07-scripts" # ~/.local/bin custom scripts + "06-scripts" # ~/.local/bin custom scripts (bw, bw-load-ssh, etc.) + "07-uv-projects" # Clone + install Julian's Python tools (needs SSH keys from stage 06) "08-systemd" # User systemd services "09-desktop" # Keybindings, hotkeys, desktop config "10-docker" # Docker CE diff --git a/stages/06-scripts.sh b/stages/06-scripts.sh new file mode 100755 index 0000000..6601947 --- /dev/null +++ b/stages/06-scripts.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +# =========================================================================== +# Stage 06: Custom Scripts (~/.local/bin/) +# Deploys Julian's custom scripts: Bitwarden SSH loader, Zoom wrapper, +# idle battery suspend, env PATH helper, and more. +# =========================================================================== +# These are the "glue" scripts that make the desktop work the way Julian +# expects. They were found in ~/.local/bin/ on the Pop machine. +# +# Config templates are in config/scripts/ +# =========================================================================== + +CONFIG_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/config" +SCRIPTS_DIR="${CONFIG_DIR}/scripts" +TARGET_DIR="$HOME/.local/bin" + +mkdir -p "$TARGET_DIR" + +info "Deploying custom scripts to $TARGET_DIR..." + +# ---- 1. env.sh — PATH helper (sourced by .profile / .bashrc) ---- +# Ensures ~/.local/bin is in PATH without duplicate entries. +if [ -f "$SCRIPTS_DIR/env.sh" ]; then + cp "$SCRIPTS_DIR/env.sh" "$TARGET_DIR/env" + chmod +x "$TARGET_DIR/env" + ok "env deployed." +fi + +# ---- 2. bw-load-ssh.sh — Load SSH keys from Bitwarden ---- +# Script that fetches SSH keys from Bitwarden vault and loads into ssh-agent. +# Depends on: bw (Bitwarden CLI), jq, ssh-agent running. +if [ -f "$SCRIPTS_DIR/bw-load-ssh.sh" ]; then + cp "$SCRIPTS_DIR/bw-load-ssh.sh" "$TARGET_DIR/bw-load-ssh.sh" + chmod +x "$TARGET_DIR/bw-load-ssh.sh" + ok "bw-load-ssh.sh deployed." +fi + +# ---- 3. zoom.sh — Zoom wrapper for Wayland + AMD GPU ---- +# Forces Wayland native mode and VAAPI hardware video decoding on AMD Radeon. +# Without this, Zoom would use XWayland and software decoding (bad perf). +if [ -f "$SCRIPTS_DIR/zoom.sh" ]; then + cp "$SCRIPTS_DIR/zoom.sh" "$TARGET_DIR/zoom" + chmod +x "$TARGET_DIR/zoom" + ok "zoom wrapper deployed." +else + # Create default Zoom wrapper + cat > "$TARGET_DIR/zoom" << 'SCRIPT' +#!/bin/bash +# Zoom wrapper — forces Wayland + HW acceleration on AMD GPU +export QT_QPA_PLATFORM=wayland +export LIBVA_DRIVER_NAME=radeonsi +export LIBVA_DRI3_DISABLE=0 +exec /usr/bin/zoom "$@" +SCRIPT + chmod +x "$TARGET_DIR/zoom" + ok "zoom wrapper created (default)." +fi + +# ---- 4. idle-battery-suspend.sh — Suspend on battery after idle ---- +# Checks if AC is disconnected before suspending. Prevents suspend on desktop. +# Used by swayidle.service (stage 08). +if [ -f "$SCRIPTS_DIR/idle-battery-suspend.sh" ]; then + cp "$SCRIPTS_DIR/idle-battery-suspend.sh" "$TARGET_DIR/idle-battery-suspend.sh" + chmod +x "$TARGET_DIR/idle-battery-suspend.sh" + ok "idle-battery-suspend.sh deployed." +fi + +# ---- 5. Bitwarden CLI (bw) ---- +# On Pop: ~/.local/bin/bw (138 MB standalone binary) +if ! command -v bw &>/dev/null; then + info "Installing Bitwarden CLI..." + # bw is a standalone binary — download it + BW_LATEST=$(curl -fsSL "https://api.github.com/repos/bitwarden/clients/releases?per_page=1" | grep -oP '"tag_name":\s*"cli-v\K[^"]+' | head -1) || BW_LATEST="2025.1.0" + curl -fsSL "https://github.com/bitwarden/clients/releases/download/cli-v${BW_LATEST}/bw-linux-${BW_LATEST}.zip" -o /tmp/bw.zip 2>/dev/null && { + unzip -o /tmp/bw.zip -d /tmp/bw-extract 2>/dev/null + cp /tmp/bw-extract/bw "$TARGET_DIR/bw" + chmod +x "$TARGET_DIR/bw" + rm -rf /tmp/bw.zip /tmp/bw-extract + ok "Bitwarden CLI installed." + } || warn "Bitwarden CLI download failed. Install manually: https://bitwarden.com/help/cli/" +fi + +# Ensure permissions +chmod -R 755 "$TARGET_DIR" 2>/dev/null || true + +# =========================================================================== +# 6. Bitwarden Login & Session Setup +# Interactive step: logs into Bitwarden and saves session key for later use. +# Run bw-load-ssh.sh afterwards to load SSH keys from the vault. +# =========================================================================== +echo "" +info "============================================" +info " Bitwarden CLI Setup" +info "============================================" +info "" +info "Julian stores SSH keys and API tokens in Bitwarden." +info "This step will log you into Bitwarden and save your session." +info "" + +BW_SESSION_DIR="$HOME/.config/Bitwarden CLI" +BW_SESSION_FILE="${BW_SESSION_DIR}/.session" +mkdir -p "$BW_SESSION_DIR" + +# ---- Skip if session already exists and is valid ---- +if [ -f "$BW_SESSION_FILE" ]; then + export BW_SESSION=$(cat "$BW_SESSION_FILE") + BW_STATUS=$(bw status 2>/dev/null | jq -r '.status // "unauthenticated"' 2>/dev/null || echo "unauthenticated") + if [ "$BW_STATUS" = "unlocked" ]; then + ok "Bitwarden session already valid (skipping login)." + # Skip to SSH key loading + else + # Session file exists but expired — need to re-authenticate + warn "Session file exists but vault is $BW_STATUS. Re-authenticating..." + _do_bw_setup=true + fi +else + _do_bw_setup=true +fi + +if [ "${_do_bw_setup:-false}" = true ]; then + # ---- Self-hosted server configuration ---- + CURRENT_SERVER=$(bw config server 2>/dev/null || echo "https://bitwarden.com") + if [ "$CURRENT_SERVER" = "https://bitwarden.com" ] || [ "$CURRENT_SERVER" = "null" ]; then + echo "----------------------------------------" + info "Server: Bitwarden cloud (default)" + echo "----------------------------------------" + echo "" + if [ -t 0 ]; then + read -r -p "Use a self-hosted Bitwarden server instead? [y/N] " use_selfhosted + if [[ "$use_selfhosted" =~ ^[Yy]$ ]]; then + echo "" + read -r -p "Enter your self-hosted server URL (e.g. https://vault.example.com): " server_url + if [ -n "$server_url" ]; then + bw config server "$server_url" 2>/dev/null && ok "Server set to $server_url" || warn "Failed to set server URL." + echo "" + fi + fi + else + info "Non-interactive mode: using default Bitwarden cloud." + info "To use a self-hosted server, run: bw config server " + fi + else + info "Server: $CURRENT_SERVER (self-hosted)" + fi + + # Check current bw status + BW_STATUS=$(bw status 2>/dev/null | jq -r '.status // "unauthenticated"' 2>/dev/null || echo "unauthenticated") + + if [ "$BW_STATUS" = "unauthenticated" ]; then + echo "----------------------------------------" + info "Step 1: Log in to Bitwarden" + info "You will be prompted for your email and master password." + info "If you have 2FA enabled, you'll be prompted for that too." + echo "----------------------------------------" + echo "" + bw login hi@julianprester.com || { + warn "Bitwarden login failed or was cancelled." + warn "You can run 'bw login' manually later." + } + echo "" + elif [ "$BW_STATUS" = "locked" ]; then + info "Bitwarden vault is locked (already logged in)." + elif [ "$BW_STATUS" = "unlocked" ]; then + info "Bitwarden vault is already unlocked." + fi + + echo "----------------------------------------" + info "Step 2: Unlock vault and save session key" + info "You will be prompted for your master password again." + info "The session key will be saved to ${BW_SESSION_FILE}" + echo "----------------------------------------" + echo "" + bw unlock --raw > "$BW_SESSION_FILE" 2>/dev/null && { + chmod 600 "$BW_SESSION_FILE" + ok "Session key saved to ${BW_SESSION_FILE}" + } || warn "Failed to save session key. Run 'bw unlock --raw > ${BW_SESSION_FILE}' manually." + echo "" +fi + +# ---- Load SSH keys from Bitwarden ---- +if [ -x "$TARGET_DIR/bw-load-ssh.sh" ]; then + echo "----------------------------------------" + info "Step 3: Load SSH keys from Bitwarden" + echo "----------------------------------------" + echo "" + # Export session so bw-load-ssh can use it + if [ -f "$BW_SESSION_FILE" ]; then + export BW_SESSION=$(cat "$BW_SESSION_FILE") + fi + + if bw status 2>/dev/null | jq -e '.status == "unlocked"' >/dev/null 2>&1; then + info "Running bw-load-ssh.sh..." + bash "$TARGET_DIR/bw-load-ssh.sh" && ok "SSH keys loaded." || warn "SSH key loading had issues." + else + warn "Vault is not unlocked. Skipping SSH key loading." + warn "Run 'bw-load-ssh.sh' manually after unlocking." + fi + echo "" +fi + +ok "Stage 06 complete: custom scripts and Bitwarden configured." diff --git a/stages/07-scripts.sh b/stages/07-scripts.sh deleted file mode 100644 index 4dc5b33..0000000 --- a/stages/07-scripts.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash -# =========================================================================== -# Stage 07: Custom Scripts (~/.local/bin/) -# Deploys Julian's custom scripts: Bitwarden SSH loader, Zoom wrapper, -# idle battery suspend, env PATH helper, and more. -# =========================================================================== -# These are the "glue" scripts that make the desktop work the way Julian -# expects. They were found in ~/.local/bin/ on the Pop machine. -# -# Config templates are in config/scripts/ -# =========================================================================== - -CONFIG_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/config" -SCRIPTS_DIR="${CONFIG_DIR}/scripts" -TARGET_DIR="$HOME/.local/bin" - -mkdir -p "$TARGET_DIR" - -info "Deploying custom scripts to $TARGET_DIR..." - -# ---- 1. env.sh — PATH helper (sourced by .profile / .bashrc) ---- -# Ensures ~/.local/bin is in PATH without duplicate entries. -if [ -f "$SCRIPTS_DIR/env.sh" ]; then - cp "$SCRIPTS_DIR/env.sh" "$TARGET_DIR/env" - chmod +x "$TARGET_DIR/env" - ok "env deployed." -fi - -# ---- 2. bw-load-ssh.sh — Load SSH keys from Bitwarden ---- -# Script that fetches SSH keys from Bitwarden vault and loads into ssh-agent. -# Depends on: bw (Bitwarden CLI), jq, ssh-agent running. -if [ -f "$SCRIPTS_DIR/bw-load-ssh.sh" ]; then - cp "$SCRIPTS_DIR/bw-load-ssh.sh" "$TARGET_DIR/bw-load-ssh.sh" - chmod +x "$TARGET_DIR/bw-load-ssh.sh" - ok "bw-load-ssh.sh deployed." -fi - -# ---- 3. zoom.sh — Zoom wrapper for Wayland + AMD GPU ---- -# Forces Wayland native mode and VAAPI hardware video decoding on AMD Radeon. -# Without this, Zoom would use XWayland and software decoding (bad perf). -if [ -f "$SCRIPTS_DIR/zoom.sh" ]; then - cp "$SCRIPTS_DIR/zoom.sh" "$TARGET_DIR/zoom" - chmod +x "$TARGET_DIR/zoom" - ok "zoom wrapper deployed." -else - # Create default Zoom wrapper - cat > "$TARGET_DIR/zoom" << 'SCRIPT' -#!/bin/bash -# Zoom wrapper — forces Wayland + HW acceleration on AMD GPU -export QT_QPA_PLATFORM=wayland -export LIBVA_DRIVER_NAME=radeonsi -export LIBVA_DRI3_DISABLE=0 -exec /usr/bin/zoom "$@" -SCRIPT - chmod +x "$TARGET_DIR/zoom" - ok "zoom wrapper created (default)." -fi - -# ---- 4. idle-battery-suspend.sh — Suspend on battery after idle ---- -# Checks if AC is disconnected before suspending. Prevents suspend on desktop. -# Used by swayidle.service (stage 08). -if [ -f "$SCRIPTS_DIR/idle-battery-suspend.sh" ]; then - cp "$SCRIPTS_DIR/idle-battery-suspend.sh" "$TARGET_DIR/idle-battery-suspend.sh" - chmod +x "$TARGET_DIR/idle-battery-suspend.sh" - ok "idle-battery-suspend.sh deployed." -fi - -# ---- 5. Bitwarden CLI (bw) ---- -# On Pop: ~/.local/bin/bw (138 MB standalone binary) -if ! command -v bw &>/dev/null; then - info "Installing Bitwarden CLI..." - # bw is a standalone binary — download it - BW_LATEST=$(curl -fsSL "https://api.github.com/repos/bitwarden/clients/releases?per_page=1" | grep -oP '"tag_name":\s*"cli-v\K[^"]+' | head -1) || BW_LATEST="2025.1.0" - curl -fsSL "https://github.com/bitwarden/clients/releases/download/cli-v${BW_LATEST}/bw-linux-${BW_LATEST}.zip" -o /tmp/bw.zip 2>/dev/null && { - unzip -o /tmp/bw.zip -d /tmp/bw-extract 2>/dev/null - cp /tmp/bw-extract/bw "$TARGET_DIR/bw" - chmod +x "$TARGET_DIR/bw" - rm -rf /tmp/bw.zip /tmp/bw-extract - ok "Bitwarden CLI installed." - } || warn "Bitwarden CLI download failed. Install manually: https://bitwarden.com/help/cli/" -fi - -# Ensure permissions -chmod -R 755 "$TARGET_DIR" 2>/dev/null || true - -ok "Stage 07 complete: custom scripts deployed to ~/.local/bin." diff --git a/stages/06-uv-projects.sh b/stages/07-uv-projects.sh old mode 100644 new mode 100755 similarity index 81% rename from stages/06-uv-projects.sh rename to stages/07-uv-projects.sh index 06a285d..041f30d --- a/stages/06-uv-projects.sh +++ b/stages/07-uv-projects.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # =========================================================================== -# Stage 06: Julian's uv Python Tools — Clone & Install +# Stage 07: Julian's uv Python Tools — Clone & Install # Clones all custom Python tool repos (from GitHub) into ~/Development/ # and installs them via 'uv tool install' (editable mode from local path). # =========================================================================== @@ -34,7 +34,7 @@ TOOLS=( "kannwas:julianprester:true:false" # Kannwas tool "tb-api:julianprester:false:false" # Thunderbird REST API (not a Python/npm pkg — Firefox addon) "hotkeys:julianprester:false:false" # Shell scripts for Wayland hotkeys (no install needed) - "ocpa:julianprester:true:false" # OpenCode pi agent Python package + "ocpa-repo:julianprester:true:false" # OpenCode pi agent Python package ) # =========================================================================== @@ -52,10 +52,14 @@ for tool_entry in "${TOOLS[@]}"; do git -C "$target_dir" pull --ff-only 2>/dev/null || warn "Could not pull $name." else info "Cloning $org/$name..." - git clone "git@github.com:${org}/${name}.git" "$target_dir" 2>/dev/null || { - warn "Clone failed for ${org}/${name}. Trying HTTPS fallback..." - git clone "https://github.com/${org}/${name}.git" "$target_dir" 2>/dev/null || { - warn "Could not clone ${org}/${name}. SSH keys not set up? Skipping." + # Try SSH first (requires SSH keys loaded from Bitwarden) + GIT_TERMINAL_PROMPT=0 git clone "git@github.com:${org}/${name}.git" "$target_dir" 2>/dev/null || { + # Fallback to HTTPS for public repos (no auth needed) + GIT_TERMINAL_PROMPT=0 git clone "https://github.com/${org}/${name}.git" "$target_dir" 2>/dev/null || { + warn "Could not clone ${org}/${name}." + warn " Load SSH keys from Bitwarden first, then run:" + warn " git clone git@github.com:${org}/${name}.git ~/Development/$name" + warn " Or set up a GitHub token for HTTPS authentication." continue } } diff --git a/stages/08-systemd.sh b/stages/08-systemd.sh old mode 100644 new mode 100755 index 7a54b26..a01cb21 --- a/stages/08-systemd.sh +++ b/stages/08-systemd.sh @@ -45,9 +45,8 @@ install_service_file "$SERVICES_DIR/pi-overview.service" "pi-overview.service" # ---- 4. bw-ssh-keys.service — Load Bitwarden SSH keys at boot ---- install_service_file "$SERVICES_DIR/bw-ssh-keys.service" "bw-ssh-keys.service" -# ---- 5. mempi-sync.service + timer — Sync memory DB to Nextcloud ---- +# ---- 5. mempi-sync.service — Sync memory DB to Nextcloud ---- install_service_file "$SERVICES_DIR/mempi-sync.service" "mempi-sync.service" -install_service_file "$SERVICES_DIR/mempi-sync.timer" "mempi-sync.timer" # ---- 6. empty_downloads.service — Clear Downloads at login ---- install_service_file "$SERVICES_DIR/empty_downloads.service" "empty_downloads.service" @@ -58,37 +57,36 @@ info "Enabling and starting services..." # Services that should start automatically (enabled) systemctl --user daemon-reload -# Check which scripts from stage 07 are available before enabling services. +# Check which scripts from stages 06 and 07 are available before enabling services. # This avoids failures when running stages out of order. if [ -x "$HOME/.local/bin/porridge" ]; then systemctl --user enable --now porridge.service 2>/dev/null && ok "porridge.service enabled" else - warn "porridge.service skipped (binary not found — run stage 07 first)." + warn "porridge.service skipped (binary not found — run stage 06 first)." fi if [ -x "$HOME/.local/bin/porridge" ]; then systemctl --user enable --now porridge-dictate.service 2>/dev/null && ok "porridge-dictate.service enabled" else - warn "porridge-dictate.service skipped (binary not found — run stage 07 first)." + warn "porridge-dictate.service skipped (binary not found — run stage 06 first)." fi if [ -x "$HOME/.local/bin/pi-overview" ]; then systemctl --user enable --now pi-overview.service 2>/dev/null && ok "pi-overview.service enabled" else - warn "pi-overview.service skipped (binary not found — run stage 06-uv-projects first)." + warn "pi-overview.service skipped (binary not found — run stage 07 first)." fi if [ -f "$HOME/.local/bin/bw-load-ssh.sh" ]; then systemctl --user enable bw-ssh-keys.service 2>/dev/null && ok "bw-ssh-keys.service enabled" else - warn "bw-ssh-keys.service skipped (script not found — run stage 07 first)." + warn "bw-ssh-keys.service skipped (script not found — run stage 06 first)." fi systemctl --user enable --now empty_downloads.service 2>/dev/null && ok "empty_downloads.service enabled" || warn "empty_downloads.service not started." -# Timers -systemctl --user enable --now mempi-sync.timer 2>/dev/null && ok "mempi-sync.timer enabled" || warn "mempi-sync.timer not started." +# (No timers currently) info "===== Service Status =====" systemctl --user list-units --type=service --state=running 2>/dev/null | grep -E "(porridge|swayidle|pi-overview|mempi|bw-ssh|empty)" || true diff --git a/stages/09-desktop.sh b/stages/09-desktop.sh old mode 100644 new mode 100755 index 2ba9695..9cc80c9 --- a/stages/09-desktop.sh +++ b/stages/09-desktop.sh @@ -21,7 +21,7 @@ DE="${XDG_CURRENT_DESKTOP:-unknown}" # =========================================================================== # These are shell scripts that use selected text for quick actions. # They rely on: wl-clipboard, wtype, wofi, jq, xdg-open -# Installed from ~/Development/hotkeys/ (cloned in stage 06). +# Installed from ~/Development/hotkeys/ (cloned in stage 07). info "Setting up hotkey scripts..." HOTKEYS_DIR="$HOME/Development/hotkeys" @@ -34,7 +34,7 @@ if [ -d "$HOTKEYS_DIR" ]; then done ok "Hotkey scripts linked to ~/.local/bin/" else - warn "hotkeys repo not cloned yet. Run stage 06 first." + warn "hotkeys repo not cloned yet. Run stage 07 first." fi # =========================================================================== @@ -136,7 +136,48 @@ if [ -f "$CONFIG_DIR/ghostty/config" ]; then fi # =========================================================================== -# 5. Fonts (Nerd Fonts for terminal + dev) +# 5. Autostart applications +# =========================================================================== +info "Configuring autostart applications..." +AUTOSTART_DIR="$HOME/.config/autostart" +mkdir -p "$AUTOSTART_DIR" + +autostart_app() { + local app_name="$1" + local desktop_file="$2" + local target="$AUTOSTART_DIR/$app_name.desktop" + + if [ -f "$target" ]; then + ok " Autostart '$app_name' already configured." + return + fi + + # Look for the desktop file in standard locations + local found="" + for dir in /usr/share/applications /var/lib/flatpak/exports/share/applications ~/.local/share/applications; do + if [ -f "$dir/$desktop_file" ]; then + found="$dir/$desktop_file" + break + fi + done + + if [ -n "$found" ]; then + cp "$found" "$target" + ok " Autostart '$app_name' configured." + else + warn " Desktop file for '$app_name' not found. Create manually: $target" + fi +} + +# Apps to autostart at login +autostart_app "firefox" "org.mozilla.firefox.desktop" +autostart_app "ghostty" "com.mitchellh.ghostty.desktop" +autostart_app "nextcloud" "com.nextcloud.desktopclient.nextcloud.desktop" +autostart_app "obsidian" "md.obsidian.Obsidian.desktop" +autostart_app "thunderbird" "net.thunderbird.Thunderbird.desktop" + +# =========================================================================== +# 6. Fonts (Nerd Fonts for terminal + dev) # =========================================================================== info "Installing Nerd Fonts..." FONT_DIR="$HOME/.local/share/fonts" @@ -180,7 +221,7 @@ fc-cache -f "$FONT_DIR" 2>/dev/null || true ok "Font cache rebuilt." # =========================================================================== -# 6. GTK Theme (dark mode + Papirus icons) +# 7. GTK Theme (dark mode + Papirus icons) # =========================================================================== info "Setting GTK theme and icons..." gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' 2>/dev/null || true @@ -189,7 +230,7 @@ gsettings set org.gnome.desktop.interface icon-theme 'Papirus-Dark' 2>/dev/null ok "GTK dark theme + Papirus icons set." # =========================================================================== -# 7. Solaar (Logitech peripherals) config +# 8. Solaar (Logitech peripherals) config # =========================================================================== # The Pop machine had config for MX Keys Mini + MX Master 3. # Solaar config is auto-generated when you pair devices. The config