projmux 0.6.7 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,28 +1,34 @@
1
1
  # Theme Palette
2
2
 
3
- This document records the built-in fallback palette that native projmux UI
4
- surfaces use before project or global theme settings exist. The source of
5
- truth in code is `internal/theme/palette.go`.
3
+ This document records the implemented theme contract and the built-in
4
+ fallback palette that native projmux UI surfaces use when no global user theme
5
+ is configured. The source of truth for current fallback values in code is
6
+ `internal/theme/palette.go`, and the source of truth for the resolver and the
7
+ semantic role map is `internal/theme/resolve.go`.
6
8
 
7
9
  ## Scope
8
10
 
9
- The fallback palette is a semantic token layer. Theme settings resolve project
10
- and global config into the resolver-facing token inventory below, then fall
11
- back to the built-in values from `internal/theme/palette.go`.
11
+ The fallback palette is a semantic token layer. The effective theme is a global
12
+ user theme resolved from `~/.config/projmux/config.toml`, followed by the
13
+ built-in fallback values from `internal/theme/palette.go`. The resolver derives
14
+ a semantic role map (`RenderRoles` for tmux chrome, `ANSIRoles` for native ANSI
15
+ surfaces) from the effective theme, and renderers consume those roles instead of
16
+ bare palette literals.
12
17
 
13
18
  - Native picker truecolor SGR tokens.
14
19
  - Native sidebar and chip-strip 256-color SGR tokens.
15
20
  - Tmux statusbar and generated-config color tokens.
16
21
  - Settings/action/state/trust/attention helper tokens.
17
22
 
18
- Renderer adapters apply resolver-backed background/foreground colors to native
19
- picker frame chrome and to tmux status/window background tokens when an
20
- `EffectiveTheme` is supplied by the caller. Fallback-sourced fields still
21
- render through the historical constants so built-in default output remains
22
- byte-identical. Settings and native project picker surfaces load `[theme]`
23
- values from global and project config through the shared effective-theme source.
24
- Theme marketplace/import/export and Visual palette reselection remain out of
25
- scope.
23
+ Renderer adapters apply resolver-backed background and `chrome_foreground`
24
+ colors to native picker frame chrome and to tmux status/window background tokens
25
+ when an `EffectiveTheme` is supplied by the caller. Fallback-sourced foreground,
26
+ status, and state fields still render through their historical constants, while
27
+ pane and popup backgrounds may intentionally inherit the terminal default.
28
+ Project `.projmux/config.toml` `[theme]` is deprecated and ignored: it is not an
29
+ effective theme source, and the resolver and Settings treat any leftover project
30
+ `[theme]` keys as inert migration data. Theme
31
+ marketplace/import/export and Visual palette reselection remain out of scope.
26
32
 
27
33
  ## Resolver Token Inventory
28
34
 
@@ -32,22 +38,38 @@ onto these stable names:
32
38
 
33
39
  | Token | Meaning | Shared surfaces |
34
40
  | --- | --- | --- |
35
- | `background` | base popup/sidebar/status surface background | native picker, frame titlebar, notify sidebar, settings popup, statusbar |
36
- | `surface` | raised or inactive chrome surface | frame titlebar, chips, switch cards, settings popup |
41
+ | `background` | inactive pane body background | tmux inactive panes via `window-style` |
42
+ | `surface` | popup/native frame base background | tmux popup body, native picker rows, frame titlebar/rule, settings popup |
43
+ | `status_background` | bottom status bar background | tmux `status-style` background |
37
44
  | `surface_active` | selected/current row or active chip surface | native picker current row, frame chips, statusbar active window |
38
- | `foreground` | primary readable text | native picker, titlebar, statusbar, notify sidebar, settings popup |
45
+ | `chrome_foreground` | app chrome readable text | native picker frame/title/search chrome, tmux status/window foregrounds, popup body style |
46
+ | `text_primary` | primary content text | settings/info rows and native terminal-rendered content text |
47
+ | `foreground` | legacy alias/fill for split foreground tokens | accepted in config for compatibility; Settings uses `text_primary` / `chrome_foreground` |
39
48
  | `muted` | secondary text, divider, disabled or stale details | picker metadata, titlebar rule, notify age/stale, settings descriptions |
