Last active
January 19, 2026 18:26
-
-
Save suobset/bdc2724876c6cd369eb73c7d7588ca7f to your computer and use it in GitHub Desktop.
macOS Rice (stable, opinionated, uses Zellij)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # ╔═══════════════════════════════════════════════════════════════════════════╗ | |
| # ║ macOS TERMINAL SETUP SCRIPT ║ | |
| # ║ ║ | |
| # ║ Idempotent: Safe to re-run. Will overwrite configs with latest version. ║ | |
| # ║ Edit the variables below, then run: ./setup.sh ║ | |
| # ║ ║ | |
| # ║ NOTE: Do NOT run this script with sudo. Homebrew doesn't like it. ║ | |
| # ║ The script will ask for sudo only when absolutely necessary. ║ | |
| # ╚═══════════════════════════════════════════════════════════════════════════╝ | |
| # | |
| # ============================================================================= | |
| # USER CONFIGURATION - EDIT THESE | |
| # ============================================================================= | |
| # Your city for weather. Examples: "Boston", "London", "San Francisco", "Tokyo" | |
| WEATHER_LOCATION="Boston" | |
| # Your preferred browser for opening RSS links | |
| # Options: "Safari", "Firefox", "Google Chrome", "Arc" | |
| RSS_BROWSER="Safari" | |
| # Newsboat reload interval in minutes | |
| RSS_RELOAD_MINUTES=30 | |
| # Weather refresh interval in seconds (1800 = 30 minutes) | |
| WEATHER_REFRESH_SECONDS=1800 | |
| # World clock timezones (TZ database name:Display Label) | |
| # Find yours: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | |
| CLOCK_ZONES=( | |
| "Europe/Zurich:Geneva" | |
| "Asia/Kolkata:New Delhi" | |
| "Asia/Dubai:Dubai" | |
| "America/Chicago:Central" | |
| ) | |
| # Which timezone to show in big ASCII (must match one of the labels above) | |
| CLOCK_PRIMARY="Central" | |
| # Stock ticker symbols (comma-separated) | |
| # Stocks: AAPL, GOOGL, MSFT, AMZN, TSLA | |
| # Crypto: BTC-USD, ETH-USD | |
| # Indices: ^DJI (Dow), ^GSPC (S&P 500), ^IXIC (NASDAQ) | |
| TICKER_SYMBOLS="AAPL,MSFT,GOOGL,BTC-USD,^GSPC" | |
| # Curated commands for cheat.sh random (space-separated) | |
| # These are commands you'll actually use - add/remove as needed | |
| CHEAT_COMMANDS="tar awk sed grep find xargs curl jq git ssh rsync chmod chown ln df du ps kill top lsof cut sort uniq tr tee head tail less vim zip unzip scp wget nc cat echo printf test cd ls cp mv rm mkdir rmdir touch file stat wc diff patch xz gzip bzip2 make gcc ld nm strings hexdump od base64 sha256sum md5sum openssl htop tmux screen cron at jobs bg fg nohup parallel iptables netstat ss ping traceroute dig host nslookup ffmpeg convert identify pdftk youtube-dl yt-dlp brew docker kubectl" | |
| # Your starter RSS feeds (add more as needed, one per line) | |
| # Format: URL "~Display Name" | |
| RSS_FEEDS=' | |
| # Personal | |
| https://skushagra.com/index.xml "~Declarative, by Kush S." | |
| https://s3gfault.dev/index.xml "~s3gfault" | |
| # Aggregators | |
| https://lobste.rs/rss "~Lobsters" | |
| https://news.ycombinator.com/rss "~Hacker News" | |
| # Systems & Low-Level | |
| https://blog.rust-lang.org/feed.xml "~Rust Blog" | |
| https://without.boats/index.xml "~without.boats" | |
| https://fasterthanli.me/index.xml "~fasterthanlime" | |
| https://blog.llvm.org/feed.xml "~LLVM Blog" | |
| https://herbsutter.com/feed/ "~Herb Sutter" | |
| https://www.brendangregg.com/blog/rss.xml "~Brendan Gregg" | |
| https://rachelbythebay.com/w/atom.xml "~rachelbythebay" | |
| https://danluu.com/atom.xml "~Dan Luu" | |
| https://eli.thegreenplace.net/feeds/all.atom.xml "~Eli Bendersky" | |
| # OS & Kernel | |
| https://lwn.net/headlines/rss "~LWN" | |
| https://www.phoronix.com/rss.php "~Phoronix" | |
| # Retro & Hardware | |
| https://www.osnews.com/feed/ "~OS News" | |
| https://hackaday.com/feed/ "~Hackaday" | |
| https://www.ifixit.com/News/feed "~iFixit" | |
| https://lowendmac.com/feed/ "~Low End Mac" | |
| # Software Craftsmanship | |
| https://blog.codinghorror.com/rss/ "~Coding Horror" | |
| https://www.joelonsoftware.com/feed/ "~Joel on Software" | |
| https://martinfowler.com/feed.atom "~Martin Fowler" | |
| https://drewdevault.com/blog/index.xml "~Drew DeVault" | |
| # Apple/Mac Native Dev | |
| https://mjtsai.com/blog/feed/ "~Michael Tsai" | |
| https://inessential.com/feed.xml "~inessential" | |
| https://daringfireball.net/feeds/main "~Daring Fireball" | |
| # Tech Policy & Right-to-Repair | |
| https://pluralistic.net/feed/ "~Pluralistic" | |
| # Research | |
| https://blog.acolyer.org/feed/ "~the morning paper" | |
| ' | |
| # Set to "true" to install optional packages (starship prompt, etc.) | |
| INSTALL_OPTIONAL=false | |
| # ============================================================================= | |
| # END USER CONFIGURATION - Don't edit below unless you know what you're doing | |
| # ============================================================================= | |
| set -e | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' | |
| info() { echo -e "${BLUE}[INFO]${NC} $1"; } | |
| success() { echo -e "${GREEN}[OK]${NC} $1"; } | |
| warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } | |
| error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } | |
| write_file() { echo -e "${CYAN}[WRITE]${NC} $1"; } | |
| echo -e "${GREEN}" | |
| cat << 'EOF' | |
| _____ ______ ______ __ ______ ______ ______ ______ __ __ __ ______ | |
| /\ __-. /\ ___\ /\ ___\ /\ \ /\ __ \ /\ == \ /\ __ \ /\__ _\ /\ \ /\ \ / / /\ ___\ | |
| \ \ \/\ \ \ \ __\ \ \ \____ \ \ \____ \ \ __ \ \ \ __< \ \ __ \ \/_/\ \/ \ \ \ \ \ \'/ \ \ __\ | |
| \ \____- \ \_____\ \ \_____\ \ \_____\ \ \_\ \_\ \ \_\ \_\ \ \_\ \_\ \ \_\ \ \_\ \ \__| \ \_____\ | |
| \/____/ \/_____/ \/_____/ \/_____/ \/_/\/_/ \/_/ /_/ \/_/\/_/ \/_/ \/_/ \/_/ \/_____/ | |
| macOS Term Setup, by Kush S · skushagra.com | |
| EOF | |
| echo -e "${NC}" | |
| echo "Configuration:" | |
| echo " Weather Location: $WEATHER_LOCATION" | |
| echo " RSS Browser: $RSS_BROWSER" | |
| echo " RSS Reload: ${RSS_RELOAD_MINUTES}min" | |
| echo " Weather Refresh: ${WEATHER_REFRESH_SECONDS}s" | |
| echo "" | |
| # Check for macOS | |
| [[ "$OSTYPE" == "darwin"* ]] || error "This script is for macOS only" | |
| # Bail if running as root/sudo - Homebrew hates this | |
| if [[ $EUID -eq 0 ]]; then | |
| error "Don't run this script with sudo. Just run: ./setup.sh" | |
| fi | |
| # ============================================================================= | |
| # HOMEBREW | |
| # ============================================================================= | |
| if ! command -v brew &>/dev/null; then | |
| info "Installing Homebrew..." | |
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| if [[ -f "/opt/homebrew/bin/brew" ]]; then | |
| eval "$(/opt/homebrew/bin/brew shellenv)" | |
| fi | |
| success "Homebrew installed" | |
| else | |
| success "Homebrew already installed" | |
| fi | |
| # ============================================================================= | |
| # PACKAGES | |
| # ============================================================================= | |
| PACKAGES=( | |
| zellij | |
| zsh-autosuggestions | |
| zsh-syntax-highlighting | |
| eza | |
| bat | |
| ripgrep | |
| fd | |
| fzf | |
| btop | |
| newsboat | |
| jq | |
| yazi | |
| vim | |
| figlet | |
| toilet | |
| nnn | |
| ) | |
| if [[ "$INSTALL_OPTIONAL" == "true" ]]; then | |
| PACKAGES+=(starship) | |
| fi | |
| info "Installing packages..." | |
| for pkg in "${PACKAGES[@]}"; do | |
| if brew list "$pkg" &>/dev/null; then | |
| success "$pkg" | |
| else | |
| info "Installing $pkg..." | |
| brew install "$pkg" && success "$pkg" || warn "Failed: $pkg" | |
| fi | |
| done | |
| # ============================================================================= | |
| # DIRECTORIES | |
| # ============================================================================= | |
| info "Creating directories..." | |
| mkdir -p ~/.config/zellij/layouts | |
| mkdir -p ~/.config/newsboat | |
| mkdir -p ~/.local/bin | |
| success "Directories ready" | |
| # ============================================================================= | |
| # VIM + AMIX/VIMRC | |
| # ============================================================================= | |
| if [[ -d ~/.vim_runtime ]]; then | |
| info "amix/vimrc already installed, updating..." | |
| cd ~/.vim_runtime && git pull --rebase 2>/dev/null || warn "Could not update amix/vimrc" | |
| cd - >/dev/null | |
| success "amix/vimrc updated" | |
| else | |
| info "Installing amix/vimrc..." | |
| git clone --depth=1 https://github.com/amix/vimrc.git ~/.vim_runtime | |
| # Run installer without sudo - it's all user-space | |
| bash ~/.vim_runtime/install_awesome_vimrc.sh | |
| success "amix/vimrc installed" | |
| fi | |
| write_file "~/.vim_runtime/my_configs.vim" | |
| cat > ~/.vim_runtime/my_configs.vim << 'VIM_EOF' | |
| " Declarative vim config | |
| " Generated by setup.sh - will be overwritten on re-run | |
| " ----------------------------- | |
| " General Editing Preferences | |
| " ----------------------------- | |
| " Use spaces instead of tabs | |
| set expandtab | |
| " Each tab = 2 spaces | |
| set tabstop=2 | |
| set shiftwidth=2 | |
| set softtabstop=2 | |
| " Enable soft wrapping for long lines | |
| set wrap | |
| set linebreak " Prevent wrapping in the middle of a word | |
| " Enable line numbers | |
| set number | |
| " Enable mouse support in all modes | |
| set mouse=a | |
| " ----------------------------- | |
| " Terminal Settings | |
| " ----------------------------- | |
| " Open new terminal windows at the bottom | |
| set splitbelow | |
| " ----------------------------- | |
| " Optional quality-of-life settings | |
| " ----------------------------- | |
| " Highlight current line | |
| set cursorline | |
| " Show matching brackets | |
| set showmatch | |
| " Use relative numbers (optional, uncomment if you want this) | |
| " set relativenumber | |
| " Use a sensible default for backspace | |
| set backspace=indent,eol,start | |
| VIM_EOF | |
| success "Vim config installed" | |
| # ============================================================================= | |
| # WORLD CLOCK SCRIPT | |
| # ============================================================================= | |
| write_file "~/.local/bin/worldclock" | |
| cat > ~/.local/bin/worldclock << CLOCK_EOF | |
| #!/bin/bash | |
| # Compact World Clock with sunrise/sunset | |
| # Generated by setup.sh - will be overwritten on re-run | |
| ZONES=( | |
| $(for zone in "${CLOCK_ZONES[@]}"; do echo " \"$zone\""; done) | |
| ) | |
| PRIMARY="${CLOCK_PRIMARY}" | |
| LOCATION="${WEATHER_LOCATION}" | |
| # Colors | |
| CYAN='\033[0;36m' | |
| MAGENTA='\033[0;35m' | |
| YELLOW='\033[1;33m' | |
| GREEN='\033[0;32m' | |
| DIM='\033[2m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' | |
| # Cache sunrise/sunset (fetch once per hour) | |
| SUN_CACHE="/tmp/suninfo_cache" | |
| SUN_CACHE_AGE=3600 | |
| get_sun_times() { | |
| if [[ -f "\$SUN_CACHE" ]]; then | |
| cache_mod=\$(stat -f %m "\$SUN_CACHE" 2>/dev/null || echo 0) | |
| now=\$(date +%s) | |
| if (( now - cache_mod < SUN_CACHE_AGE )); then | |
| cat "\$SUN_CACHE" | |
| return | |
| fi | |
| fi | |
| sun_data=\$(curl -s --max-time 3 "wttr.in/\${LOCATION}?format=%S|%s" 2>/dev/null) | |
| if [[ -n "\$sun_data" && "\$sun_data" != *"Unknown"* ]]; then | |
| echo "\$sun_data" > "\$SUN_CACHE" | |
| echo "\$sun_data" | |
| else | |
| echo "—|—" | |
| fi | |
| } | |
| while true; do | |
| clear | |
| echo -e "\${MAGENTA}┌──────────────────┐\${NC}" | |
| echo -e "\${MAGENTA}│\${NC} \${CYAN}\${BOLD}W O R L D T I M E\${NC}\${MAGENTA}│\${NC}" | |
| echo -e "\${MAGENTA}├──────────────────┤\${NC}" | |
| for zone_entry in "\${ZONES[@]}"; do | |
| tz="\${zone_entry%%:*}" | |
| label="\${zone_entry##*:}" | |
| time=\$(TZ="\$tz" date '+%H:%M') | |
| day=\$(TZ="\$tz" date '+%a') | |
| if [[ "\$label" == "\$PRIMARY" ]]; then | |
| printf "\${MAGENTA}│\${NC}\${YELLOW}%-8s\${NC} \${GREEN}%s\${NC} %s\${MAGENTA}│\${NC}\n" "\$label" "\$time" "\$day" | |
| else | |
| printf "\${MAGENTA}│\${NC}\${DIM}%-8s\${NC} %s \${DIM}%s\${NC}\${MAGENTA}│\${NC}\n" "\$label" "\$time" "\$day" | |
| fi | |
| done | |
| echo -e "\${MAGENTA}├──────────────────┤\${NC}" | |
| sun_times=\$(get_sun_times) | |
| sunrise=\${sun_times%%|*} | |
| sunset=\${sun_times##*|} | |
| printf "\${MAGENTA}│\${NC}\${GREEN}↑\${NC}%-6s \${YELLOW}↓\${NC}%-6s\${MAGENTA}│\${NC}\n" "\$sunrise" "\$sunset" | |
| echo -e "\${MAGENTA}└──────────────────┘\${NC}" | |
| sleep 1 | |
| done | |
| CLOCK_EOF | |
| chmod +x ~/.local/bin/worldclock | |
| success "World clock script installed" | |
| # ============================================================================= | |
| # NETWORK INFO SCRIPT | |
| # ============================================================================= | |
| write_file "~/.local/bin/moonphase" | |
| cat > ~/.local/bin/moonphase << 'MOONPHASE_EOF' | |
| #!/bin/bash | |
| # ASCII Moon phase display - landscape layout | |
| # Generated by setup.sh - will be overwritten on re-run | |
| # Colors | |
| CYAN='\033[0;36m' | |
| MAGENTA='\033[0;35m' | |
| YELLOW='\033[1;33m' | |
| WHITE='\033[1;37m' | |
| DIM='\033[2m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' | |
| # Calculate moon phase (0-100 through cycle) | |
| get_moon_day() { | |
| local known_new=946972800 | |
| local now=$(date +%s) | |
| local diff=$((now - known_new)) | |
| local cycle=2551443 | |
| local phase_secs=$((diff % cycle)) | |
| echo $((phase_secs * 100 / cycle)) | |
| } | |
| get_phase_info() { | |
| local day=$1 | |
| if (( day < 2 )); then | |
| echo "New Moon|0" | |
| elif (( day < 12 )); then | |
| echo "Waxing Crescent|$((day * 50 / 12))" | |
| elif (( day < 26 )); then | |
| echo "First Quarter|$((25 + (day - 12) * 25 / 14))" | |
| elif (( day < 37 )); then | |
| echo "Waxing Gibbous|$((50 + (day - 26) * 25 / 11))" | |
| elif (( day < 52 )); then | |
| echo "Full Moon|100" | |
| elif (( day < 62 )); then | |
| echo "Waning Gibbous|$((100 - (day - 52) * 25 / 10))" | |
| elif (( day < 75 )); then | |
| echo "Last Quarter|$((75 - (day - 62) * 25 / 13))" | |
| elif (( day < 88 )); then | |
| echo "Waning Crescent|$((50 - (day - 75) * 25 / 13))" | |
| else | |
| echo "New Moon|0" | |
| fi | |
| } | |
| get_moon_lines() { | |
| local day=$1 | |
| local line=$2 | |
| if (( day < 6 )); then | |
| # New Moon | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .' '. " ;; | |
| 2) echo " / .. \\ " ;; | |
| 3) echo "| .''. |" ;; | |
| 4) echo "| '..' |" ;; | |
| 5) echo " \\ '' / " ;; | |
| 6) echo " '._ _.' " ;; | |
| esac | |
| elif (( day < 19 )); then | |
| # Waxing Crescent | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .' '. " ;; | |
| 2) echo " / _.-|\\ " ;; | |
| 3) echo "| .' ||" ;; | |
| 4) echo "| '. ||" ;; | |
| 5) echo " \\ '-._/ " ;; | |
| 6) echo " '._ _.' " ;; | |
| esac | |
| elif (( day < 31 )); then | |
| # First Quarter | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .' |'. " ;; | |
| 2) echo " / | \\ " ;; | |
| 3) echo "| | |" ;; | |
| 4) echo "| | |" ;; | |
| 5) echo " \\ | / " ;; | |
| 6) echo " '._ _|.' " ;; | |
| esac | |
| elif (( day < 44 )); then | |
| # Waxing Gibbous | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .'###|'. " ;; | |
| 2) echo " / ####| \\ " ;; | |
| 3) echo "| #####| |" ;; | |
| 4) echo "| #####| |" ;; | |
| 5) echo " \\ ####| / " ;; | |
| 6) echo " '.###|.' " ;; | |
| esac | |
| elif (( day < 56 )); then | |
| # Full Moon | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .'####'. " ;; | |
| 2) echo " /########\\ " ;; | |
| 3) echo "|##########|" ;; | |
| 4) echo "|##########|" ;; | |
| 5) echo " \\########/ " ;; | |
| 6) echo " '.####.' " ;; | |
| esac | |
| elif (( day < 69 )); then | |
| # Waning Gibbous | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .'|###'. " ;; | |
| 2) echo " / |#### \\ " ;; | |
| 3) echo "| |#####|" ;; | |
| 4) echo "| |#####|" ;; | |
| 5) echo " \\ |#### / " ;; | |
| 6) echo " '.|###.' " ;; | |
| esac | |
| elif (( day < 81 )); then | |
| # Last Quarter | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .'| '. " ;; | |
| 2) echo " / | \\ " ;; | |
| 3) echo "| | |" ;; | |
| 4) echo "| | |" ;; | |
| 5) echo " \\ | / " ;; | |
| 6) echo " '.|_ _.' " ;; | |
| esac | |
| else | |
| # Waning Crescent | |
| case $line in | |
| 0) echo " _.._ " ;; | |
| 1) echo " .' '. " ;; | |
| 2) echo " /|-._ \\ " ;; | |
| 3) echo "|| '. |" ;; | |
| 4) echo "|| .' |" ;; | |
| 5) echo " \\'-._ / " ;; | |
| 6) echo " '._ _.' " ;; | |
| esac | |
| fi | |
| } | |
| get_next_moons() { | |
| local day=$1 | |
| local days_to_full=$(( (50 - day + 100) % 100 * 2953 / 10000 )) | |
| local days_to_new=$(( (100 - day) % 100 * 2953 / 10000 )) | |
| local full_date=$(date -v+${days_to_full}d '+%b %d') | |
| local new_date=$(date -v+${days_to_new}d '+%b %d') | |
| echo "${full_date}|${new_date}" | |
| } | |
| while true; do | |
| clear | |
| moon_day=$(get_moon_day) | |
| phase_info=$(get_phase_info $moon_day) | |
| phase_name="${phase_info%%|*}" | |
| illumination="${phase_info##*|}" | |
| next_moons=$(get_next_moons $moon_day) | |
| next_full="${next_moons%%|*}" | |
| next_new="${next_moons##*|}" | |
| echo -e "${MAGENTA}┌────────────────────────────────────────┐${NC}" | |
| echo -e "${MAGENTA}│${NC} ${CYAN}${BOLD}M O O N${NC} ${MAGENTA}│${NC}" | |
| echo -e "${MAGENTA}├────────────────────────────────────────┤${NC}" | |
| # Line 0: moon art | empty | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 0)" | |
| # Line 1: moon art | phase name | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${WHITE}%-19s${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 1)" "$phase_name" | |
| # Line 2: moon art | illumination | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${DIM}%3d%% illuminated${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 2)" "$illumination" | |
| # Line 3: moon art | empty | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 3)" | |
| # Line 4: moon art | next full | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${WHITE}●${NC} Full: ${DIM}%-10s${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 4)" "$next_full" | |
| # Line 5: moon art | next new | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${DIM}○${NC} New: ${DIM}%-10s${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 5)" "$next_new" | |
| # Line 6: moon art | empty | |
| printf "${MAGENTA}│${NC} ${YELLOW}%-14s${NC}${MAGENTA}│${NC} ${MAGENTA}│${NC}\n" "$(get_moon_lines $moon_day 6)" | |
| echo -e "${MAGENTA}└────────────────────────────────────────┘${NC}" | |
| sleep 3600 | |
| done | |
| MOONPHASE_EOF | |
| chmod +x ~/.local/bin/moonphase | |
| success "Moon phase script installed" | |
| # ============================================================================= | |
| # CHEAT.SH SCRIPT | |
| # ============================================================================= | |
| write_file "~/.local/bin/cheatsh" | |
| cat > ~/.local/bin/cheatsh << 'CHEATSH_EOF' | |
| #!/bin/bash | |
| # Interactive cheat.sh browser | |
| # Generated by setup.sh - will be overwritten on re-run | |
| # | |
| # Controls: | |
| # r or Enter or Space - random command from curated list | |
| # / - search for a specific command | |
| # q - quit | |
| COMMANDS=(CHEAT_COMMANDS_PLACEHOLDER) | |
| # Colors | |
| CYAN='\033[0;36m' | |
| MAGENTA='\033[0;35m' | |
| YELLOW='\033[1;33m' | |
| GREEN='\033[0;32m' | |
| DIM='\033[2m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' | |
| get_random_cmd() { | |
| echo "${COMMANDS[$RANDOM % ${#COMMANDS[@]}]}" | |
| } | |
| fetch_cheat() { | |
| local cmd="$1" | |
| curl -s "cheat.sh/${cmd}?style=rrt" 2>/dev/null | |
| } | |
| show_header() { | |
| local cmd="$1" | |
| echo -e "${MAGENTA}┌─────────────────────────────────────────────────────────────┐${NC}" | |
| printf "${MAGENTA}│${NC} ${CYAN}${BOLD}CHEAT.SH${NC} %-35s ${DIM}[r]andom [/]search${NC} ${MAGENTA}│${NC}\n" "" | |
| echo -e "${MAGENTA}├─────────────────────────────────────────────────────────────┤${NC}" | |
| echo -e "${MAGENTA}│${NC} ${YELLOW}Command:${NC} ${GREEN}${BOLD}${cmd}${NC}" | |
| echo -e "${MAGENTA}├─────────────────────────────────────────────────────────────┤${NC}" | |
| } | |
| show_footer() { | |
| echo "" | |
| echo -e "${MAGENTA}└─────────────────────────────────────────────────────────────┘${NC}" | |
| echo -e "${DIM} r/Enter/Space: random │ /: search │ q: quit${NC}" | |
| } | |
| display_cheat() { | |
| local cmd="$1" | |
| clear | |
| show_header "$cmd" | |
| fetch_cheat "$cmd" | head -40 | |
| show_footer | |
| } | |
| search_prompt() { | |
| echo -ne "${YELLOW}Search command: ${NC}" | |
| read -r search_cmd | |
| if [[ -n "$search_cmd" ]]; then | |
| display_cheat "$search_cmd" | |
| fi | |
| } | |
| # Initial display | |
| current_cmd=$(get_random_cmd) | |
| display_cheat "$current_cmd" | |
| # Main loop | |
| while true; do | |
| read -rsn1 key | |
| case "$key" in | |
| r|""|" ") | |
| current_cmd=$(get_random_cmd) | |
| display_cheat "$current_cmd" | |
| ;; | |
| /) | |
| search_prompt | |
| ;; | |
| q) | |
| clear | |
| exit 0 | |
| ;; | |
| *) | |
| # Any other key also gets random | |
| current_cmd=$(get_random_cmd) | |
| display_cheat "$current_cmd" | |
| ;; | |
| esac | |
| done | |
| CHEATSH_EOF | |
| # Replace placeholder with actual commands | |
| sed -i '' "s/CHEAT_COMMANDS_PLACEHOLDER/${CHEAT_COMMANDS}/" ~/.local/bin/cheatsh | |
| chmod +x ~/.local/bin/cheatsh | |
| success "Cheat.sh script installed" | |
| # ============================================================================= | |
| # WEATHER SCRIPT | |
| # ============================================================================= | |
| write_file "~/.local/bin/weather" | |
| cat > ~/.local/bin/weather << 'WEATHER_EOF' | |
| #!/bin/bash | |
| # Weather CLI with graph toggle | |
| # Generated by setup.sh - will be overwritten on re-run | |
| # | |
| # Controls: | |
| # g - toggle graph view | |
| # r - refresh | |
| # q - quit | |
| LOCATION="${1:-${WEATHER_LOCATION:-Boston}}" | |
| SHOW_GRAPH=false | |
| # Colors | |
| CYAN='\033[0;36m' | |
| MAGENTA='\033[0;35m' | |
| YELLOW='\033[1;33m' | |
| DIM='\033[2m' | |
| NC='\033[0m' | |
| fetch_weather() { | |
| if $SHOW_GRAPH; then | |
| curl -s --max-time 10 "wttr.in/${LOCATION}?format=v2" 2>/dev/null | |
| else | |
| curl -s --max-time 10 "wttr.in/${LOCATION}" 2>/dev/null | |
| fi | |
| } | |
| fetch_fallback() { | |
| local loc=$(echo "$LOCATION" | tr '[:upper:]' '[:lower:]' | tr -d ' ') | |
| finger "${loc}@graph.no" 2>/dev/null | |
| } | |
| display() { | |
| clear | |
| output=$(fetch_weather) | |
| if [[ -n "$output" ]]; then | |
| echo "$output" | |
| echo "" | |
| if $SHOW_GRAPH; then | |
| echo -e "${DIM}[g] standard [r] refresh [q] quit${NC}" | |
| else | |
| echo -e "${DIM}[g] graph [r] refresh [q] quit${NC}" | |
| fi | |
| else | |
| echo -e "${YELLOW}wttr.in unavailable, using fallback...${NC}" | |
| echo "" | |
| fetch_fallback | |
| echo "" | |
| echo -e "${DIM}[r] refresh [q] quit${NC}" | |
| fi | |
| } | |
| # Initial display | |
| display | |
| # Main loop | |
| while true; do | |
| read -rsn1 -t 300 key # Auto-refresh every 5 min if no input | |
| case "$key" in | |
| g) | |
| if $SHOW_GRAPH; then | |
| SHOW_GRAPH=false | |
| else | |
| SHOW_GRAPH=true | |
| fi | |
| display | |
| ;; | |
| r|"") | |
| display | |
| ;; | |
| q) | |
| clear | |
| exit 0 | |
| ;; | |
| esac | |
| done | |
| WEATHER_EOF | |
| chmod +x ~/.local/bin/weather | |
| success "Weather script installed" | |
| # ============================================================================= | |
| # ZELLIJ LAYOUT | |
| # ============================================================================= | |
| write_file "~/.config/zellij/layouts/default.kdl" | |
| cat > ~/.config/zellij/layouts/default.kdl << ZELLIJ_EOF | |
| // Terminal Rice Dashboard Layout | |
| // Generated by setup.sh - will be overwritten on re-run | |
| layout { | |
| tab name="dashboard" focus=true { | |
| pane split_direction="vertical" { | |
| pane split_direction="horizontal" size="40%" { | |
| pane name="cheat" size="30%" { | |
| command "bash" | |
| args "-c" "~/.local/bin/cheatsh" | |
| } | |
| pane name="system" { | |
| command "btop" | |
| } | |
| } | |
| pane split_direction="horizontal" size="60%" { | |
| pane name="feeds" size="50%" { | |
| command "newsboat" | |
| } | |
| pane split_direction="vertical" { | |
| pane split_direction="horizontal" size="20%" { | |
| pane name="clock" { | |
| command "bash" | |
| args "-c" "~/.local/bin/worldclock" | |
| } | |
| pane name="moon" { | |
| command "bash" | |
| args "-c" "~/.local/bin/moonphase" | |
| } | |
| } | |
| pane name="weather" { | |
| command "bash" | |
| args "-c" "~/.local/bin/weather" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| tab name="work" { | |
| pane | |
| } | |
| new_tab_template { | |
| pane | |
| pane size=1 borderless=true { | |
| plugin location="status-bar" | |
| } | |
| } | |
| default_tab_template { | |
| children | |
| pane size=1 borderless=true { | |
| plugin location="status-bar" | |
| } | |
| } | |
| } | |
| ZELLIJ_EOF | |
| success "Zellij layout installed" | |
| # ============================================================================= | |
| # ZELLIJ CONFIG | |
| # ============================================================================= | |
| write_file "~/.config/zellij/config.kdl" | |
| cat > ~/.config/zellij/config.kdl << 'ZELLIJ_CONF_EOF' | |
| // Zellij config | |
| // Generated by setup.sh - will be overwritten on re-run | |
| default_layout "default" | |
| // Uncomment these once you're comfortable: | |
| // simplified_ui true | |
| // pane_frames false | |
| // Mouse support | |
| mouse_mode true | |
| // Copy on select | |
| copy_on_select true | |
| // Synthwave theme | |
| theme "synthwave" | |
| themes { | |
| synthwave { | |
| fg "#f0e8e8" | |
| bg "#1a1a2e" | |
| black "#16213e" | |
| red "#ff2e63" | |
| green "#08f7fe" | |
| yellow "#f5d300" | |
| blue "#7b2cbf" | |
| magenta "#e94560" | |
| cyan "#00fff5" | |
| white "#f0e8e8" | |
| orange "#ff6b35" | |
| } | |
| } | |
| ZELLIJ_CONF_EOF | |
| success "Zellij config installed" | |
| # ============================================================================= | |
| # NEWSBOAT CONFIG | |
| # ============================================================================= | |
| write_file "~/.config/newsboat/config" | |
| cat > ~/.config/newsboat/config << NEWSBOAT_EOF | |
| # Newsboat config | |
| # Generated by setup.sh - will be overwritten on re-run | |
| auto-reload yes | |
| reload-time ${RSS_RELOAD_MINUTES} | |
| reload-threads 4 | |
| # Vim keybinds | |
| bind-key j down | |
| bind-key k up | |
| bind-key J next-feed articlelist | |
| bind-key K prev-feed articlelist | |
| bind-key G end | |
| bind-key g home | |
| bind-key l open | |
| bind-key h quit | |
| # Appearance | |
| show-read-feeds no | |
| feed-sort-order unreadarticlecount-asc | |
| # Browser | |
| browser "open -a '${RSS_BROWSER}' %u" | |
| NEWSBOAT_EOF | |
| success "Newsboat config installed" | |
| write_file "~/.config/newsboat/urls" | |
| cat > ~/.config/newsboat/urls << URLS_EOF | |
| # RSS Feeds | |
| # Generated by setup.sh - will be overwritten on re-run | |
| # Add your own feeds below or edit RSS_FEEDS in setup.sh | |
| ${RSS_FEEDS} | |
| URLS_EOF | |
| success "Newsboat feeds installed" | |
| # ============================================================================= | |
| # SHELL CONFIGURATION | |
| # ============================================================================= | |
| info "Updating .zshrc..." | |
| # Remove old managed block if it exists | |
| if grep -q "# >>> terminal-rice managed >>>" ~/.zshrc 2>/dev/null; then | |
| sed -i '' '/# >>> terminal-rice managed >>>/,/# <<< terminal-rice managed <<</d' ~/.zshrc | |
| success "Removed old config block" | |
| fi | |
| # Add new managed block | |
| cat >> ~/.zshrc << ZSHRC_EOF | |
| # >>> terminal-rice managed >>> | |
| # This block is managed by setup.sh - edits will be overwritten on re-run | |
| # To customize, edit setup.sh and re-run it | |
| # Local scripts | |
| export PATH="\$HOME/.local/bin:\$PATH" | |
| # Weather location | |
| export WEATHER_LOCATION="${WEATHER_LOCATION}" | |
| # Homebrew zsh plugins | |
| source \$(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh | |
| source \$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh | |
| # Modern CLI aliases | |
| alias ls='eza --icons' | |
| alias ll='eza -l --icons' | |
| alias la='eza -la --icons' | |
| alias lt='eza --tree --icons' | |
| alias cat='bat' | |
| alias grep='rg' | |
| alias find='fd' | |
| alias v='vim' | |
| # Zellij shortcuts | |
| alias zj='zellij' | |
| alias zja='zellij attach' | |
| alias zjl='zellij list-sessions' | |
| alias zjk='zellij kill-session' | |
| # Quick access | |
| alias feeds='newsboat' | |
| alias weather='~/.local/bin/weather' | |
| alias clock='~/.local/bin/worldclock' | |
| alias moon='~/.local/bin/moonphase' | |
| alias cheat='~/.local/bin/cheatsh' | |
| # nnn file manager with cd on quit | |
| # Press q to quit and cd to last directory | |
| export NNN_TMPFILE="\${XDG_CONFIG_HOME:-\$HOME/.config}/nnn/.lastd" | |
| export NNN_OPTS="c" | |
| n() { | |
| nnn "\$@" | |
| if [ -f "\$NNN_TMPFILE" ]; then | |
| . "\$NNN_TMPFILE" | |
| rm -f "\$NNN_TMPFILE" > /dev/null | |
| fi | |
| } | |
| # <<< terminal-rice managed <<< | |
| ZSHRC_EOF | |
| success "Shell config updated" | |
| # ============================================================================= | |
| # DONE | |
| # ============================================================================= | |
| echo "" | |
| echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}" | |
| echo -e "${GREEN}Setup complete!${NC}" | |
| echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}" | |
| echo "" | |
| echo "Installed/updated:" | |
| echo " ~/.local/bin/weather - Weather with graph toggle [g/r/q]" | |
| echo " ~/.local/bin/worldclock - World times + sunrise/sunset" | |
| echo " ~/.local/bin/moonphase - Moon phase display" | |
| echo " ~/.local/bin/cheatsh - Interactive cheat.sh browser" | |
| echo " ~/.config/zellij/config.kdl - Zellij configuration" | |
| echo " ~/.config/zellij/layouts/default.kdl - Dashboard layout" | |
| echo " ~/.config/newsboat/config - Newsboat with vim keybinds" | |
| echo " ~/.config/newsboat/urls - Your RSS feeds" | |
| echo " ~/.vim_runtime/my_configs.vim - Vim configuration" | |
| echo " ~/.zshrc - Shell aliases and config" | |
| echo "" | |
| echo "To apply changes now:" | |
| echo -e " ${CYAN}source ~/.zshrc${NC}" | |
| echo "" | |
| echo "To start your terminal rice:" | |
| echo -e " ${CYAN}zellij${NC}" | |
| echo "" | |
| echo "To change settings later, edit the variables at the top of this" | |
| echo "script and re-run it. Your todo.md will be preserved." | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment