From 313807730aae2324223b7c4677939b6b3296d860 Mon Sep 17 00:00:00 2001 From: Aygea Date: Sat, 28 Mar 2026 23:17:20 -0700 Subject: [PATCH] Add automated installer with OS detection and curl pipe support - install.sh: One-command installer with macOS/Ubuntu/Arch detection - Supports curl pipe (remote) and local execution - Interactive sudo prompt for Linux, user-level by default - Idempotent rc file edits with marker blocks - Backs up existing configs before overwriting - Installs: fonts, tmux, starship, fetch scripts, iTerm2 colors - --uninstall flag restores backups and removes all changes - Update .gitignore to include fonts/ directory - Update README.md with install instructions - Update TERMINAL-SETUP.md to reference automated installer Co-Authored-By: Claude Opus 4.6 --- .gitignore | 4 +- README.md | 22 +- TERMINAL-SETUP.md | 5 +- install.sh | 606 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 633 insertions(+), 4 deletions(-) create mode 100755 install.sh diff --git a/.gitignore b/.gitignore index 5819150..43e15a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Font files — included in private mirror only -# fonts/ +# Font files — download separately from nerdfonts.com +fonts/ # macOS .DS_Store diff --git a/README.md b/README.md index 5bcbef3..db15f8b 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,29 @@ Tokyo Night base with Aygea brand accent colors. Baby blue, sapphire, pink, silv See [TERMINAL-SETUP.md](TERMINAL-SETUP.md) for full step-by-step install instructions. +## Install + +**One command:** +```bash +./install.sh +``` + +**Or remote (curl pipe):** +```bash +curl -fsSL https://raw.githubusercontent.com/itsaygea/aygeaNight/main/install.sh | bash +``` + +Flags: `--sudo` · `--skip-fonts` · `--skip-tmux` · `--skip-starship` · `--skip-fetch` · `--uninstall` + +The installer auto-detects your OS, installs all dependencies, and configures everything. It asks about sudo on Linux; defaults to user-level installs otherwise. + +See [TERMINAL-SETUP.md](TERMINAL-SETUP.md) for full step-by-step manual instructions. + +Run `./install.sh --help` for all options. + ## Font -This theme uses **JetBrainsMono Nerd Font**. It's not included in this repo — download it from [nerdfonts.com](https://www.nerdfonts.com/font-downloads). +JetBrainsMono Nerd Font files are included in `fonts/JetBrainsMono/`. The installer copies them automatically. ## Colors diff --git a/TERMINAL-SETUP.md b/TERMINAL-SETUP.md index 757f4c9..93e3bf2 100644 --- a/TERMINAL-SETUP.md +++ b/TERMINAL-SETUP.md @@ -1,8 +1,11 @@ -# 🦊 AygeaNight Terminal Setup Guide +# AygeaNight Terminal Setup Guide Tokyo Night base · Aygea brand accent colors Baby blue, sapphire, pink, silver — dark navy background +> **The recommended way to install is the automated installer: `./install.sh`** +> This guide covers the manual step-by-step process for reference or customization. + --- ## Files in this repo diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..83f1a25 --- /dev/null +++ b/install.sh @@ -0,0 +1,606 @@ +#!/usr/bin/env bash +# ╭──────────────────────────────────────────────────────────────────╮ +# │ install.sh · AygeaNight terminal theme installer │ +# │ curl -fsSL /install.sh | bash │ +# ╰──────────────────────────────────────────────────────────────────╯ +set -euo pipefail + +parse_flags() { + while [[ $# -gt 0 ]]; do + case "$1" in + --sudo) USE_SUDO=1; shift ;; + --skip-fonts) SKIP_FONTS=1; shift ;; + --skip-tmux) SKIP_TMUX=1; shift ;; + --skip-starship) SKIP_STARSHIP=1; shift ;; + --skip-fetch) SKIP_FETCH=1; shift ;; + --uninstall) UNINSTALL=1; shift ;; + -h|--help) usage ;; + *) warn "Unknown flag: $1"; shift ;; + esac + done +} + +VERSION="1.0.0" +REPO="itsaygea/aygeaNight" +REPO_URL="https://github.com/${REPO}" +MARKER_BEGIN="# >>> aygea-night >>>" +MARKER_END="# <<< aygea-night <<<" + +# ── Globals ────────────────────────────────────────────────────── +OS="" +SCRIPT_DIR="" +USE_SUDO=0 +SKIP_FONTS=0 +SKIP_TMUX=0 +SKIP_STARSHIP=0 +SKIP_FETCH=0 +UNINSTALL=0 +STEP_NUM=0 +LOCAL_BIN="$HOME/.local/bin" +TMPDIR_AYGEA="" + +# ── Cleanup temp dir on exit ──────────────────────────────────── +cleanup() { + if [[ -n "${TMPDIR_AYGEA}" && -d "${TMPDIR_AYGEA}" ]]; then + rm -rf "${TMPDIR_AYGEA}" || true + fi +} +trap cleanup EXIT + +# ── Color palette (truecolor) ────────────────────────────────── +_tput() { [[ -t 1 ]] && printf '\033[38;2;%s;%s;%sm' "$1" "$2" "$3" || true; } +C_RESET="" +[[ -t 1 ]] && C_RESET=$(printf '\033[0m') || true + +C_BLUE=$(_tput 175 203 255) # Baby Blue #AFCBFF +C_SAPPHIRE=$(_tput 106 143 211) # Sapphire #6A8FD3 +C_PINK=$(_tput 248 200 220) # Soft Pink #F8C8DC +C_PINK_DK=$(_tput 216 140 168) # Pink Dark #D88CA8 +C_SILVER=$(_tput 230 238 248) # Silver #E6EEF8 +C_DIM=$(_tput 59 74 117) # Navy dim #3B4A75 +C_NAVY=$(_tput 42 58 94) # Navy mid #2A3A5E +C_BOLD="" +[[ -t 1 ]] && C_BOLD=$(printf '\033[1m') || true + +# ── Output functions ────────────────────────────────────────────── +info() { printf '%s -> %s%s\n' "$C_BLUE" "$*" "$C_RESET"; } +success() { printf '%s ok %s%s\n' "$C_SILVER" "$*" "$C_RESET"; } +warn() { printf '%s !! %s%s\n' "$C_PINK_DK" "$*" "$C_RESET"; } +error() { printf '%s XX %s%s\n' "$C_BOLD$C_PINK_DK" "$*" "$C_RESET"; } + +step() { + STEP_NUM=$((STEP_NUM + 1)) + printf '\n%s%s[%s]%s %s%s%s\n' "$C_BOLD" "$C_SAPPHIRE" "$STEP_NUM" "$C_RESET" "$C_BOLD" "$*" "$C_RESET" + printf '%s---%s------------------------------------------\n' "$C_NAVY" "$C_RESET" +} + +banner() { + printf '\n' + printf '%s _ _ _ _ _ _ \n' "$C_BLUE" + printf '%s / \\ | |__ __ _ _ __ ___| | | | \\ | |\n' "$C_BLUE" + printf '%s / _ \\| `_ \\ / _` | `_ \\ / _ \\ | | | \\| |\n' "$C_BLUE" + printf '%s / ___ \\ | | | (_| | | | | __/ |_| | |\\ |\n' "$C_BLUE" + printf '%s/_/ \\_\\_| |_| |\\__,_|_| |_|\\___/\\___/|_| \\_|\n' "$C_BLUE" + printf '%s Night\n' "$C_PINK" + printf '%s\n' "$C_RESET" + printf '%s Tokyo Night base · Aygea brand accents · v%s%s\n' "$C_DIM" "$VERSION" "$C_RESET" + printf '\n' +} + +# ── Ask yes/no (reads from /dev/tty for curl support) ────────── +ask_yn() { + local prompt="$1" default="${2:-n}" + local choices + [[ "$default" == "y" ]] && choices="[Y/n]" || choices="[y/N]" + while true; do + printf '%s%s %s? %s ' "$C_BOLD" "$C_SAPPHIRE" "$prompt" "$choices" + printf '%s' "$C_RESET" + read -r answer < /dev/tty || answer="" + answer="${answer,,}" + [[ -z "$answer" ]] && answer="$default" + case "$answer" in + y|yes) return 0 ;; + n|no) return 1 ;; + esac + done +} + +# ── Run with or without sudo ──────────────────────────────────── +maybe_sudo() { + if [[ $USE_SUDO -eq 1 ]]; then + sudo "$@" + else + "$@" + fi +} + +# ── Flag parsing ────────────────────────────────────────────────── +usage() { + cat </install.sh | bash # remote install +EOF + exit 0 +} + +parse_flags() { + while [[ $# -gt 0 ]]; do + case "$1" in + --sudo) USE_SUDO=1; shift ;; + --skip-fonts) SKIP_FONTS=1; shift ;; + --skip-tmux) SKIP_TMUX=1; shift ;; + --skip-starship) SKIP_STARSHIP=1; shift ;; + --skip-fetch) SKIP_FETCH=1; shift ;; + --uninstall) UNINSTALL=1; shift ;; + -h|--help) usage ;; + *) warn "Unknown flag: $1"; shift ;; + esac + done +} + +# ── OS detection ────────────────────────────────────────────────── +detect_os() { + case "$OSTYPE" in + darwin*) OS="macos" ;; + *) + if [[ -f /etc/os-release ]]; then + local id + id=$(grep '^ID=' /etc/os-release | head -1 | cut -d= -f2) + case "$id" in + ubuntu|pop|linuxmint|debian) OS="ubuntu" ;; + arch|manjaro|endeavouros|garuda) OS="arch" ;; + *) + local id_like + id_like=$(grep '^ID_LIKE=' /etc/os-release | head -1 | cut -d= -f2 | tr -d '"') + case "$id_like" in + *ubuntu*|*debian*) OS="ubuntu" ;; + *arch*) OS="arch" ;; + *) error "Unsupported OS: $id"; exit 1 ;; + esac + ;; + esac + else + error "Cannot detect OS" + exit 1 + fi + ;; + esac +} + +# ── Resolve source directory (local vs curl pipe) ─────────────── +resolve_source() { + if [[ -n "${BASH_SOURCE[0]:-}" && -f "${BASH_SOURCE[0]}" ]]; then + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + if [[ -f "$SCRIPT_DIR/starship.toml" && -f "$SCRIPT_DIR/tmux.conf" ]]; then + info "Running from local directory" + return 0 + fi + fi + + info "Downloading aygeaNight from GitHub..." + TMPDIR_AYGEA=$(mktemp -d) + local archive_url="${REPO_URL}/archive/refs/heads/main.tar.gz" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$archive_url" | tar xz -C "$TMPDIR_AYGEA" --strip-components=1 + elif command -v wget >/dev/null 2>&1; then + wget -qO- "$archive_url" | tar xz -C "$TMPDIR_AYGEA" --strip-components=1 + else + error "Need curl or wget to download" + exit 1 + fi + + SCRIPT_DIR="$TMPDIR_AYGEA" + success "Downloaded to temp directory" +} + +# ── Helper: rc file path ───────────────────────────────────────── +rc_file() { + if [[ "$OS" == "macos" ]]; then + printf '%s' "$HOME/.zshrc" + else + printf '%s' "$HOME/.bashrc" + fi +} + +# ── Helper: ensure directory and PATH ──────────────────────────── +ensure_local_bin() { + mkdir -p "$LOCAL_BIN" + local rc + rc=$(rc_file) + if ! grep -qF 'export PATH="$HOME/.local/bin:$PATH"' "$rc" 2>/dev/null; then + ensure_rc_line "$rc" 'export PATH="$HOME/.local/bin:$PATH"' top "aygea path" + fi + export PATH="$LOCAL_BIN:$PATH" +} + +# ── Helper: backup a file ──────────────────────────────────────── +backup_file() { + local src="$1" + if [[ -f "$src" ]]; then + local bak="${src}.aygea.bak.$(date +%Y%m%d%H%M%S)" + cp "$src" "$bak" + info "Backed up: $bak" + fi +} + +# ── Helper: idempotent rc line insertion ───────────────────────── +ensure_rc_line() { + local file="$1" line="$2" pos="$3" comment="${4:-}" + [[ -f "$file" ]] || touch "$file" + + if grep -qF "$line" "$file" 2>/dev/null; then + return 0 + fi + + local block="$MARKER_BEGIN" + [[ -n "$comment" ]] && block="$block ($comment)" + block="$block"$'\n'"$line"$'\n'"$MARKER_END" + + if [[ "$pos" == "top" ]]; then + local tmp + tmp=$(mktemp) + printf '%s\n' "$block" > "$tmp" + cat "$file" >> "$tmp" + mv "$tmp" "$file" + else + printf '\n%s\n' "$block" >> "$file" + fi +} + +# ── Helper: remove marked blocks from rc file ──────────────────── +remove_rc_blocks() { + local file="$1" + [[ -f "$file" ]] || return 0 + + local tmp + tmp=$(mktemp) + local in_block=0 + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" == *"$MARKER_BEGIN"* ]]; then + in_block=1 + continue + fi + if [[ "$line" == *"$MARKER_END"* ]]; then + in_block=0 + continue + fi + if [[ $in_block -eq 0 ]]; then + printf '%s\n' "$line" + fi + done < "$file" > "$tmp" + + local tmp2 + tmp2=$(mktemp) + awk 'NF {p=1} p' "$tmp" > "$tmp2" + mv "$tmp2" "$file" + rm -f "$tmp" +} + +# ── Install: Fonts ─────────────────────────────────────────────── +install_fonts() { + local src="$SCRIPT_DIR/fonts/JetBrainsMono" + if [[ ! -d "$src" ]]; then + warn "Font directory not found: $src" + return 1 + fi + + local count + count=$(find "$src" -name '*.ttf' 2>/dev/null | wc -l | tr -d ' ') + if [[ "$count" -eq 0 ]]; then + warn "No .ttf files found in $src" + return 1 + fi + + if [[ "$OS" == "macos" ]]; then + local dest="$HOME/Library/Fonts" + mkdir -p "$dest" + cp "$src"/*.ttf "$dest/" + success "Installed $count font files to $dest" + else + local dest="$HOME/.local/share/fonts/aygea-night" + mkdir -p "$dest" + cp "$src"/*.ttf "$dest/" + if command -v fc-cache >/dev/null 2>&1; then + fc-cache -f "$dest" 2>/dev/null || true + fi + success "Installed $count font files to $dest" + info "Set 'JetBrainsMono Nerd Font' in your terminal emulator settings" + fi +} + +# ── Install: tmux ──────────────────────────────────────────────── +install_tmux() { + if ! command -v tmux >/dev/null 2>&1; then + info "tmux not found, installing..." + case "$OS" in + macos) + if command -v brew >/dev/null 2>&1; then + brew install tmux + else + warn "Homebrew not found. Install tmux manually." + return 1 + fi + ;; + ubuntu) + maybe_sudo apt-get update -qq && maybe_sudo apt-get install -y -qq tmux + ;; + arch) + maybe_sudo pacman -Sy --noconfirm tmux + ;; + esac + else + info "tmux already installed" + fi + + backup_file "$HOME/.tmux.conf" + cp "$SCRIPT_DIR/tmux.conf" "$HOME/.tmux.conf" + success "Deployed ~/.tmux.conf" + + if tmux ls >/dev/null 2>&1; then + info "Run 'tmux kill-server' then start a new session to apply" + fi +} + +# ── Install: Locale (Linux only, needs sudo) ──────────────────── +install_locale() { + [[ "$OS" == "macos" ]] && return 0 + + info "Setting up en_US.UTF-8 locale..." + case "$OS" in + ubuntu) + maybe_sudo apt-get install -y -qq locales 2>/dev/null || true + maybe_sudo locale-gen en_US.UTF-8 2>/dev/null || true + maybe_sudo update-locale LANG=en_US.UTF-8 2>/dev/null || true + ;; + arch) + if [[ -f /etc/locale.gen ]]; then + maybe_sudo sed -i 's/^#en_US\.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen + maybe_sudo locale-gen + maybe_sudo localectl set-locale LANG=en_US.UTF-8 2>/dev/null || true + fi + ;; + esac + success "Locale configured (log out and back in to fully apply)" +} + +# ── Install: Starship prompt ──────────────────────────────────── +install_starship() { + if ! command -v starship >/dev/null 2>&1; then + info "Installing starship..." + case "$OS" in + macos) + if command -v brew >/dev/null 2>&1; then + brew install starship + else + ensure_local_bin + curl -sS https://starship.rs/install.sh | sh -s -- -y -b "$LOCAL_BIN" + fi + ;; + ubuntu|arch) + ensure_local_bin + curl -sS https://starship.rs/install.sh | sh -s -- -y -b "$LOCAL_BIN" + ;; + esac + else + info "starship already installed" + fi + + mkdir -p "$HOME/.config" + backup_file "$HOME/.config/starship.toml" + cp "$SCRIPT_DIR/starship.toml" "$HOME/.config/starship.toml" + success "Deployed ~/.config/starship.toml" + + local rc + rc=$(rc_file) + + if [[ "$OS" == "macos" ]]; then + ensure_rc_line "$rc" 'eval "$(starship init zsh)"' bottom "aygea-night starship" + success "Starship init added to $rc" + else + ensure_rc_line "$rc" 'export LANG=en_US.UTF-8' top "aygea locale" + ensure_rc_line "$rc" 'export LC_ALL=en_US.UTF-8' top "aygea locale" + ensure_rc_line "$rc" 'export TERM=xterm-256color' top "aygea terminal" + ensure_rc_line "$rc" 'export COLORTERM=truecolor' top "aygea terminal" + ensure_rc_line "$rc" 'eval "$(starship init bash)"' bottom "aygea-night starship" + success "Starship init + locale exports added to $rc" + fi +} + +# ── Install: Fetch script ─────────────────────────────────────── +install_fetch() { + ensure_local_bin + local rc + rc=$(rc_file) + + case "$OS" in + macos) + cp "$SCRIPT_DIR/fetch/aygeafetch.zsh" "$HOME/aygeafetch.zsh" + chmod +x "$HOME/aygeafetch.zsh" + ensure_rc_line "$rc" 'source ~/aygeafetch.zsh' bottom "aygea-night fetch" + success "aygeafetch installed to ~/aygeafetch.zsh" + ;; + ubuntu) + cp "$SCRIPT_DIR/fetch/aygeafetch-ubuntu.sh" "$LOCAL_BIN/aygeafetch" + chmod +x "$LOCAL_BIN/aygeafetch" + ensure_rc_line "$rc" 'aygeafetch' bottom "aygea-night fetch" + success "aygeafetch installed to $LOCAL_BIN/aygeafetch" + ;; + arch) + cp "$SCRIPT_DIR/fetch/aygeafetch-arch.sh" "$LOCAL_BIN/aygeafetch" + chmod +x "$LOCAL_BIN/aygeafetch" + ensure_rc_line "$rc" 'aygeafetch' bottom "aygea-night fetch" + success "aygeafetch installed to $LOCAL_BIN/aygeafetch" + ;; + esac +} + +# ── Install: iTerm2 colors (macOS only) ───────────────────────── +install_iterm_colors() { + [[ "$OS" != "macos" ]] && return 0 + + local colorscheme="$SCRIPT_DIR/AygeaNight.itermcolors" + if [[ ! -f "$colorscheme" ]]; then + warn "iTerm2 color scheme not found" + return 1 + fi + + if command -v open >/dev/null 2>&1; then + info "Opening iTerm2 color scheme for import..." + open "$colorscheme" + success "In iTerm2: Preferences -> Profiles -> Colors -> Color Presets -> AygeaNight" + else + info "Double-click AygeaNight.itermcolors to import into iTerm2" + fi +} + +# ── Uninstall ──────────────────────────────────────────────────── +do_uninstall() { + banner + info "Uninstalling AygeaNight..." + + local rc + rc=$(rc_file) + + if [[ -f "$rc" ]]; then + remove_rc_blocks "$rc" + success "Cleaned $rc" + fi + + if [[ -f "$HOME/.tmux.conf" ]]; then + rm -f "$HOME/.tmux.conf" + info "Removed ~/.tmux.conf" + fi + + if [[ -f "$HOME/.config/starship.toml" ]]; then + rm -f "$HOME/.config/starship.toml" + info "Removed ~/.config/starship.toml" + fi + + case "$OS" in + macos) + rm -f "$HOME/aygeafetch.zsh" + info "Removed ~/aygeafetch.zsh" + ;; + *) + rm -f "$LOCAL_BIN/aygeafetch" + info "Removed $LOCAL_BIN/aygeafetch" + ;; + esac + + if [[ "$OS" == "macos" ]]; then + local dest="$HOME/Library/Fonts" + local removed=0 + for f in "$dest"/JetBrainsMonoNerdFont*.ttf "$dest"/JetBrainsMonoNerdFontMono*.ttf \ + "$dest"/JetBrainsMonoNerdFontPropo*.ttf "$dest"/JetBrainsMonoNLNerdFont*.ttf; do + [[ -f "$f" ]] && { rm -f "$f"; removed=$((removed + 1)); } + done + [[ $removed -gt 0 ]] && info "Removed $removed font files" + else + local dest="$HOME/.local/share/fonts/aygea-night" + if [[ -d "$dest" ]]; then + rm -rf "$dest" + command -v fc-cache >/dev/null 2>&1 && fc-cache -f 2>/dev/null || true + info "Removed $dest" + fi + fi + + local bak + for bak in "$HOME/.tmux.conf.aygea.bak."* "$HOME/.config/starship.toml.aygea.bak."*; do + if [[ -f "$bak" ]]; then + local orig="${bak%.aygea.bak.*}" + mv "$bak" "$orig" + success "Restored backup: $orig" + fi + done + + printf '\n' + success "AygeaNight uninstalled. Restart your terminal." +} + +# ── Main ──────────────────────────────────────────────────────── +main() { + parse_flags "$@" + detect_os + resolve_source + + if [[ $UNINSTALL -eq 1 ]]; then + do_uninstall + exit 0 + fi + + banner + info "Detected OS: ${C_BOLD}$OS${C_RESET}" + + # Ask about sudo (Linux only, skip if already set via --sudo flag) + if [[ $USE_SUDO -eq 0 && "$OS" != "macos" ]]; then + if ask_yn "Use sudo for system-wide installs (locale, package manager)?"; then + USE_SUDO=1 + else + info "Installing without sudo (user-level only)" + fi + fi + + printf '\n' + + # Step 1: Locale (Linux only) + if [[ "$OS" != "macos" && $USE_SUDO -eq 1 ]]; then + step "Setting up locale (en_US.UTF-8)" + install_locale + fi + + # Step 2: Fonts + if [[ $SKIP_FONTS -eq 0 ]]; then + step "Installing JetBrainsMono Nerd Font" + install_fonts + fi + + # Step 3: tmux + if [[ $SKIP_TMUX -eq 0 ]]; then + step "Installing tmux config" + install_tmux + fi + + # Step 4: Starship + if [[ $SKIP_STARSHIP -eq 0 ]]; then + step "Installing Starship prompt" + install_starship + fi + + # Step 5: Fetch script + if [[ $SKIP_FETCH -eq 0 ]]; then + step "Installing aygeafetch" + install_fetch + fi + + # Step 6: iTerm2 (macOS only) + if [[ "$OS" == "macos" ]]; then + step "iTerm2 color scheme" + install_iterm_colors + fi + + printf '\n' + success "AygeaNight installation complete!" + printf '\n' + local rc; rc=$(rc_file) + printf ' -> Restart your terminal or run: %s%s%s\n' "$C_BOLD" "source $rc" "$C_RESET" + printf '\n' +} + +main "$@"