#!/usr/bin/env bash # ghostty-setup.sh # Replicates Ghostty + zsh configuration (Warp-like experience) # Safe to re-run: checks before installing/overwriting set -e echo "==> Checking for Homebrew..." if ! command -v brew &>/dev/null; then echo "Homebrew not found. Install it first: https://brew.sh" exit 1 fi # ─── Packages ────────────────────────────────────────────────────────────────── echo "==> Installing packages..." brew install --quiet \ ghostty \ fzf \ zsh-autosuggestions \ zsh-syntax-highlighting \ zsh-completions \ pyenv \ starship \ thefuck # Install Iosevka Nerd Font brew install --quiet --cask font-iosevka-nerd-font # Fix insecure zsh-completions directory permissions chmod go-w "$(brew --prefix)/share" chmod -R go-w "$(brew --prefix)/share/zsh" # ─── fzf-tab ─────────────────────────────────────────────────────────────────── echo "==> Installing fzf-tab..." if [[ ! -d ~/.zsh/fzf-tab ]]; then git clone https://github.com/Aloxaf/fzf-tab ~/.zsh/fzf-tab else echo " fzf-tab already installed, skipping." fi # ─── Ghostty config ──────────────────────────────────────────────────────────── echo "==> Writing Ghostty config..." mkdir -p ~/.config/ghostty cat > ~/.config/ghostty/config << 'EOF' # Shell integration - enables prompt markers, title updates, cursor shape changes shell-integration = zsh shell-integration-features = cursor,sudo,title # Option key as Alt - enables word-movement (Option+Left/Right) like a text editor macos-option-as-alt = true # Font - using Iosevka Nerd Font font-family = IosevkaNFM font-size = 14 # Colors — Catppuccin Mocha palette on black background background = #000000 foreground = #ffffff cursor-color = #f5e0dc selection-background = #6d28d9 selection-foreground = #ffffff # ANSI palette (0-7 normal, 8-15 bright) palette = 0=#45475a palette = 1=#f38ba8 palette = 2=#a6e3a1 palette = 3=#f9e2af palette = 4=#89b4fa palette = 5=#cba6f7 palette = 6=#89dceb palette = 7=#cdd6f4 palette = 8=#585b70 palette = 9=#f38ba8 palette = 10=#a6e3a1 palette = 11=#f9e2af palette = 12=#89b4fa palette = 13=#cba6f7 palette = 14=#89dceb palette = 15=#a6adc8 # Hide the folder proxy icon in the title bar macos-titlebar-proxy-icon = hidden # Window padding for readability window-padding-x = 8 window-padding-y = 8 # Clipboard clipboard-read = allow clipboard-write = allow # Keep the window open when the shell exits (useful for seeing errors) quit-after-last-window-closed = true # Cmd+Shift+Left/Right: send escape sequences for "select to line start/end" # zsh binds these to _select-to-bol / _select-to-eol keybind = super+shift+left=text:\x1b[1;6D keybind = super+shift+right=text:\x1b[1;6C EOF # ─── Starship config ─────────────────────────────────────────────────────────── echo "==> Writing Starship config..." mkdir -p ~/.config cat > ~/.config/starship.toml << 'EOF' # Starship prompt — Warp-style layout with Catppuccin Mocha colors # Line 1: runtime · directory · git branch+status · duration # Line 2: ❯ (input) format = """ $nodejs\ $python\ $conda\ $directory\ $git_branch\ $git_status\ $cmd_duration\ $line_break\ $character""" [nodejs] symbol = "" format = "[$version]($style) " style = "green" detect_files = ["package.json", ".nvmrc", ".node-version"] [directory] truncation_length = 4 truncate_to_repo = true style = "blue" format = "[$path]($style) " [git_branch] symbol = "" format = "[git:($branch)]($style) " style = "bold magenta" [git_status] ahead = "↑${count}" behind = "↓${count}" diverged = "↕" conflicted = "!" untracked = "" stashed = "" modified = "" staged = "" renamed = "" deleted = "" format = "[$ahead_behind$conflicted]($style)" style = "yellow" [python] symbol = "" pyenv_version_name = true style = "cyan" format = "[$version]($style) " [conda] symbol = "" style = "cyan" format = "[$environment]($style) " [cmd_duration] min_time = 0 style = "bright-black" format = "[(${duration})]($style) " [character] success_symbol = "[❯](bold white)" error_symbol = "[❯](bold red)" EOF # ─── .zshrc ──────────────────────────────────────────────────────────────────── echo "==> Writing ~/.zshrc..." cat > ~/.zshrc << 'ZSHRC' export PATH="$HOME/.local/bin:$PATH" # ─── pyenv ──────────────────────────────────────────────────────────────────── export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" # ─── Completions ────────────────────────────────────────────────────────────── FPATH="$(brew --prefix)/share/zsh-completions:$(brew --prefix)/share/zsh/site-functions:$FPATH" autoload -Uz compinit if [[ -n ~/.zcompdump(#qN.mh+24) ]]; then compinit else compinit -C fi # Completion styling (set before fzf-tab loads) zstyle ':completion:*' menu no zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' zstyle ':completion:*:descriptions' format '[%d]' # ─── fzf (load before fzf-tab so fzf-tab can wrap the right widget) ─────────── export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border' source "$(brew --prefix)/opt/fzf/shell/completion.zsh" # fzf-tab: replaces Tab completion menu with fzf popup # Must load: after compinit + fzf/completion.zsh, but before autosuggestions source ~/.zsh/fzf-tab/fzf-tab.plugin.zsh zstyle ':fzf-tab:complete:cd:*' fzf-preview 'ls -1 -G $realpath' zstyle ':fzf-tab:complete:*' fzf-flags --height=60% # ─── Autosuggestions & syntax highlighting (after fzf-tab) ──────────────────── source "$(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh" ZSH_AUTOSUGGEST_STRATEGY=(history completion) ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' source "$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" # fzf key bindings: Ctrl+R (history), Ctrl+T (files), Option+C (cd) source "$(brew --prefix)/opt/fzf/shell/key-bindings.zsh" # ─── ls colors ──────────────────────────────────────────────────────────────── export CLICOLOR=1 export LSCOLORS=ExGxFxDxCxegedabagacad # ─── History ────────────────────────────────────────────────────────────────── HISTFILE=~/.zsh_history HISTSIZE=50000 SAVEHIST=50000 setopt SHARE_HISTORY HIST_IGNORE_DUPS HIST_IGNORE_SPACE HIST_REDUCE_BLANKS EXTENDED_HISTORY # ─── Editor-like keybindings (emacs mode) ───────────────────────────────────── bindkey -e zle_highlight=(region:bg=#6d28d9,fg=#ffffff) bindkey '^[[1;3D' backward-word # Option+Left bindkey '^[[1;3C' forward-word # Option+Right bindkey '^[b' backward-word bindkey '^[f' forward-word bindkey '^[^?' backward-kill-word # Option+Backspace bindkey '^[d' kill-word # Option+Delete bindkey '^[[H' beginning-of-line bindkey '^[[F' end-of-line bindkey '^[OH' beginning-of-line bindkey '^[OF' end-of-line bindkey '^[[1;5D' backward-word # Ctrl+Left bindkey '^[[1;5C' forward-word # Ctrl+Right # Keyboard selection _select-backward-word() { (( REGION_ACTIVE )) || zle set-mark-command; zle backward-word; REGION_ACTIVE=1 } _select-forward-word() { (( REGION_ACTIVE )) || zle set-mark-command; zle forward-word; REGION_ACTIVE=1 } _select-to-bol() { (( REGION_ACTIVE )) || zle set-mark-command; zle beginning-of-line; REGION_ACTIVE=1 } _select-to-eol() { (( REGION_ACTIVE )) || zle set-mark-command; zle end-of-line; REGION_ACTIVE=1 } _select-backward-char() { (( REGION_ACTIVE )) || zle set-mark-command; zle backward-char; REGION_ACTIVE=1 } _select-forward-char() { (( REGION_ACTIVE )) || zle set-mark-command; zle forward-char; REGION_ACTIVE=1 } zle -N _select-backward-word zle -N _select-forward-word zle -N _select-to-bol zle -N _select-to-eol zle -N _select-backward-char zle -N _select-forward-char bindkey '^[[1;4D' _select-backward-word # Option+Shift+Left bindkey '^[[1;4C' _select-forward-word # Option+Shift+Right bindkey '^[[1;2D' _select-backward-char # Shift+Left bindkey '^[[1;2C' _select-forward-char # Shift+Right bindkey '^[[1;6D' _select-to-bol # Cmd+Shift+Left bindkey '^[[1;6C' _select-to-eol # Cmd+Shift+Right _delete-or-kill-region() { if (( REGION_ACTIVE )); then zle kill-region else zle delete-char; fi } _backspace-or-kill-region() { if (( REGION_ACTIVE )); then zle kill-region else zle backward-delete-char; fi } zle -N _delete-or-kill-region zle -N _backspace-or-kill-region bindkey '^[[3~' _delete-or-kill-region # Delete key bindkey '^?' _backspace-or-kill-region # Backspace autoload -Uz up-line-or-beginning-search down-line-or-beginning-search zle -N up-line-or-beginning-search zle -N down-line-or-beginning-search bindkey '^[[A' up-line-or-beginning-search bindkey '^[[B' down-line-or-beginning-search bindkey '^[OA' up-line-or-beginning-search bindkey '^[OB' down-line-or-beginning-search autoload -Uz edit-command-line zle -N edit-command-line bindkey '^X^E' edit-command-line # Ctrl+X Ctrl+E: open line in $EDITOR # ─── thefuck ────────────────────────────────────────────────────────────────── eval "$(thefuck --alias)" # ─── Starship prompt ────────────────────────────────────────────────────────── eval "$(starship init zsh)" ZSHRC # Clear stale completion cache rm -f ~/.zcompdump* echo "" echo "Done! Open Ghostty and you're set."