40
49
  | `accent` | pointer, primary action, highlight, active affordance | native picker pointer/highlight, settings actions, chips |
41
50
  | `critical` | destructive/error/critical state | settings remove/quit, notify critical badge, statusbar critical usage |
42
51
  | `warning` | progress, pending, warning, busy state | AI busy/thinking indicators, notify pending title, usage warning |
43
-
44
- AI semantic status badges use renderer-only state roles layered on top of that
45
- inventory: `progress`, `success`, and `action_required`. The fallback contract
46
- is progress yellow, success green, action-required amber-orange. These roles are
47
- separate from notify queue severity and desktop notification urgency; an AI
48
- approval row can be `critical` in the notify queue while the live status badge
49
- uses `action_required`, not red. `critical` remains reserved for error, failure,
50
- destructive, over-limit, or risk states.
52
+ | `progress` | in-progress / working state color | AI progress badge, statusbar progress, state.progress |
53
+ | `success` | completed / success state color | AI success badge, state.success |
54
+ | `action_required` | AI needs-input/approval badge color | AI action-required badge (independent of `critical`) |
55
+ | `pane_active_bg` | active-pane background tint | active-pane window-active-style tint (tmux pane chrome) |
56
+ | `focus` | active-pane border color | active-pane border (tmux pane chrome) |
57
+
58
+ `text_primary` and `chrome_foreground` split the old broad foreground behavior:
59
+ changing primary content text no longer repaints frame/title/search/border/status
60
+ chrome as a side effect. The legacy `foreground` key remains readable; it fills
61
+ both split fields unless either split field is explicitly set.
62
+
63
+ `progress`, `success`, and `action_required` are public `[theme]` keys, no
64
+ longer renderer-only candidates. The fallback contract is progress yellow,
65
+ success green, action-required amber-orange. These roles are separate from
66
+ notify queue severity and desktop notification urgency; an AI approval row can
67
+ be `critical` in the notify queue while the live status badge uses
68
+ `action_required`, not red. `action_required` is independent of `critical`:
69
+ repainting `critical` never changes it. `critical` remains reserved for error,
70
+ failure, destructive, over-limit, or risk states. `pane_active_bg` and `focus`
71
+ are also public keys driving the active-pane tint and border (tmux-only pane
72
+ chrome, with no ANSI/native role).
51
73
 
52
74
  Renderer-only role names such as `accent.ai`, `state.progress`, `git.branch`,
53
75
  and trust colors remain in `internal/theme/palette.go` until Phase 2+ maps each
@@ -56,28 +78,31 @@ native picker, frame titlebar, chips, statusbar, notify sidebar, and settings
56
78
  popup all consume a shared effective token set instead of independently
57
79
  choosing colors.
58
80
 
59
- Font is not part of this universal token inventory. `font_family` and
60
- `font_size` are resolved as terminal capability/profile hints: projmux can
61
- store and display the desired value, but tmux/ANSI rendering cannot force a
62
- font family or size across terminal emulators. In environments without a
63
- supported terminal font adapter, projmux reports the desired font as
64
- `not applied` instead of treating storage as a successful font change.
81
+ Font is not part of this token inventory. The earlier `font_family` and
82
+ `font_size` theme keys were removed in Phase 1b: tmux/ANSI rendering cannot
83
+ force a font family or size across terminal emulators, so the values never
84
+ applied to the terminal. Leftover font keys in an existing config are accepted
85
+ but ignored. See `docs/upgrading.md`.
65
86
 
66
87
  ## Mapping Policy
67
88
 
68
89
  Native picker rows can emit truecolor SGR, while tmux statusbar/config strings
69
- must use tmux color specs. The resolver therefore carries both forms for each
70
- color token.
90
+ accept tmux style color specs. The resolver therefore carries exact hex plus a
91
+ 256-color approximation for renderer paths that still need it.
71
92
 
72
93
  Rules:
73
94
 
74
95
  - Truecolor tokens keep exact `#RRGGBB` values and can be converted to
75
96
  foreground/background SGR fragments such as `38;2;R;G;B` or `48;2;R;G;B`.
