Skip to content

Instantly share code, notes, and snippets.

@crsayen
Created March 17, 2026 13:13
Show Gist options
  • Select an option

  • Save crsayen/f5fc53fd1fc8f9a3153580a861b57bab to your computer and use it in GitHub Desktop.

Select an option

Save crsayen/f5fc53fd1fc8f9a3153580a861b57bab to your computer and use it in GitHub Desktop.
#!/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."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment