A personal collection of dotfiles for macOS and Linux, designed for a consistent and productive development environment. This setup is modular, managed by a simple setup script, and consistently themed with Catppuccin Mocha.
| Category | Tools |
|---|---|
| Window Manager | Sway, Hyprland, Niri |
| Status Bar | Waybar |
| Terminal | Alacritty, Kitty, Ghostty |
| Shell | Zsh (with Oh My Zsh, Atuin, Zoxide) |
| Editor | Neovim |
| Multiplexer | Zellij, Tmux |
| Git | delta, git-cliff, github-cli |
| Launcher | Rofi (+ rofi-calc) |
| Notifications | Dunst |
| Screenshots | grim, slurp, satty |
| File Manager | lf |
| Utilities | bat, eza, fzf, ripgrep, jq, tldr, mise |
| TUI | bluetui (Bluetooth) |
| Audio | PipeWire, WirePlumber |
-
Core:
git: For cloning and managing the repository.zsh& oh-my-zsh: The primary shell environment.- zsh-syntax-highlighting
- zsh-autosuggestions
- zsh-defer: Deferred execution for faster shell startup.
- Nerd Fonts: Required for icons (this config uses FiraCode Nerd Font).
-
Terminal Tools:
- atuin: Enhanced shell history (installed via its own installer).
- bat: A
catclone with syntax highlighting. - eza: A modern replacement for
ls. - fzf: A command-line fuzzy finder.
- delta: A viewer for git diffs.
- neovim: The primary text editor.
- ripgrep: A fast line-oriented search tool.
- zoxide: A smarter
cdcommand. - jq: A command-line JSON parser.
- mise: Dev tools/runtimes manager (installed via its own installer).
- tldr: Simplified man pages.
- tree-sitter-cli: Required for Neovim.
-
GUI (Linux/Wayland) — install only what you need for your chosen WM:
- Sway:
sway,swaylock,swaybg,swayidle,xdg-desktop-portal-wlr - Hyprland:
hyprland,hyprpaper,hypridle,hyprlock - Niri:
niri,xwayland-satellite,swaybg - Common:
waybar,rofi,rofi-calc,dunst,libnotify - Screenshots:
grim,slurp,satty(annotation editor),wf-recorder(screen recording) - Clipboard:
wl-clipboard - Audio:
pipewire,wireplumber - Bluetooth:
bluez,bluez-utils,bluetui(TUI manager)
- Sway:
A full package list is maintained in packages/:
packages/official_packages.txt— pacman packagespackages/aur_packages.txt— AUR packages
Below are the packages relevant to these dotfiles.
# Shell & core
sudo pacman -S zsh git
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
git clone https://github.com/romkatv/zsh-defer ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-defer
# Terminal tools
sudo pacman -S bat eza fzf git-delta ghostty jq neovim ripgrep tldr zoxide
cargo install --locked tree-sitter-cli
# Installed via their own installers (not pacman)
curl https://mise.run | sh
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh
# Wayland common
sudo pacman -S waybar rofi rofi-calc dunst libnotify grim slurp satty wl-clipboard wf-recorder
# Sway
sudo pacman -S sway swaylock swaybg swayidle xdg-desktop-portal-wlr
# Hyprland (install instead of / in addition to Sway)
sudo pacman -S hyprland hyprpaper hypridle hyprlock
# Niri (install instead of / in addition to Sway)
sudo pacman -S niri xwayland-satellite swaybg
# Audio & Bluetooth
sudo pacman -S pipewire wireplumber bluez bluez-utils bluetui-
Clone the repository and its submodules:
git clone --recurse-submodules git@github.com:cjvnjde/dotfiles.git $HOME/dotfiles cd $HOME/dotfiles
-
Choose which modules to enable by creating a
.modulesfile:cp .modules.example .modules
Edit
.modulesto keep only the modules you need. Lines starting with!disable a previously enabled module (useful in.modules.local). -
Set the Waybar target (only needed if you use Waybar):
echo "sway" > waybar/target # or hyprland, niri
-
Run the setup script:
bash setup.sh
The setup is fully module-based. The root setup.sh does three things:
- Reads
.modules(and.modules.local) to determine which modules are enabled. - Discovers all module setup scripts automatically.
- Calls each module's
setup.shwithenableordisable.
Each module owns its own setup logic — the root script has no hardcoded paths or destinations.
Directory modules — most modules are entire config directories (e.g. nvim, sway, alacritty). They contain a setup.sh that symlinks the directory to the right place:
alacritty/setup.sh → symlinks alacritty/ to ~/.config/alacritty
sway/setup.sh → symlinks sway/ to ~/.config/sway
nvim/setup.sh → symlinks nvim/ to ~/.config/nvim
Single-file modules — configs that are just one file live in home/ and have their setup scripts in home/setup/:
home/setup/zshrc.sh → symlinks home/.zshrc to ~/.zshrc
home/setup/git.sh → symlinks home/.gitconfig to ~/.gitconfig
home/setup/wezterm.sh → symlinks home/.wezterm.lua to ~/.wezterm.lua
Custom modules — some modules need more than a single symlink and have custom logic:
scripts/setup.sh— creates~/.local/scripts/as a real directory and symlinks individual scripts into it (includingccodeandclipboard-codealiases).waybar/setup.sh— symlinks shared files (style.css,scripts/) plus the active config based onwaybar/target.
| Module | Destination | Notes |
|---|---|---|
nvim |
~/.config/nvim |
|
alacritty |
~/.config/alacritty |
|
kitty |
~/.config/kitty |
|
ghostty |
~/.config/ghostty |
|
zshrc |
~/.zshrc |
Sources ~/.zshrc_local if it exists |
git |
~/.gitconfig |
|
ideavim |
~/.ideavimrc |
|
wezterm |
~/.wezterm.lua |
|
opencode |
~/.config/opencode/opencode.json |
|
scripts |
~/.local/scripts/* |
Individual script symlinks |
atuin |
~/.config/atuin |
|
bat |
~/.config/bat |
|
zellij |
~/.config/zellij |
|
tmux |
~/.config/tmux |
|
sway |
~/.config/sway |
See sway/README.md |
hyprland |
~/.config/hypr |
|
niri |
~/.config/niri |
|
rofi |
~/.config/rofi |
|
waybar |
~/.config/waybar |
Uses waybar/target to pick config |
dunst |
~/.config/dunst |
Waybar has per-environment configs (sway/, hyprland/, niri/) that are all symlinked into ~/.config/waybar/. The active one is selected via a config.jsonc symlink based on the waybar/target file.
Switch the active config:
echo "hyprland" > waybar/target # switch to hyprland config
bash setup.sh # re-run to applySupported targets: sway, hyprland, niri.
For machine-specific overrides, create waybar/target.local instead — it takes priority and is git-ignored.
Several config files support machine-specific overrides that are git-ignored:
| File | Purpose |
|---|---|
.modules.local |
Override enabled modules (supports !module to disable) |
waybar/target.local |
Override the active Waybar environment |
~/.zshrc_local |
Private aliases, env vars, machine-specific shell config |
Example .modules.local — enable everything from .modules but drop sway and add hyprland:
!sway
hyprland
Directory module (e.g. ~/.config/foo):
- Add the config directory:
foo/ - Create
foo/setup.shfollowing any existing module as a template (e.g.alacritty/setup.sh). - Add
footo.modules.
Single-file module (e.g. ~/.some_rc):
- Add the file to
home/(e.g.home/.some_rc). - Create
home/setup/some_rc.shfollowinghome/setup/git.shas a template. - Add
some_rcto.modules.
The root setup.sh discovers modules automatically — no registration step needed.
-
Re-run after changes:
bash ~/dotfiles/setup.shThe script is idempotent — running it again with no changes produces no output beyond info logs.
-
Update from remote:
cd ~/dotfiles && git pull --recurse-submodules bash setup.sh
-
Conflicts: If a destination already exists (and isn't the expected symlink), the setup backs it up to
<path>.bakbefore linking. -
Submodules: Most modules are git submodules. Changes to module setup scripts need to be committed inside the submodule first, then the parent repo pointer updated.