76
- - Tmux tokens keep `colourN` strings where tmux owns rendering.
77
- - The built-in `projmux-dark` fallback uses the established ANSI and tmux
78
- tokens from `internal/theme/palette.go` to preserve current output.
79
- - Explicit `#RRGGBB` overrides keep exact truecolor and derive the closest
80
- xterm 256-color `colourN` token for tmux surfaces.
97
+ - Tmux style roles use exact `#RRGGBB` for explicit theme/preset colors and
98
+ keep historical `colourN` strings only for fallback literals. Generated tmux
99
+ config declares `xterm*:RGB` in `terminal-features` so capable terminals render
100
+ those exact colors instead of tmux downsampling them to xterm-256 colors.
101
+ - The built-in `projmux` fallback keeps text/accent/state tokens from the
102
+ established palette while pane and popup backgrounds ride the terminal
103
+ default.
104
+ - Explicit `#RRGGBB` overrides also retain the closest xterm 256-color
105
+ `colourN` token for 256-color-only renderer roles.
81
106
  - Native chip/sidebar badge tokens use 256-color SGR when they intentionally
82
107
  mirror tmux colors.
83
108
  - Output compatibility wins inside this baseline. For example, the kube
@@ -88,32 +113,63 @@ Rules:
88
113
 
89
114
  ## Resolver Contract
90
115
 
91
- Theme resolution is field-by-field after validating each layer:
116
+ The effective theme resolves theme fields from:
92
117
 
93
- 1. Project `.projmux/config.toml`
94
- 2. Global `~/.config/projmux/config.toml`
95
- 3. Built-in fallback preset `projmux-dark`
118
+ 1. Global `~/.config/projmux/config.toml`
119
+ 2. Built-in fallback preset `projmux`
96
120
 
97
121
  Rules:
98
122
 
99
- - Project values override global values for the same field.
100
- - Missing or `inherit` project values fall back to global values.
101
123
  - Missing global values fall back to built-in values.
102
- - A preset fills missing color tokens in its own layer.
103
- - Explicit color tokens in the same layer override preset colors.
104
- - An unknown preset invalidates only that layer and emits a warning.
105
- - An invalid color, `font_family`, or `font_size` invalidates only that layer
106
- and emits a warning.
107
- - Every effective field reports `project`, `global`, or `fallback` as its
108
- source label.
124
+ - A preset fills missing color tokens in the global layer.
125
+ - Explicit global color tokens override preset colors.
126
+ - Legacy `foreground` fills `text_primary` and `chrome_foreground` unless either
127
+ split key is explicitly set.
128
+ - An unknown preset invalidates only the global layer.
129
+ - An invalid color invalidates only the global layer.
130
+ - Every effective field reports `global` or `fallback` as its source label.
131
+
132
+ ### Terminal default sentinel
133
+
134
+ The `background`, `surface`, `status_background`, `surface_active`, and
135
+ `pane_active_bg` tokens accept the special value `default` ("Terminal default"
136
+ in Settings). It pins that surface to the terminal background instead of a
137
+ concrete color, even when a preset is selected:
138
+
139
+ - Priority is **explicit `default` > preset fill > unset (fallback)**. Setting a
140
+ token to `default` overrides the preset's color for that token while leaving
141
+ every other token preset-filled.
142
+ - On the tmux side the derived roles emit `bg=default` (for example
143
+ `window-style "bg=default"` from `background`, popup body style from
144
+ `surface`, `window-active-style "bg=default"` from `pane_active_bg`, and
145
+ `status-style bg=default` from `status_background`).
146
+ - On the ANSI side the corresponding surface emits no background sequence (no
147
+ `48;2;…` / `48;5;…`), so the terminal background shows through.
148
+ - `default` is only valid on `background` / `surface` / `status_background` /
149
+ `surface_active` / `pane_active_bg`. On any other token it is treated as an
150
+ invalid hex color and invalidates the global layer (same as any other invalid
151
+ color).
152
+
153
+ Historical project `[theme]` values in `.projmux/config.toml` are migration
154
+ data only. They are not a current or target source in this contract.
109
155
 
110
156
  Built-in preset config values are:
111
157
 
112
- - `projmux-dark`
113
- - `midnight`
158
+ - `projmux`
159
+ - `high-contrast`
160
+ - `blue-hour`
161
+ - `carbon-violet`
162
+ - `ember`
114
163
  - `forest`
115
164
  - `rose`
116
- - `high-contrast`
165
+ - `blue-hour` — dark theme tuned around a terminal blue accent; it uses a dark
166
+ blue-tinted inactive pane body, black popup surface, deep navy status bar,
167
+ and a deep indigo active pane tint.
168
+ - `carbon-violet` — charcoal/violet dark theme with darker popup/status
169
+ surfaces and a black active pane tint.
170
+ - `high-contrast` — black surfaces, white text, a dark blue active surface,
171
+ near-black active pane tint, vivid cyan focus, and bright
172
+ cyan/yellow/red/green state colors.
117
173
 
118
174
  ## Fallback Inventory
119
175
 
@@ -122,7 +178,7 @@ Chrome and text:
122
178
  | Role | Native SGR | Tmux |
123
179
  | --- | --- | --- |
124
180
  | `surface.active` | `48;2;44;56;61` with selected white text | window active `colour240` / `colour231` |
125
- | `surface.raised` | `48;2;24;34;38` with `216;224;228` text | window inactive `colour235` / `colour245` |
181
+ | `surface.raised` | terminal-default background with `216;224;228` text | popup/native frame surface |
126
182
  | `text.primary` | `216;224;228` | `colour231` or `colour254` for identity text |
127
183
  | `text.secondary` | `164;176;182` | `colour245` |
128
184
  | `text.muted` | `117;132;140` or ANSI dim | `colour244`, `colour238`, `colour240` for low-signal blocks |
@@ -155,6 +211,25 @@ Surface-specific tokens:
155
211
  | Settings | add/type/open action, destructive remove/quit, back/cancel, info/read-only, dim description, root action/dim rows, trust trusted/stale/untrusted |
156
212
  | Switch picker cards | path metadata, active/inactive git branch badges aligned with statusbar git branch block colors, statusbar-like window tabs, inline attention/progress dots |
157
213
 
214
+ Active-pane focus: tmux draws a single shared border between adjacent panes, so
215
+ a full active-pane rectangle (e.g. tinting the whole active pane edge-to-edge)
216
+ is not guaranteed. Active focus is instead reinforced by the `pane-border-status
217
+ top` topic line, an active-pane border (`pane-active-border-style`, fallback
218
+ cyan `colour51`, the public `focus` token), and a subtle dark background tint
219
+ applied via `window-active-style` (fallback `colour234` — one tone darker than
220
+ the base `colour235` so the active pane visibly sinks; the public `pane_active_bg`
221
+ token). Inactive panes keep the terminal default background via `window-style
222
+ "bg=default"` unless the public `background` token is set.
223
+
224
+ Pane body vs popup vs status background: the general pane body, popup/native
225
+ frame background, and bottom status bar are derived from separate public tokens.
226
+ The pane body follows `background` (tmux `window-style`; unset keeps
227
+ `bg=default`), popup/native frames follow `surface`, and the bottom status bar
228
+ follows `status_background` (tmux `status-style`; unset keeps `colour235`).
229
+ Built-in presets fill `status_background` from their surface color to preserve
230
+ the old look, but an explicit `surface` override no longer repaints the bottom
231
+ status line.
232
+
158
233
  ## Current Literal Inventory
159
234
 
160
235
  After the Phase 3 token pass, raw color values intentionally remain in:
package/docs/upgrading.md CHANGED
@@ -30,6 +30,83 @@ the current latest release tag in `update-skip.json`; the prompt appears again
30
30
  when the cached latest tag changes. For `source` and unknown installer sources,
31
31
  Upgrade prints guidance and continues shell entry without applying anything.
32
32
 
33
+ ## Behavior Changes
34
+
35
+ ### Theme is now global-only
36
+
37
+ Theme is a global user preference. The effective theme resolves from the global
38
+ `[theme]` in `~/.config/projmux/config.toml` plus a built-in fallback preset.
39
+
40
+ If you previously set a `[theme]` section in a project's `.projmux/config.toml`,
41
+ it is now **deprecated and ignored** — it no longer overrides the global theme
42
+ and does not affect the native picker, statusbar, popups, or any tmux chrome.
43
+ The project `[theme]` keys are left in the file untouched (no warning, no
44
+ removal); they simply have no effect.
45
+
46
+ To restore your previous look, copy the values into the global
47
+ `~/.config/projmux/config.toml` `[theme]` section, or edit them through
48
+ Settings > Theme. Settings no longer exposes a Project theme editor, and the
49
+ separate Effective theme view has been merged into the Global theme view: each
50
+ token row now shows its resolved value inline, with unset tokens shown as their
51
+ dimmed `(fallback)` value.
52
+
53
+ ### Foreground split
54
+
55
+ The old broad `foreground` theme key is now split into two clearer public keys:
56
+ `text_primary` for primary native content text, and `chrome_foreground` for
57
+ frame/title/search/status/window chrome foreground roles. Existing global
58
+ `foreground` values still work as a legacy alias/fill and will feed both split
59
+ roles unless either new key is set explicitly. Settings shows and writes the new
60
+ split names instead of encouraging new `foreground` writes.
61
+
62
+ If your previous `foreground` override made both content and chrome change
63
+ together, you do not need to migrate immediately. To tune them separately, copy
64
+ the value into `text_primary` and/or `chrome_foreground`, then remove
65
+ `foreground` when you no longer need the compatibility fill.
66
+
67
+ ### New public theme keys
68
+
69
+ Seven new public `[theme]` keys are now available: `text_primary`,
70
+ `chrome_foreground`, `progress`, `success`, `action_required` (AI/status
71
+ colors), `pane_active_bg` (active-pane tint), and `focus` (active-pane border).
72
+ Leaving a key unset keeps the historical built-in color; setting it repaints the
73
+ matching role. `action_required` is independent of `critical` — repainting
74
+ `critical` never changes it. The full public token set is documented in
75
+ `docs/configuration.md` and `docs/theme-palette.md`.
76
+
77
+ The active-pane tint (`pane_active_bg`) defaults to the terminal background in
78
+ the built-in `projmux` preset; set it to a concrete color when the active pane
79
+ should visibly sink. The active-pane border (`focus`) defaults to cyan
80
+ `colour51`. Both apply only to tmux pane chrome.
81
+
82
+ Built-in presets are intentionally small: `projmux`, `high-contrast`,
83
+ `blue-hour`, `carbon-violet`, `ember`, `forest`, and `rose`. Terminal-default
84
+ backgrounds are configured per token with the `default` sentinel rather than
85
+ through separate terminal preset variants.
86
+
87
+ ### Pane body vs popup backgrounds
88
+
89
+ The general (pane) background and the popup/chrome background are now driven by
90
+ separate public tokens. The pane body follows `background` (unset keeps the
91
+ terminal default), while the status bar, native popup bodies, and the
92
+ settings/notify/recent/picker frames follow `surface`. Because the `surface`
93
+ fallback equals `background`, leaving both unset looks exactly as before; set
94
+ them to different values to make popups read as a distinct surface from the pane
95
+ body.
96
+
97
+ ### Theme font keys removed
98
+
99
+ The `[theme]` `font_family` and `font_size` keys were removed. They never
100
+ applied to the terminal — tmux/ANSI rendering cannot force a font family or
101
+ size across terminal emulators — so they only stored and displayed a desired
102
+ value that was always reported as `not applied`.
103
+
104
+ Leftover `font_family` / `font_size` keys in a global or project
105
+ `config.toml` are accepted but ignored: they no longer parse into the theme,
106
+ appear in Settings, or affect any surface. You can delete them at your
107
+ convenience. Set your terminal font through your terminal emulator's own
108
+ profile settings instead.
109
+
33
110
  ## npm Installs
34
111
 
35
112
  The recommended install path is:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projmux",
3
- "version": "0.6.7",
3
+ "version": "0.7.1",
4
4
  "description": "tmux project session manager",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/crevissepartners/projmux#readme",
@@ -28,9 +28,9 @@
28
28
  "package:npm:pack": "scripts/package-npm.sh --pack"
29
29
  },
30
30
  "optionalDependencies": {
31
- "@projmux/linux-x64": "0.6.7",
32
- "@projmux/linux-arm64": "0.6.7",
33
- "@projmux/darwin-x64": "0.6.7",
34
- "@projmux/darwin-arm64": "0.6.7"
31
+ "@projmux/linux-x64": "0.7.1",
32
+ "@projmux/linux-arm64": "0.7.1",
33
+ "@projmux/darwin-x64": "0.7.1",
34
+ "@projmux/darwin-arm64": "0.7.1"
35
35
  }
