You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every top-level directory is a [stow](https://www.gnu.org/software/stow/) package whose contents
21
+
mirror `$HOME` (`zsh/.zshrc` → `~/.zshrc`), linked by a single interactive `./install`. Assumes
22
+
macOS with zsh; Homebrew is detected at both the Apple Silicon (`/opt/homebrew`) and Intel
23
+
(`/usr/local`) prefixes. Everything is tuned for one specific machine — see [Requirements](#requirements).
24
+
25
+
-**Leaf-only symlinks** — stow runs `--no-folding`, so app-managed directories (`~/.ssh`, `~/.claude`) stay real folders and only tracked files are linked; runtime state and secrets never land inside the repo
26
+
-**One interactive installer** — `./install` prompts y/N before each step and is driven by a commented [`Brewfile`](Brewfile) that is the source of truth for packages
27
+
-**Catppuccin Frappé everywhere** — kitty, Neovim, bat, btop, lazygit, delta, fzf, SketchyBar and JankyBorders share one palette
28
+
-**Tuned for one machine** — a Mac Studio (M4 Max) driving a single 2560×1440 external; AeroSpace and SketchyBar assume exactly that
29
+
-**Layout-aware keys** — workspaces live on `ctrl-<digit>`, because `alt-<digit>` is where the Danish layout hides `[ ] { } \`
30
+
-**Private by default** — `.zprofile` opts out of every telemetry channel in the stack, and allowlist `.gitignore`s keep credentials uncommittable
31
+
32
+
## Requirements
33
+
34
+
macOS with zsh, and [GNU Stow](https://www.gnu.org/software/stow/) (installed by the Brewfile). The
35
+
window-manager and status-bar configs assume a **Mac Studio (M4 Max) driving one 2560×1440 external
36
+
display** — no battery, no built-in display, no notch. AeroSpace and SketchyBar are tuned for exactly
37
+
that: no per-monitor gaps, no forced workspace-to-monitor assignment, no notch arithmetic.
38
+
Reintroducing multi-monitor or laptop assumptions needs a second display actually present.
7
39
8
40
## Install
9
41
@@ -13,7 +45,8 @@ cd ~/dotfiles
13
45
./install
14
46
```
15
47
16
-
The install script interactively prompts (y/N) before each step:
48
+
`install` is a **zsh** script (it uses `read -q`, `print` and the `*(/)` glob qualifier), so
49
+
shellcheck can't read it — syntax-check with `zsh -n install`. It prompts (y/N) before each step:
17
50
18
51
1. Xcode Command Line Tools
19
52
2. Homebrew
@@ -22,31 +55,52 @@ The install script interactively prompts (y/N) before each step:
22
55
5. Oh My Zsh
23
56
6. Oh My Zsh theme (powerlevel10k)
24
57
7. Oh My Zsh custom plugins (fzf-tab, zsh-autosuggestions, zsh-syntax-highlighting)
8. macOS defaults (Finder, Dock, menu bar, keyboard, launcher, screenshots, trackpad, misc power-user)
26
59
9. Stow dotfile linking (errors on conflict by default)
27
60
28
-
Step 8 includes a separate, individually-prompted step that **disables the
29
-
Gatekeeper "downloaded from the internet" warning** (`LSQuarantine`). It is the
30
-
one prompt that weakens a security control — answer `N` to keep the warning.
61
+
Step 8 includes a separate, individually-prompted step that **disables the Gatekeeper "downloaded
62
+
from the internet" warning** (`LSQuarantine`). It is the one prompt that weakens a security control —
63
+
answer `N` to keep the warning.
31
64
32
65
Flags:
66
+
33
67
-`--yes` / `-y` — non-interactive (auto-yes to all prompts)
34
68
-`--adopt` — allow stow to resolve conflicts by adopting `$HOME` files into the repo (**destructive**: overwrites tracked configs; review `git diff` after)
35
69
70
+
## Stow Packages
71
+
72
+
| Package | Contents |
73
+
|---------|----------|
74
+
|`aerospace`|[AeroSpace](https://github.com/nikitabobko/AeroSpace) tiling WM. Numeric workspaces on `ctrl-1`–`ctrl-9` (**not**`alt` — that is where a Danish layout keeps `[ ] { } \`), focus/move on `alt-hjkl`. Launches `borders` and `sketchybar` at startup. Needs Accessibility permission |
75
+
|`bat`| bat config (Catppuccin Frappé theme) |
76
+
|`borders`|[JankyBorders](https://github.com/FelixKratz/JankyBorders) focus ring (`bordersrc`), Catppuccin Frappé mauve/surface1. Started by AeroSpace, not `brew services`, so it reads the stowed config |
77
+
|`btop`| btop Catppuccin Frappé theme (the install script seeds `color_theme = "catppuccin_frappe"` for you) |
78
+
|`claude`| Claude Code settings and statusline |
79
+
|`git`|`.gitconfig` with delta pager, global gitignore. Repos under `~/work/` commit with the work identity — the rule is inert until that directory exists |
80
+
|`hushlogin`| Suppresses the "Last login" message |
|`raycast`|[Raycast](https://www.raycast.com) script commands. Raycast's own settings are not file-based, so only this directory is tracked — add it under Settings > Script Commands > Add Script Directory |
85
+
|`sketchybar`|[SketchyBar](https://github.com/FelixKratz/SketchyBar) status bar with plugins and Swift helpers. Driven by AeroSpace workspace-change events; lint with [`scripts/lint-sketchybar.sh`](scripts/lint-sketchybar.sh)|
86
+
|`ssh`| SSH config with macOS Keychain, hardened algorithms. Put machine-local hosts in `~/.ssh/config.local` (included first, untracked) |
Several packages come from third-party taps (Azure's, `isen-ng`, `nikitabobko`).
66
-
Homebrew 6+ refuses to load from an untrusted tap, so the install script grants
67
-
each declared tap trust as it adds it.
68
-
69
-
## Stow Packages
70
-
71
-
| Package | Contents |
72
-
|---------|----------|
73
-
|`aerospace`|[AeroSpace](https://github.com/nikitabobko/AeroSpace) tiling WM. Numeric workspaces on `ctrl-1`–`ctrl-9` (**not**`alt` — that is where a Danish layout keeps `[ ] { } \`), focus/move on `alt-hjkl`. Needs Accessibility permission; does **not** start at login |
74
-
|`bat`| bat config (Catppuccin Frappé theme) |
75
-
|`btop`| btop Catppuccin Frappé theme (the install script seeds `color_theme = "catppuccin_frappe"` for you) |
76
-
|`claude`| Claude Code settings and statusline |
77
-
|`git`|`.gitconfig` with delta pager, global gitignore. Repos under `~/work/` commit with the work identity — the rule is inert until that directory exists |
|`raycast`|[Raycast](https://www.raycast.com) script commands. Raycast's own settings are not file-based, so only this directory is tracked — add it under Settings > Script Commands > Add Script Directory |
83
-
|`ssh`| SSH config with macOS Keychain, hardened algorithms. Put machine-local hosts in `~/.ssh/config.local` (included first, untracked) |
Several packages come from third-party taps (`isen-ng`, Azure's, `nikitabobko`, `FelixKratz`).
120
+
Homebrew 6+ refuses to load from an untrusted tap, so the install script grants each declared tap
121
+
trust as it adds it.
85
122
86
123
## Zsh Plugins
87
124
@@ -101,6 +138,7 @@ The install script can configure:
101
138
102
139
-**Finder** — show hidden files, extensions, path/status bar, list view, folders on top, search current folder, unhide `~/Library`, no `.DS_Store` on network/USB
103
140
-**Dock** — minimize to app icon, hide recent apps, no space rearranging, icon size, faster animations (auto-hide timings are tuned, but auto-hide itself is left to you)
141
+
-**Menu bar** — optionally auto-hide it (offered only if SketchyBar starts at login, so the two bars don't stack)
104
142
-**Keyboard** — fast key repeat, below what System Settings can express (`KeyRepeat=1.5`, `InitialKeyRepeat=12`, in 15 ms ticks — the slider bottoms out at 2 and 15). Also disables auto-correct, smart quotes and smart dashes, and enables full keyboard access. Takes effect after logout/login
105
143
-**Launcher** — disables the Spotlight (`⌘Space`) and Finder-search (`⌘⌥Space`) shortcuts so Raycast can take the key. `⌥Space` is deliberately *not* used: kitty runs `macos_option_as_alt left`, so it would swallow zsh's `expand-history`
106
144
-**Screenshots** — save to `~/Pictures/Screenshots`, PNG format, no shadow
@@ -119,27 +157,54 @@ The install script can configure:
119
157
|`AZURE_DEV_COLLECT_TELEMETRY=no`| azd |
120
158
|`HOMEBREW_NO_ANALYTICS=1`| Homebrew |
121
159
122
-
These reach anything launched from a shell that sourced `.zprofile`, which
123
-
includes VS Code's integrated terminal but not apps launched from Finder. For a
124
-
setting that holds regardless of environment, `brew analytics off` writes
125
-
Homebrew's own persistent config.
160
+
These reach anything launched from a shell that sourced `.zprofile`, which includes VS Code's
161
+
integrated terminal but not apps launched from Finder. For a setting that holds regardless of
162
+
environment, `brew analytics off` writes Homebrew's own persistent config.
126
163
127
164
## Ignore files
128
165
129
166
Two, with different scopes:
130
167
131
-
-**`.gitignore`** — this repo only. Mostly allowlists: the `ssh` and `claude`
132
-
packages ignore everything and re-include just the tracked configs, so app
133
-
runtime state and credentials can't be committed.
134
-
-**`git/.config/git/ignore`** — stowed to `~/.config/git/ignore` and applied to
135
-
**every** repo on the machine. Git finds it there automatically; no
136
-
`core.excludesfile` needed.
168
+
-**`.gitignore`** — this repo only. Mostly allowlists: the `ssh` and `claude` packages ignore
169
+
everything and re-include just the tracked configs, so app runtime state and credentials can't be
170
+
committed.
171
+
-**`git/.config/git/ignore`** — stowed to `~/.config/git/ignore` and applied to **every** repo on
172
+
the machine. Git finds it there automatically; no `core.excludesfile` needed.
173
+
174
+
Note that gitignore has no trailing-comment syntax — `pattern # note` silently stops matching. Keep
175
+
comments on their own lines.
176
+
177
+
## Gotchas
178
+
179
+
Each of these has been rediscovered more than once. Some fail *silently*.
180
+
181
+
> **A new file in an existing package needs a re-stow.** Because stow runs `--no-folding`,
182
+
> directories are real and only tracked leaf files are symlinked. Editing an existing file takes
183
+
> effect immediately; adding a *new* one does not until you re-stow that package.
137
184
138
-
Note that gitignore has no trailing-comment syntax — `pattern # note` silently
139
-
stops matching. Keep comments on their own lines.
185
+
> **Workspaces are on `ctrl`, not `alt` — leave them there.** On the Danish layout `[ ] { } \` live
186
+
> on the Option layer of the digit row, so binding `alt-<digit>` makes them untypable system-wide.
187
+
> `cmd` is not an escape (`cmd-alt-8` → `[`). The same holds for Norwegian/Swedish/Finnish/German.
188
+
189
+
> **`.zshrc` load order is load-bearing.** Powerlevel10k's instant prompt must come first — nothing
190
+
> may print to stdout before it — and in `plugins=()`**`fzf-tab` must precede `zsh-autosuggestions`
191
+
> and `zsh-syntax-highlighting`**, or completion and highlighting silently break.
192
+
193
+
> **SketchyBar fails silently.** An item that hides itself with `drawing=off` must also set
194
+
> `updates=on` or it never runs again; new plugin scripts need `chmod +x`; and a plugin has less TCC
195
+
> access than your terminal, so a read that works by hand can `EPERM` in the bar.
196
+
> [`scripts/lint-sketchybar.sh`](scripts/lint-sketchybar.sh) catches the first kinds.
197
+
198
+
> **The installer reads the Brewfile from FD 3.**`ask()`'s `read -q` consumes stdin, so the package
199
+
> loop uses `... <&3; done 3< Brewfile`. Converting it to a plain `< Brewfile` makes every prompt eat
200
+
> the next line, silently skipping packages.
140
201
141
202
## Re-stow
142
203
143
204
```zsh
144
205
stow -d ~/dotfiles --no-folding -R <package>
145
206
```
207
+
208
+
## License
209
+
210
+
Released into the public domain under [The Unlicense](LICENSE.md). Take any of it.
0 commit comments