Skip to content

Instantly share code, notes, and snippets.

@suobset
Last active January 19, 2026 18:26
Show Gist options
  • Select an option

  • Save suobset/bdc2724876c6cd369eb73c7d7588ca7f to your computer and use it in GitHub Desktop.

Select an option

Save suobset/bdc2724876c6cd369eb73c7d7588ca7f to your computer and use it in GitHub Desktop.
macOS Rice (stable, opinionated, uses Zellij)
#!/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