36
36
  }
@@ -1,97 +0,0 @@
1
- # README Hero GIF Recording
2
-
3
- This recipe records the README hero GIF:
4
-
5
- - `docs/assets/projmux-ai-attention.gif`
6
-
7
- The maintained recorder is kept in the local dotfiles checkout at:
8
-
9
- ```sh
10
- /home/es5h/dotfiles/bin/projmux/projmux-record-readme-gifs.py
11
- ```
12
-
13
- ## Prerequisites
14
-
15
- Install or verify these local tools before recording:
16
-
17
- - `python3`
18
- - `git`
19
- - `tmux`
20
- - `ffmpeg` and `ffprobe`
21
- - `Xvfb`
22
- - `openbox`
23
- - `ghostty`
24
- - `xdotool`
25
- - `xwininfo`
26
- - `script` from util-linux
27
- - authenticated `codex`
28
-
29
- Build the local projmux binary first:
30
-
31
- ```sh
32
- make build
33
- ```
34
-
35
- The script expects `.bin/projmux` by default. Override paths only when needed:
36
-
37
- ```sh
38
- PROJMUX_RECORD_REPO=/path/to/projmux \
39
- PROJMUX_RECORD_BIN=/path/to/projmux \
40
- PROJMUX_RECORD_DISPLAY=117 \
41
- PROJMUX_RECORD_SCREEN=2560x1440x24 \
42
- PROJMUX_RECORD_KEEP_TMP=1 \
43
- python3 /home/es5h/dotfiles/bin/projmux/projmux-record-readme-gifs.py
44
- ```
45
-
46
- For the normal checkout, run:
47
-
48
- ```sh
49
- python3 /home/es5h/dotfiles/bin/projmux/projmux-record-readme-gifs.py
50
- ```
51
-
52
- ## Scenario Contract
53
-
54
- The recording uses a private demo home, XDG config/state dirs, demo git
55
- projects, and an isolated `CODEX_HOME`. It copies the local Codex auth/config
56
- into that isolated home, trusts the demo projects/hooks, and seeds usage cache
57
- data so the tmux status line shows the Codex HUD during the capture.
58
-
59
- The scenario records the AI attention flow:
60
-
61
- 1. Start in `mobile-client` with no pending notification.
62
- 2. Open the AI picker and launch a real Codex pane.
63
- 3. Ask Codex to do a short task.
64
- 4. Use the projmux sessionizer sidebar to move to `atlas-api`.
65
- 5. Keep working in zsh while Codex finishes in the previous project.
66
- 6. When the Codex completion notification exists, open the notification sidebar.
67
- 7. Select the notification and return focus to the original Codex pane.
68
-
69
- ## Visual Guardrails
70
-
71
- - Keep the native picker UI native. The script runs picker commands through
72
- `script(1)` with a fixed PTY size so fzf/terminal UI rendering is captured
73
- instead of degraded line-mode output.
74
- - Keep the terminal in zsh with the demo prompt so the project and git branch
75
- are visible.
76
- - Keep the Codex usage HUD visible in the tmux status line.
77
- - Use the sessionizer sidebar for project movement in 4a.
78
- - Capture the Ghostty X11 window geometry with `xwininfo` and feed that exact
79
- rectangle to ffmpeg. This prevents the GIF from drifting away from `(0, 0)`.
80
-
81
- ## Verification
82
-
83
- After recording, inspect the resulting streams:
84
-
85
- ```sh
86
- ffprobe -v error -select_streams v:0 \
87
- -show_entries stream=width,height,nb_frames,duration \
88
- -of default=noprint_wrappers=1 \
89
- docs/assets/projmux-ai-attention.gif
90
- ```
91
-
92
- Expected final shape is roughly 1110px wide and about 16-18 seconds, with the
93
- native picker, zsh prompt, Codex pane, notification sidebar, and usage HUD
94
- visible in the relevant frames.
95
-
96
- Set `PROJMUX_RECORD_KEEP_TMP=1` when you need to inspect intermediate MP4s,
97
- Ghostty logs, or palette files under `/tmp/projmux-readme-record-*`.