first commit

This commit is contained in:
Mattia Mascarello 2025-04-16 16:47:59 +02:00
commit 327959e0fa
4 changed files with 426 additions and 0 deletions

60
README.md Normal file
View File

@ -0,0 +1,60 @@
# GNOME Terminal Auto Theme Sync
![GNOME Terminal Logo](https://upload.wikimedia.org/wikipedia/commons/d/da/GNOME_Terminal_icon_2019.svg)
Automatically sync your GNOME Terminal theme with your system's light/dark mode preferences.
## Features
- 🌓 Automatic theme switching between light and dark modes
- ⚡ Instant theme application when system preference changes
- 🔄 Background process that monitors system theme changes
- 🛠️ Easy installation and uninstallation
## Installation
1. **Download the project files**:
```bash
git clone https://github.com/MatMasIt/gnome-terminal-autotheme.git
cd gnome-terminal-autotheme
```
2. **Run the installer**:
```bash
./install.sh
```
3. **Follow the prompts** to complete installation.
The installer will:
- Copy necessary files
- Set up autostart entry
- Start the theme sync service
## Uninstallation
To remove the auto theme sync, simply run the installer again.
You'll be prompted to choose:
- Remove just the autostart entry or the whole installation
- Whether to keep your terminal in light or dark mode after uninstall
## Requirements
- GNOME Desktop Environment (tested on GNOME 40+)
- GNOME Terminal
- Bash 4.0+
## Configuration
Advanced users can edit the theme definitions in: [sync-terminal-theme.sh](sync-terminal-theme.sh)
## Contributing
Contributions are welcome! Please open an issue or pull request.
## License
MIT License - See [LICENSE](LICENSE) file for details

View File

@ -0,0 +1,7 @@
[Desktop Entry]
Type=Application
Exec=PATH/sync-terminal-theme.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=GNOME Terminal Theme Sync

305
install.sh Executable file
View File

@ -0,0 +1,305 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# Constants
readonly DEFAULT_DIR="$HOME/.local/share/gnome-terminal-autotheme"
readonly DESKTOP_FILE_NAME="gnome-terminal-autotheme.desktop"
readonly AUTOSTART_DIR="$HOME/.config/autostart"
readonly AUTOSTART_TARGET="$AUTOSTART_DIR/$DESKTOP_FILE_NAME"
readonly SCRIPT_NAME="sync-terminal-theme.sh"
readonly VERSION="1.1.0"
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[0;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
# Utility functions
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
check_installed() {
command -v "$1" >/dev/null 2>&1
}
stop_existing_processes() {
local pids
pids=$(pgrep -f "$SCRIPT_NAME" || true)
if [[ -n "$pids" ]]; then
info "Stopping existing theme sync processes..."
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" && info "Stopped process $pid" || warning "Failed to stop process $pid"
fi
done
# Wait a moment for processes to terminate
sleep 0.5
fi
}
validate_path() {
local path="$1"
[[ -z "$path" ]] && { error "Path cannot be empty"; return 1; }
[[ "$path" == "/" ]] && { error "Cannot use root directory"; return 1; }
[[ "$path" =~ ^/etc ]] && { error "System directories not allowed"; return 1; }
return 0
}
is_gnome() {
[[ "$XDG_CURRENT_DESKTOP" =~ GNOME|gnome ]] || check_installed gnome-session
}
start_sync() {
info "Running sync script now in the background..."
nohup bash "$INSTALL_DIR/$SCRIPT_NAME" >/dev/null 2>&1 &
disown
success "Theme sync script running in background (PID: $!)"
}
check_integrity() {
local install_dir="${1:-$DEFAULT_DIR}"
info "Checking installation integrity in: $install_dir"
local desktop_file="$AUTOSTART_TARGET"
local target_exec
if [[ ! -f "$desktop_file" ]]; then
error "Autostart desktop entry not found: $desktop_file"
return 1
fi
# Extract Exec path from .desktop file
target_exec=$(grep -oP '^Exec=\K.+' "$desktop_file" | head -n1)
if [[ -z "$target_exec" ]]; then
error "No Exec entry found in desktop file"
return 1
fi
if [[ ! -f "$target_exec" ]]; then
error "Script file referenced in .desktop does not exist: $target_exec"
return 1
fi
if [[ ! -x "$target_exec" ]]; then
error "Script file is not executable: $target_exec"
return 1
fi
if ! head -n 1 "$target_exec" | grep -qE '^#!.*/bash'; then
error "Script does not start with a valid bash shebang: $target_exec"
return 1
fi
success "All basic integrity checks passed"
}
uninstall() {
local install_dir="$1"
local uninstall_choice="${2:-autostart}"
local theme_choice="${3:-d}"
info "Starting uninstallation..."
# Kill running process
stop_existing_processes
# Remove autostart entry
if [[ -f "$AUTOSTART_TARGET" ]]; then
rm -f -- "$AUTOSTART_TARGET" && success "Autostart entry removed" || error "Failed to remove autostart entry"
fi
# Apply final theme
case "${theme_choice,,}" in
l|light)
bash "$install_dir/$SCRIPT_NAME" light && success "Applied light theme" || error "Failed to apply light theme"
;;
d|dark)
bash "$install_dir/$SCRIPT_NAME" dark && success "Applied dark theme" || error "Failed to apply dark theme"
;;
*)
warning "No theme change applied"
;;
esac
# Remove installation directory if requested
if [[ "$uninstall_choice" =~ ^whole$ ]]; then
if [[ -d "$install_dir" ]]; then
rm -rf -- "$install_dir" && success "Installation directory removed" || error "Failed to remove directory"
else
warning "Installation directory not found"
fi
fi
success "Uninstallation complete"
}
install() {
local reinstall="${1:-false}"
local provided_path="${2:-}"
info "Starting installation..."
stop_existing_processes
# Check for gnome-terminal
if ! check_installed gnome-terminal; then
warning "gnome-terminal not found"
read -rp "Continue anyway? [y/N]: " proceed_terminal
[[ "${proceed_terminal,,}" =~ ^y ]] || { error "Aborted"; exit 1; }
fi
# Check for GNOME
if ! is_gnome; then
warning "GNOME not detected"
read -rp "Continue anyway? [y/N]: " proceed_gnome
[[ "${proceed_gnome,,}" =~ ^y ]] || { error "Aborted"; exit 1; }
fi
# Use provided path or prompt user
if [[ -n "$provided_path" ]]; then
INSTALL_DIR=$(realpath -m "$provided_path")
else
read -rp "Enter install path [default: $DEFAULT_DIR]: " install_dir
INSTALL_DIR=$(realpath -m "${install_dir:-$DEFAULT_DIR}")
fi
if ! validate_path "$INSTALL_DIR"; then
error "Invalid install path: $INSTALL_DIR"
exit 1
fi
# Create directory
mkdir -p -- "$INSTALL_DIR" || { error "Failed to create directory"; exit 1; }
# Verify source files exist
if [[ ! -f "$SCRIPT_NAME" || ! -f "$DESKTOP_FILE_NAME" ]]; then
error "Missing required files: $SCRIPT_NAME and $DESKTOP_FILE_NAME must be in current directory"
exit 1
fi
# Copy files
cp -- "$SCRIPT_NAME" "$INSTALL_DIR/" || { error "Failed to copy script"; exit 1; }
cp -- "$DESKTOP_FILE_NAME" "$INSTALL_DIR/" || { error "Failed to copy desktop file"; exit 1; }
# Make script executable
chmod +x "$INSTALL_DIR/$SCRIPT_NAME" || { error "Failed to make script executable"; exit 1; }
# Create autostart entry
mkdir -p -- "$AUTOSTART_DIR" || { error "Failed to create autostart directory"; exit 1; }
# Escape path for sed
local escaped_path=$(printf '%q' "$INSTALL_DIR/$SCRIPT_NAME")
sed "s|Exec=.*|Exec=$escaped_path|" "$INSTALL_DIR/$DESKTOP_FILE_NAME" > "$AUTOSTART_TARGET" || {
error "Failed to create autostart entry"
exit 1
}
success "Installation complete"
success "Script installed to: $INSTALL_DIR"
success "Autostart entry created: $AUTOSTART_TARGET"
# Start sync process
start_sync
}
main() {
echo -e "${BLUE}=== GNOME Terminal Auto Theme Sync v$VERSION ===${NC}"
# Check if installed
if [[ -f "$AUTOSTART_TARGET" ]]; then
local old_install_dir=$(grep -oP 'Exec=\K.+' "$AUTOSTART_TARGET" | sed "s|/$SCRIPT_NAME||")
if [[ -z "$old_install_dir" || ! -d "$old_install_dir" ]]; then
warning "Could not determine valid install path from autostart entry (you may want to look into it)"
info "Fallback to default installation directory"
old_install_dir="$DEFAULT_DIR"
if [[ ! -d "$old_install_dir" ]]; then
error "Default install directory does not exist: $old_install_dir"
exit 1
fi
fi
info "Theme watcher already installed at: $old_install_dir"
read -rp "Do you want to (u)ninstall, (r)einstall or check (i)ntegrity? [u/r/i/N]: " choice
case "${choice,,}" in
u)
# Loop until a valid choice is entered for uninstall
while true; do
read -rp "Uninstall (w)hole installation or (a)utostart entry? [w/a]: " uninstall_choice
case "${uninstall_choice,,}" in
a) uninstall_choice="autostart"; break ;;
w) uninstall_choice="whole"; break ;;
esac
echo "Invalid choice, please enter 'w' for whole or 'a' for autostart."
done
# Loop until a valid choice is entered for theme selection
while true; do
read -rp "Set terminal theme to (l)ight or (d)ark after uninstall? [l/d]: " theme_choice
case "${theme_choice,,}" in
l) theme_choice="l"; break ;;
d) theme_choice="d"; break ;;
esac
echo "Invalid choice, please enter 'l' for light or 'd' for dark."
done
uninstall "$old_install_dir" "$uninstall_choice" "$theme_choice"
exit 0
;;
r)
read -rp "Enter new install path [default: $DEFAULT_DIR]: " install_dir
INSTALL_DIR="${install_dir:-$DEFAULT_DIR}"
INSTALL_DIR=$(realpath -m "$INSTALL_DIR")
if [[ "$INSTALL_DIR" != "$old_install_dir" ]]; then
read -rp "The new install path is different from the old one ($old_install_dir). Remove old directory? [y/N]: " remove_old
if [[ "${remove_old,,}" =~ ^y ]]; then
if [[ -d "$old_install_dir" ]]; then
rm -rf -- "$old_install_dir" && success "Old installation directory removed" || error "Failed to remove old directory"
else
warning "Old installation directory not found"
fi
fi
fi
install true "$INSTALL_DIR"
;;
i)
check_integrity "$old_install_dir"
exit $?
;;
*)
info "No changes made"
exit 0
;;
esac
else
install
fi
}
main

54
sync-terminal-theme.sh Executable file
View File

@ -0,0 +1,54 @@
#!/bin/bash
# Get the default profile UUID
PROFILE_ID=$(gsettings get org.gnome.Terminal.ProfilesList default | tr -d \')
PROFILE_PATH="/org/gnome/terminal/legacy/profiles:/:$PROFILE_ID"
# Define themes
set_dark_theme() {
gsettings set org.gnome.Terminal.Legacy.Settings theme-variant 'dark'
dconf write $PROFILE_PATH/use-theme-colors false
dconf write $PROFILE_PATH/foreground-color "'#d4d4d4'"
dconf write $PROFILE_PATH/background-color "'#1e1e1e'"
dconf write $PROFILE_PATH/palette "['#1e1e1e', '#f44747', '#619955', '#ffaf00', '#85dfff', '#d27bff', '#4EC9B0', '#d4d4d4', '#808080', '#f44747', '#619955', '#ffaf00', '#85dfff', '#d27bff', '#4EC9B0', '#ffffff']"
dconf write $PROFILE_PATH/cursor-color "'#d4d4d4'"
}
set_light_theme() {
gsettings set org.gnome.Terminal.Legacy.Settings theme-variant 'light'
dconf write $PROFILE_PATH/use-theme-colors false
dconf write $PROFILE_PATH/foreground-color "'#000000'"
dconf write $PROFILE_PATH/background-color "'#ffffff'"
dconf write $PROFILE_PATH/palette "['#000000', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', '#d3d7cf', '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', '#eeeeec']"
dconf write $PROFILE_PATH/cursor-color "'#000000'"
}
update_terminal_theme() {
SCHEME=$(gsettings get org.gnome.desktop.interface color-scheme)
if [[ "$SCHEME" == *"dark"* ]]; then
set_dark_theme
else
set_light_theme
fi
}
# Check for manual arguments (light or dark)
if [[ "${1:-}" == "light" ]]; then
set_light_theme
echo "[sync-script] Applied light theme manually."
exit 0
elif [[ "${1:-}" == "dark" ]]; then
set_dark_theme
echo "[sync-script] Applied dark theme manually."
exit 0
fi
# Run once immediately to apply based on the current GNOME theme
update_terminal_theme
# Monitor and auto-update GNOME theme changes
echo "[sync-script] Monitoring GNOME theme changes..."
gsettings monitor org.gnome.desktop.interface color-scheme | while read -r; do
update_terminal_theme
done