macarchy
The tiling desktop from Linux, on the MacBook you already own. One command restyles the terminal, editor, status bar, window borders and wallpaper. Windows tile over native Spaces. Say what you want out loud and it happens. System Integrity Protection stays on.
$ theme-set retro-82
This mock, this page and your desktop read the same colors.sh. Press /, type theme-set, and watch them repaint together.
What macarchy is, and what it is not
macarchy is one Mac desktop config, published as is. It themes the apps in that setup and no others: Ghostty, Zellij, Neovim, SketchyBar, JankyBorders and the wallpaper. theme-set <name> repoints one symlink and pokes each of them to re-read its config, so they repaint together. yabai tiles windows inside each Space, skhd binds the keys, and alt+/ pops up a searchable list of every binding.
If your terminal is Kitty or your editor is VS Code, nothing here restyles it today. A theme directory has no file for those apps, so a switch leaves them alone. The mechanism is Omarchy’s and could drive more apps; that is the plan below. Your Ghostty, Zellij and Neovim configs stay yours, since macarchy hooks in with one line each.
- Theming
- 23 themes, 5 of them light.
theme-set,theme-pick,theme-next,theme-bg,theme-port,theme-new. docs/theme-system.md - Windows
- yabai + skhd + SketchyBar + JankyBorders over native Spaces. No scripting addition, no
csrutil. docs/window-management.md - Hotkey discovery
macarchy-keys: a which-key style popup on alt+/. Descriptions come from the# commentsin skhdrc.- Raycast
- A generated “Set Theme” script command.
theme-raycast-syncregenerates it. - Workspace agent
ws: plain English or voice control of windows, Spaces, theme and Zellij tabs, on device. Talk to your desktop.- Planned
- Theme files for other terminals (Alacritty, Kitty, WezTerm, iTerm2), editors (VS Code, Zed) and browsers, a pick-your-apps install, and light themes without hand fix-ups. None of it exists yet; the README keeps the list.
- Requirements
- macOS with Homebrew. Scripts run on the bash 3.2 that ships with the system.
- Licence
- MIT.
Install macarchy
Two routes, depending on how you already manage dotfiles. Either way, one step stays manual: macOS makes you grant Accessibility permission by hand.
Full READMEClone and run the installer
install.sh bootstraps Homebrew, installs the packages, sets a handful of macOS defaults, links home/ into $HOME (with stow if it is on PATH, otherwise plain symlinks), seeds the default theme, generates wallpapers and writes ~/.config/macarchy/config from the example. It is idempotent, so re-running is safe.
$ git clone https://github.com/jlargs64/macarchy ~/Projects/macarchy
$ cd ~/Projects/macarchy
$ ./install.sh --dry-run # prints every action, changes nothing
$ ./install.sh
Only want the files linked, no packages or defaults? stow -t "$HOME" home.
Or let an agent walk you through it
$ curl -fsSL https://raw.githubusercontent.com/jlargs64/macarchy/main/install-agent.sh \
| sh -s -- claude # or codex, pi, opencode, gemini, copilot, cursor, amp
Launches that agent CLI interactively, seeded with AGENT-INSTALL.md, which walks through the same steps. It still asks before it runs anything. No agent installed? install-agent.sh --print dumps the prompt to paste into any chat agent instead.
Or pull it into chezmoi
If your dotfiles live in chezmoi, add macarchy as a tagged archive external rather than a submodule. One entry per directory, eight in total; stripComponents = 2 strips macarchy-0.1.0/home/ off every path.
[".config/theme"]
type = "archive"
url = "https://github.com/jlargs64/macarchy/archive/refs/tags/v0.1.0.tar.gz"
exact = true
stripComponents = 2
include = [".config/theme/**"]
chezmoi only writes files. After the first chezmoi apply, run ~/.config/theme/bin/theme-maintain once by hand. Homebrew packages and defaults are yours to run too, or copy those blocks out of install.sh.
Pick a theme, change everything
Every app reads its colours from ~/.config/theme/current, a symlink. Switching repoints it and nudges each app; no app knows a theme’s name. 23 ship, 5 of them light. Pick one here and this page repaints the same way.
shift+alt+/ opens theme-pick, an fzf picker with a swatch strip per theme and a picture of its background above the palette. alt+b opens theme-bg-pick, the current theme’s backgrounds with a picture of each, Omarchy-style; Enter applies it. The pictures are drawn by theme-img with chafa over the Kitty graphics protocol in Ghostty (kitty and WezTerm too), iTerm2’s own protocol in iTerm2, and unicode blocks anywhere else. shift+alt+t cycles themes and shift+alt+b cycles the wallpaper within one; the choice is remembered per theme.
The wallpaper follows the theme on every Space. macOS stores it per Space, so every Space points at one fixed file and a switch rewrites the file. A new Space enrolls itself the first time you land on it: yabai runs theme-wallpaper-enroll --here on each Space switch, which points only the desktops on screen at the file and does nothing when they already are. Without yabai, run theme-wallpaper-enroll once by hand instead. theme-port <name> brings in anything else from Omarchy’s catalogue. Light themes need --allow-light and a look afterwards: the SketchyBar and border colour maths were written for dark backgrounds.
Drive the windows
alt focuses and queries. shift+alt moves and mutates. alt+/ lists every binding when you forget one. alt+letter is left free for your app launchers.
Focus, move, resize
- alt - h / j / k / l
- focus window west / south / north / east
- shift + alt - h / j / k / l
- warp window in that direction
- ctrl + alt - h / l
- shrink / grow horizontally
- ctrl + alt - j / k
- grow / shrink vertically
- alt + drag
- left-drag moves, right-drag resizes
Everything else
- shift + alt - t
- cycle the desktop theme
- shift + alt - b
- cycle the wallpaper within the current theme
- shift + alt - /
- pop up theme-pick, an fzf theme picker with swatches and a picture of each background
- alt - b
- pop up theme-bg-pick, the current theme's backgrounds with pictures
- shift + alt - r
- macarchy-restart: yabai, then borders, then the bar, then skhd, in that order
- alt - /
- pop up macarchy-keys
- ctrl - number
- switch Space (native macOS, enable it under Keyboard Shortcuts › Mission Control)
Layout
- alt - =
- balance the tree
- alt - s
- toggle the Space between bsp (tiled) and stack
- alt - [ / alt - ]
- focus previous / next window in the stack
- shift + alt - s
- next new window stacks onto this one
- shift + alt - e / d
- next new window splits right / below
- alt - r
- rotate layout 90°
- alt - g
- toggle float, centred on a 4×4 grid
- alt - f
- zoom to fullscreen within the tile tree
- alt - c
- center the window in a column with gutters; again to put it back
- shift + alt - f
- native macOS fullscreen
- alt - v
- flip split orientation
Talk to your desktop
Say what you want done to the desktop in plain words, and a 14 MB on-device model turns it into yabai, Zellij and theme commands. Nothing leaves the machine.
Dry run is the default. ws prints what it would do until MACARCHY_AGENT_EXECUTE=true is set in ~/.config/macarchy/config. The pipeline, the tool list and how to add a tool are in docs/agent.md.
# prints what it would run
$ ws "put slack on space 2 and switch to the kanagawa theme"
# runs it
$ ws -x "focus helium"
# alt - w: press to listen, press again to run
$ ws --voice
By voice
- alt - w
- press once to start listening, again to stop. Handy transcribes offline and
wsprints or runs the result - alt - space
- Handy’s own dictation hotkey for typing anywhere: hold to talk, tap to toggle. Set inside Handy, not skhd
One symlink, every app
Each app points at current/<its file> forever. Only the symlink moves. That is the whole trick, and it is why nothing needs a restart.
Hook in your own configs
- Ghostty
config-file = ?~/.config/theme/current/ghosttyin~/.config/ghostty/config. The?makes the include optional.- Zellij
theme "current"inconfig.kdl.- Neovim
- Declare every shipped theme’s colorscheme plugin with
lazy = true, so lazy.nvim knows them before a switch. Fifteen entries; the list is in docs/theme-system.md. Seven themes drivemini.base16from their own palette and need no plugin of their own.
install.sh also symlinks ~/.config/zellij/themes/current.kdl and ~/.config/nvim/lua/plugins/theme.lua for you, if those config directories already exist.
bin/
theme-set theme-pick theme-list theme-next theme-bg theme-port theme-new theme-maintain
current -> themes/retro-82 # THE symlink. Everything reads through it.
themes/<name>/
colors.sh # the palette, as shell exports
ghostty # Ghostty config fragment
zellij.kdl # a themes { current { … } } block
neovim.lua # a LazyVim plugin spec
borders # JankyBorders settings
wallpaper.jpg # generated, not tracked
What theme-set does, in order
- Validates the theme exists, listing the available ones on error.
- Repoints
currentand sources the newcolors.sh. sketchybar --reload, then sets the border colours fromACCENTandMUTED.- Touches Zellij’s
config.kdlso a running session repaints. - Sends
:colorschemeto every running Neovim. - Sets the wallpaper, generating it first if missing. Every Space follows: each points at one fixed file, and a new Space enrolls itself the first time you land on it.
- Asks Ghostty to reload with cmd+shift+,. If the keystroke does not land, it prints the shortcut and you press it yourself.
Steps 3 through 7 are best-effort. A missing or stopped app is skipped and the switch carries on.
Make it yours
A config file, a six-file theme contract, and a porter for anything in Omarchy’s catalogue that is not here yet.
- Config
-
~/.config/macarchy/configis copied from the example on first install. Edit it and re-run theme-set or theme-raycast-sync, or restart SketchyBar, as relevant. Nothing needs a reinstall.~/.config/macarchy/config MACARCHY_DEFAULT_THEME=retro-82 # theme-set with no argument, and the seed on first install MACARCHY_FONT="Hack Nerd Font" # SketchyBar icon/label font MACARCHY_SPACES=9 # highest Space number the bar draws (1-9, global across displays) MACARCHY_RAYCAST_AUTHOR="" # @raycast.author line; blank omits it MACARCHY_WIFI_IFACE=en0 # interface the wifi bar plugin reads - Layout
-
Gaps, padding, the bar strip and the border are settings in the same file, not edits to yabairc. All pixels; the defaults are in
lib.sh, so a line you delete falls back to them. yabairc, bordersrc and sketchybarrc all read them.Setting Default Meaning MACARCHY_GAP8gap between tiled windows MACARCHY_PADDING_TOP8gap under the bar MACARCHY_PADDING_BOTTOM20gap above the screen edge; bigger than the rest because the border draws outside the window MACARCHY_PADDING_LEFT/_RIGHT8side gaps (a centered column adds its own, see Wide displays) MACARCHY_BAR_HEIGHT32SketchyBar strip; yabai reserves the same height at the top of every display ( external_bar)MACARCHY_BORDER_WIDTH6JankyBorders focus border MACARCHY_BORDER_STYLEroundroundorsquareMACARCHY_CENTER_WIDTH16:10centered column width, as an aspect ratio relative to the display height or a pixel count ( 2200)MACARCHY_CENTER_SINGLEtruecenter a lone window automatically; falseleaves only alt+cAfter a change, restart what reads them:
sh $ yabai --restart-service; brew services restart borders; sketchybar --reloadOr shift+alt+r, which runs
macarchy-restartfor all of them. - Add a theme
-
A theme is a directory under
~/.config/theme/themes/<name>/with the six files shown above. Copy an existing theme, edit, thentheme-set <name>andtheme-raycast-sync.colors.shexports bare hex, no#and no0x.colors.sh export THEME_NAME=<name> export NVIM_COLORSCHEME=<name neovim's :colorscheme accepts> export BG=...... export FG=...... export ACCENT=...... export ACCENT2=...... export MUTED=...... export RED=...... export GREEN=...... export YELLOW=...... export BLUE=...... export MAGENTA=...... export CYAN=...... - Make your own
-
theme-new <name>copies the active theme’scolors.shunder a new name and generates everything else from it, Neovim included. Edit the 11 colours, rebuild, switch.sh $ theme-new mine $ $EDITOR ~/.config/theme/themes/mine/colors.sh $ theme-new --regen mine $ theme-set mine - Port one from Omarchy
-
theme-port <name>fetches an Omarchy theme’scolors.tomlandneovim.luaand writes the macarchy files from them.--src DIRreads a local Omarchy checkout instead. Light themes need--allow-light. Two outputs deserve a human look afterwards:ACCENT2is a heuristic, and a theme with no upstream Neovim plugin gets a placeholder to fill in.sh $ theme-port everforest $ theme-set everforest
When something looks wrong
- Start with
macarchy-doctor. It checks every component you installed, packages, permissions, services and the theme state, and prints a fix line under each failure. It is read-only and never starts or installs anything;--quietprints only warnings and failures, and the exit status is 1 when something failed. - Hotkeys stopped landing. skhd stops receiving hotkeys while an app has secure keyboard entry on, which a password field turns on and some terminals keep on.
macarchy-doctorreports which app has it. - Something is stuck. shift+alt+r runs
macarchy-restart: yabai, then borders, then the bar, then skhd, in that order. - A window ended up off every display.
macarchy-rescuepulls it back onto the focused one. - An app opens but shows no window after a monitor comes back or the Mac wakes. yabai kept a window it can no longer move, and the app keeps showing it. A notification names the app;
macarchy-rescue --relaunchquits and reopens it. - Ghostty shows a “Configuration Errors” dialog after a switch. The theme’s
ghosttyfragment names a theme Ghostty does not have. Names are exact:Catppuccin Mocha, notcatppuccin-mocha. Check withghostty +list-themes. - Space indicators look wrong after a reboot.
mru-spacesmust stay off; the installer sets it. yabai and the bar both assume Space N stays Space N. - Switching Spaces feels floaty. The installer already halves the slide to 0.1s. The stronger option is System Settings › Accessibility › Display › Reduce Motion, left off by default because it flattens animations system-wide.
- Neovim did not repaint. Its server socket is under
$TMPDIRon macOS, not/tmp. theme-set searches both. If the theme reports a differentg:colors_name(kanagawa reportskanagawa), the colours are still correct.
$ macarchy-doctor # every component, a fix line per failure
$ macarchy-restart # yabai, borders, the bar, skhd
$ macarchy-rescue # off-display windows, back onto this one
$ macarchy-rescue --relaunch # and reopen an app whose window will not move
$ readlink ~/.config/theme/current # which theme is active
$ ghostty +show-config 2>&1 >/dev/null # theme-name errors show here
$ nvim --headless -c 'lua print(vim.g.colors_name) vim.cmd("qa!")'
$ sketchybar --query bar | jq .color # expect 0xff<BG>
$ tail /tmp/yabai_$USER.err.log