projmux 0.5.3 → 0.6.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.
@@ -8,10 +8,10 @@ The recommended path when a key does not fire:
8
8
 
9
9
  1. Press the key inside `projmux shell` and see what works on your terminal
10
10
  out of the box ([Quick start](#quick-start-no-setup)).
11
- 2. If something is swallowed, open Settings > Labs > Diagnose keybindings, or
12
- run [`projmux setup`](#diagnose-projmux-setup) outside tmux. Both paths tell
13
- you exactly which sequences reach the process and which the terminal is
14
- eating.
11
+ 2. If something is swallowed, open Settings > Keybindings and select the
12
+ action to capture the key, or run [`projmux setup`](#diagnose-projmux-setup)
13
+ outside tmux. Both paths tell you exactly which sequences reach the process
14
+ and which the terminal is eating.
15
15
  3. For terminals projmux knows how to configure, run
16
16
  [`projmux init [terminal]`](#auto-config-projmux-init) as the fallback:
17
17
  preview first, then add `--apply` to merge the right bindings into your
@@ -20,23 +20,20 @@ The recommended path when a key does not fire:
20
20
  hand), use the [Manual fallback / advanced (CSI-u)](#manual-fallback--advanced-csi-u)
21
21
  section.
22
22
 
23
- To change projmux's tmux-level chords, open Settings > Keybindings. The
24
- in-app editor writes `~/.config/projmux/keymap.toml`, regenerates
25
- `~/.config/projmux/tmux.conf`, and hot-reloads the live tmux config when
26
- Settings is running inside tmux. Terminal fallback mappings are separate:
27
- after changing fallback keys, rerun `projmux init` and restart the terminal
28
- where applicable.
29
-
30
- Settings > Labs > Diagnose keybindings reuses the same probe and terminal
31
- fallback engines from `projmux setup` and `projmux init`. It lists the
32
- keybinding catalog, lets you press one action key at a time from inside the
33
- app, distinguishes plain / CSI-u / unexpected / timeout outcomes, and exposes
34
- preview/apply rows for supported terminal fallbacks. When an unexpected
35
- sequence can be safely read as a tmux plain chord, the Lab shows an explicit
36
- `Save as plain override` row with the suggested chord; it never overwrites
37
- `keymap.toml` from an unexpected sequence unless you select that confirmation
38
- row. The Lab also shows whether the detected terminal can reload config after
39
- fallback apply or needs a restart/manual reload.
23
+ To change a user-facing key, open Settings > Keybindings, select an action,
24
+ then choose `Press new key`. Settings captures one keypress through the same
25
+ TTY probe path used by `projmux setup`, writes safe tmux plain chords to
26
+ `~/.config/projmux/keymap.toml`, regenerates `~/.config/projmux/tmux.conf`,
27
+ and hot-reloads the live tmux config when Settings is running inside tmux.
28
+ CSI-u/User-key captures are reported as terminal fallback delivery and do not
29
+ need a keymap write. Raw sequences that cannot be represented safely as a tmux
30
+ plain chord are not persisted; configure terminal fallback with `projmux init`
31
+ instead.
32
+
33
+ The Settings surface no longer exposes plain/prefix tmux chord fields. Legacy
34
+ `prefix = ...` entries in an existing `keymap.toml` still parse during
35
+ migration, but Settings does not write new prefix entries and generated tmux
36
+ config no longer binds the old action prefix chords.
40
37
 
41
38
  > 한국어 요약: 대부분의 터미널은 `projmux shell` 만으로 아래 키가 바로 동작합니다.
42
39
  > 동작하지 않으면 `projmux setup` 으로 어떤 키가 막혔는지 진단하고,
@@ -71,15 +68,6 @@ These open the projmux popups and the sidebar. No prefix needed.
71
68
  | `Alt-5` | Settings |
72
69
  | `Alt-6` | Project switcher popup |
73
70
 
74
- The same surfaces are also available from the prefix table:
75
-
76
- | Shortcut | Action |
77
- | --- | --- |
78
- | `Prefix F` | Project sidebar |
79
- | `Prefix b` | Existing session popup |
80
- | `Prefix f` | Project switcher popup |
81
- | `Prefix g` | Jump to the current pane's project session |
82
-
83
71
  ### Windows, panes, AI splits
84
72
 
85
73
  | Shortcut | Action |
@@ -88,9 +76,6 @@ The same surfaces are also available from the prefix table:
88
76
  | `Alt-Shift-Left` / `Alt-Shift-Right` | Previous / next window |
89
77
  | `Alt-Left` / `Right` / `Up` / `Down` | Move focus between panes |
90
78
  | `Alt-r` | Rename the current window |
91
- | `Prefix R` | Rename the current window |
92
- | `Prefix r` | Open an AI split to the right |
93
- | `Prefix l` | Open an AI split below |
94
79
 
95
80
  When a pane closes, projmux re-spreads remaining panes so the surviving split
96
81
  does not stretch lopsided.
@@ -125,8 +110,8 @@ which projmux keys actually reach the process. The command auto-detects your
125
110
  terminal, then asks you to press each shortcut in turn and classifies the
126
111
  result:
127
112
 
128
- The same diagnostic is available in-app at Settings > Labs > Diagnose
129
- keybindings. The Lab flow reads the controlling TTY directly, so it can probe a
113
+ The same diagnostic is available in-app at Settings > Keybindings > Diagnostic.
114
+ The flow reads the controlling TTY directly, so it can probe a
130
115
  key while Settings itself is running inside tmux.
131
116
 
132
117
  | Status | Meaning |
@@ -140,10 +125,8 @@ The summary at the end lists the failing keys and a remediation hint
140
125
  tailored to the detected terminal (Ghostty, WezTerm, kitty, iTerm2,
141
126
  Alacritty, Windows Terminal, foot, VS Code, …). When projmux ships an init
142
127
  adapter for the terminal, the summary gives both the dry-run preview and the
143
- exact apply command, e.g. `projmux init ghostty --apply`.
144
- Settings > Labs also includes a concise after-apply hint: Ghostty/WezTerm/kitty
145
- can reload config, Windows Terminal and iTerm2 generally need a restarted tab
146
- or session, and unknown terminals are marked manual.
128
+ exact apply command, e.g. `projmux init ghostty --apply`. Settings >
129
+ Keybindings shows the same delivery categories after a capture.
147
130
 
148
131
  Useful flags:
149
132
 
@@ -72,14 +72,14 @@ a different command worked better.
72
72
  |---|---|---|---|---|
73
73
  | Ghostty | macOS | `osascript -e 'tell application "Ghostty" to activate'` + window-id match | | [ ] |
74
74
  | Ghostty | Linux X11 | `wmctrl -ia <wid>` | | [ ] |
75
- | Ghostty | Linux Wayland | Compositor-specific: gnome `dbus` (`org.gnome.Shell.Eval`), KDE `kdotool`, sway `swaymsg` | | [ ] |
76
- | Kitty | Any | `kitty @ focus-window --match <expr>` | | [ ] |
77
- | WezTerm | Any | `wezterm cli activate-pane --pane-id <id>` | | [ ] |
75
+ | Ghostty | Linux Wayland | Compositor-specific: gnome `dbus` (`org.gnome.Shell.Eval`), KDE `kdotool`, sway `swaymsg` | No — Ubuntu GNOME 50 Wayland with Ghostty 1.3.1 exposes `GHOSTTY_*` detection signals, but Ghostty has no raise/focus CLI action and GNOME Shell `Eval` returns `(false, '')` outside unsafe mode. | [x] |
76
+ | Kitty | Any | `kitty @ focus-window --match <expr>` | Partial — Ubuntu GNOME Wayland default kitty 0.45.0 attach exposes `KITTY_WINDOW_ID`/`KITTY_PID`, but no `KITTY_LISTEN_ON`; external `kitty @ ls` fails without a controlling TTY unless kitty is launched with a remote-control socket. | [x] |
77
+ | WezTerm | Any | `wezterm cli activate-pane --pane-id <id>` | Partial — WezTerm 20240203 crashes on native GNOME 50 Wayland in this host, but the X11 fallback (`WAYLAND_DISPLAY` unset) attaches and `wezterm cli activate-pane --pane-id 0` succeeds. | [x] |
78
78
  | Windows Terminal | Windows | `wt -w <id> focus-tab -t <n>` (same instance only) | | [ ] |
79
79
  | Windows Terminal | WSL → Windows | WSL interop + `wt.exe -w <id> focus-tab` | OK — `wt.exe -w 0 focus-tab -t 0` raises the WT window (verified WSL2 Ubuntu-24.04, WT host) | [x] |
80
80
  | iTerm2 | macOS | AppleScript (`tell application "iTerm" ...`) or Python API | | [ ] |
81
- | Alacritty | Any | No remote IPC. OS-window focus only — fall back to the table below. | | [ ] |
82
- | Foot | Linux | `footclient` is limited. OS-window focus only — fall back to the table below. | | [ ] |
81
+ | Alacritty | Any | No remote IPC. OS-window focus only — fall back to the table below. | No — Alacritty 0.16.1 IPC socket is reachable and can create windows/read config, but exposes no existing-window focus or raise command. | [x] |
82
+ | Foot | Linux | `footclient` is limited. OS-window focus only — fall back to the table below. | No — foot 1.25.0 server/footclient can create a new terminal window, but exposes no existing-window focus or raise command. | [x] |
83
83
  | VS Code embedded | Any | Best-effort via `vscode://` URL handler or `code --command` | | [ ] |
84
84
 
85
85
  ## OS-level window activation matrix
@@ -91,7 +91,7 @@ Independent of the terminal: "raise this app to the foreground." Fill the
91
91
  |---|---|---|---|---|
92
92
  | macOS | `osascript -e 'tell application "<AppName>" to activate'` | Stable; no special permission. | | [ ] |
93
93
  | Linux X11 | `wmctrl -a <window-name>` or `xdotool windowactivate <wid>` | Requires `wmctrl` / `xdotool` installed; standard. | | [ ] |
94
- | Linux Wayland (GNOME) | `gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell --method org.gnome.Shell.Eval ...` | Depends on shell version; recent GNOME locks `Eval` outside dev mode. | | [ ] |
94
+ | Linux Wayland (GNOME) | `gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell --method org.gnome.Shell.Eval ...` | Depends on shell version; recent GNOME locks `Eval` outside dev mode. | No — GNOME Shell 50 returns `(false, '')` for `Eval`; X11 fallbacks also do not see native Wayland Ghostty windows (`wmctrl -lx` empty, `xdotool` cannot resolve title/pid). | [x] |
95
95
  | Linux Wayland (KDE) | `kdotool windowactivate <wid>` | KWin script bridge; requires `kdotool`. | | [ ] |
96
96
  | Linux Wayland (sway) | `swaymsg '[con_id=<id>] focus'` | Works for sway / wlroots compositors. | | [ ] |
97
97
  | Windows | `SetForegroundWindow` via PowerShell + P/Invoke | Foreground-lock policy: if the target hasn't recently been active, the OS will only flash the taskbar entry. | | [ ] |
@@ -155,6 +155,84 @@ Notes from Test 3 (`wt.exe -w 0` bare, no subcommand):
155
155
  (Test 1) to raise without creating a tab. Treat the bare form as
156
156
  reserved for the "open new tab" case only.
157
157
 
158
+ Notes from Test 4 (Ubuntu GNOME Wayland + Ghostty):
159
+
160
+ - Environment: Ubuntu GNOME Wayland (`XDG_SESSION_TYPE=wayland`,
161
+ `XDG_CURRENT_DESKTOP=ubuntu:GNOME`, `WAYLAND_DISPLAY=wayland-0`) with
162
+ Ghostty 1.3.1 (`GHOSTTY_RESOURCES_DIR`, `GHOSTTY_BIN_DIR` set). Inside tmux,
163
+ `TERM_PROGRAM=tmux`, so Ghostty detection must use the `GHOSTTY_*` signals.
164
+ - Ghostty 1.3.1 exposes helper actions such as `+new-window`,
165
+ `+show-config`, and `+list-actions`, but no CLI action that raises an
166
+ existing window, focuses a tab, or activates a split. Treat Ghostty on
167
+ native Linux Wayland as OS-window-focus-only unless a future Ghostty IPC
168
+ surface lands.
169
+ - GNOME Shell `Eval` is not usable as a default adapter path on GNOME 50:
170
+ `gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell
171
+ --method org.gnome.Shell.Eval 'global.display.focus_window ?
172
+ global.display.focus_window.get_title() : ""'` returned `(false, '')`.
173
+ - X11 fallbacks are insufficient for native Wayland windows in this
174
+ configuration: `wmctrl -lx` returned an empty list, and `xdotool
175
+ getactivewindow getwindowname getwindowpid` could only see an Xwayland
176
+ window id with no associated pid/title.
177
+
178
+ Notes from Test 5 (Ubuntu GNOME Wayland + kitty default attach):
179
+
180
+ - Environment: Ubuntu GNOME Wayland with kitty 0.45.0 attached to the projmux
181
+ tmux server (`client_termname=xterm-kitty`).
182
+ - The tmux client process inherited `KITTY_WINDOW_ID=1`, `KITTY_PID=<pid>`,
183
+ `TERM=xterm-kitty`, and `COLORTERM=truecolor`, but not `KITTY_LISTEN_ON`.
184
+ - From a non-interactive external process, `kitty @ ls` failed with
185
+ `open /dev/tty: no such device or address`. That confirms the default
186
+ no-socket launch cannot be driven by an osfocus adapter running outside the
187
+ kitty window.
188
+ - The candidate command remains viable only when kitty is launched with a
189
+ remote-control surface (`--listen-on ...` or `listen_on` plus
190
+ `allow_remote_control`) and the adapter can discover that address.
191
+
192
+ Notes from Test 6 (Ubuntu GNOME Wayland + kitty socket attach):
193
+
194
+ - Launching kitty with `--listen-on unix:/tmp/projmux-kitty.sock --override
195
+ allow_remote_control=yes` passed `KITTY_LISTEN_ON` through to the tmux client
196
+ and allowed an external process to run `kitty @ --to
197
+ unix:/tmp/projmux-kitty.sock ls`.
198
+ - `kitty @ --to unix:/tmp/projmux-kitty.sock focus-window --match id:1`
199
+ exited successfully. The adapter path is viable when the launch/config
200
+ exposes a discoverable remote-control socket.
201
+
202
+ Notes from Test 7 (Ubuntu GNOME Wayland + WezTerm):
203
+
204
+ - Native Wayland launch failed on GNOME 50 with `wl_surface ... Buffer size
205
+ ... must be an integer multiple of the buffer_scale (2)`.
206
+ - X11 fallback launch (`WAYLAND_DISPLAY` unset) attached to tmux successfully
207
+ (`client_termname=xterm-256color`).
208
+ - `wezterm cli list` failed while `WAYLAND_DISPLAY` pointed at the stale
209
+ Wayland socket, but `env -u WAYLAND_DISPLAY wezterm cli list` found the X11
210
+ GUI socket and reported `PANEID 0`.
211
+ - `env -u WAYLAND_DISPLAY wezterm cli activate-pane --pane-id 0` exited
212
+ successfully. A WezTerm adapter on GNOME Wayland must avoid stale Wayland
213
+ socket selection when the GUI is actually running on X11.
214
+
215
+ Notes from Test 8 (Ubuntu GNOME Wayland + Alacritty):
216
+
217
+ - Alacritty 0.16.1 daemon mode created an IPC socket at
218
+ `/run/user/1000/Alacritty-wayland-0-<pid>.sock`; `alacritty msg --socket
219
+ ... create-window -e tmux -L projmux attach -t repos-projmux` attached a new
220
+ tmux client (`client_termname=alacritty`).
221
+ - `alacritty msg --socket ... get-config` succeeded, confirming that the
222
+ socket is externally reachable.
223
+ - `alacritty msg` exposes `create-window`, `config`, and `get-config`; it does
224
+ not expose an existing-window focus/raise command. Treat Alacritty as
225
+ OS-window-focus-only for this roadmap item.
226
+
227
+ Notes from Test 9 (Ubuntu GNOME Wayland + foot):
228
+
229
+ - Starting `foot-server.socket` and then running `footclient -N ... tmux -L
230
+ projmux attach -t repos-projmux` attached a new tmux client
231
+ (`client_termname=foot`).
232
+ - The footclient surface is useful for creating terminals against the server,
233
+ but does not provide an IPC command to focus or raise an existing window.
234
+ Treat foot as OS-window-focus-only.
235
+
158
236
  If multiple signals are present (e.g. tmux inside VS Code's embedded
159
237
  terminal), the adapter chain picks the innermost match the IPC can actually
160
238
  talk to and falls through to the next on detect failure.
@@ -34,6 +34,7 @@ type Notification struct {
34
34
  Text string // capped at 80 runes
35
35
  Severity string // info | warn | critical
36
36
  Source string // ai | k8s | git | external
37
+ Metadata map[string]string // optional producer metadata
37
38
  CreatedAt time.Time
38
39
  ExpiresAt time.Time
39
40
  Target // session, window, pane, socket
@@ -53,6 +54,16 @@ freshness/display field, not a removal condition. `Severity` and
53
54
  returns `ErrInvalidSeverity` / `ErrInvalidSource` which the CLI maps to
54
55
  exit code 2.
55
56
 
57
+ `Metadata` is optional and is omitted when empty. Hook producers use it for
58
+ routing/debug context such as `agent`, `thread_id`, `turn_id`, `cwd`,
59
+ `model`, and `client`; Claude hook rows also carry event-specific keys such as
60
+ `tool_name`, `tool_input.command`, `error_type`, `subagent_type`, and
61
+ `teammate_name`. Tmux bell fallback rows carry `agent=bell`, `event=bell`,
62
+ and tmux target context such as pane title, command, session, window, pane, and
63
+ socket. `notify list --json` includes this metadata as the structured data
64
+ channel while human table/sidebar output keeps the compact text body. Existing
65
+ entries without metadata remain valid.
66
+
56
67
  ## CLI surface
57
68
 
58
69
  ### push
@@ -72,7 +83,7 @@ number of seconds. `--json` prints `{id, queued}` for scripting.
72
83
  ### list
73
84
 
74
85
  ```
75
- projmux notify list [--live] [--json] [--limit N] [--ui table|sidebar]
86
+ projmux notify list [--live] [--json] [--limit N] [--ui table|sidebar] [--client <tty>]
76
87
  [--severity ...] [--source ...]
77
88
  ```
78
89
 
@@ -87,7 +98,7 @@ acks the row after focus succeeds. `x` acks the selected row. `Ctrl-X` clears
87
98
  all rows via `notify ack --all`. Rows are intentionally compact: the visible
88
99
  label keeps notification text first, then age, project, window, and pane
89
100
  metadata; hidden queue ids remain action values but the sidebar has no search
90
- input.
101
+ input and intentionally does not expose a separate metadata detail view.
91
102
 
92
103
  `--live` adds a non-mutating explanation view that reads
93
104
  `tmux list-panes -a` and compares the queue with live reply-state panes. It
@@ -170,21 +181,42 @@ Manual `projmux attention toggle` on a pane without an agent option
170
181
  does NOT push — the queue is intentionally AI-driven; reconcile honours
171
182
  the same contract.
172
183
 
184
+ ## Producer (tmux bell fallback)
185
+
186
+ `projmux ai ingest bell --pane <pane_id>` is the opt-in fallback producer
187
+ installed by `projmux ai integrate tmux-bell`. It reads the target pane from
188
+ tmux and writes an info/source-ai row with:
189
+
190
+ - id: `ai:bell:<session>:<pane>`
191
+ - text: `bell · <pane title>` with command/window fallback context
192
+ - metadata: `agent=bell`, `event=bell`, pane/session/window/socket fields
193
+ - freshness TTL: 10 minutes
194
+
195
+ Unlike reply-ready reconcile, bell ingest does not require AI pane metadata.
196
+ It is intentionally available for arbitrary CLIs that only signal attention
197
+ through BEL or OSC 9. Repeated bells from the same pane are suppressed for 5
198
+ seconds before a later bell refreshes the stable queue id.
199
+
173
200
  ## Consumer (status-bar click)
174
201
 
175
202
  `internal/app/statusbar.go::handleNotify`. A click on the notify range
176
203
  or the `prefix s n` chord reads the newest queue entry and dispatches:
177
204
 
178
205
  ```
179
- projmux focus --target <target> --source status-bar --kind segment-click [--socket <s>]
206
+ projmux focus --target <target> --source status-bar --kind segment-click [--socket <s>] [--client <tty>]
180
207
  ```
181
208
 
209
+ The status bar passes the clicked tmux client as `--client` when tmux provides
210
+ it. The notify sidebar does the same for row selection. If that origin client
211
+ is no longer attached, `projmux focus` falls back to the existing target-session
212
+ client selection policy.
213
+
182
214
  Outcomes:
183
215
 
184
216
  - **Focus succeeded** — ack the entry. Focus is the single consume path for
185
217
  routed notification clicks.
186
- - **Focus exited 2 (target unresolved)** — keep the entry pending and toast
187
- `notify target gone; ack to clear`.
218
+ - **Focus exited 2 (target unresolved)** — ack the entry and toast
219
+ `notify target gone; cleared`.
188
220
  - **Other failure** — keep the entry, toast `focus failed: <reason>`
189
221
  so the user can retry without losing the row.
190
222
 
@@ -0,0 +1,72 @@
1
+ # Session Restore
2
+
3
+ Session snapshots store tmux layout metadata and replay recipes for shell,
4
+ startup, and supported agent panes. Manual CLI actions remain available through:
5
+
6
+ ```sh
7
+ projmux session-state status [--session <name>]
8
+ projmux session-state save
9
+ projmux session-state preview [--session <name>]
10
+ projmux session-state restore --dry-run [--session <name>]
11
+ projmux session-state delete [--session <name>]
12
+ ```
13
+
14
+ The primary inspection surface is `Projects > Sessions > State`. It shows a
15
+ read-only overview first: latest snapshot status, named snapshots, window ->
16
+ pane structure, cwd, recipe, and agent resume health. Mutation belongs one
17
+ level deeper in explicit actions; the overview does not immediately save,
18
+ delete, preview, or restore.
19
+
20
+ Project Session State actions are scoped to the project-derived session
21
+ identity, not the currently attached tmux session. `Save latest snapshot`
22
+ captures the live project session when that session exists. `Save named
23
+ snapshot` captures the same live project session into a project-local named
24
+ snapshot and stores cwd values with portable project-root placeholders. Closed
25
+ project sessions disable save actions with the live-session reason. `Preview
26
+ restore` prints the read-only dry-run restore plan for the project snapshot, and
27
+ `Delete snapshot` requires a confirmation picker before removing the project
28
+ snapshot. Destructive restore execution remains outside Settings in this slice.
29
+
30
+ Agent panes in Settings and restore dry-run previews show resume metadata
31
+ health next to the pane recipe. `available` means a resume id is present and
32
+ recent enough for replay, `stale` means the stored id exists but the metadata is
33
+ missing or older than the snapshot policy, and `unavailable` means projmux
34
+ cannot safely resume that agent pane from the snapshot. Confidence is derived
35
+ from the metadata source: direct session ids and hook ingest are high
36
+ confidence, transcript/log fallbacks are medium confidence, and missing or
37
+ unknown sources are low or none. The old statusbar Session State shortcut has
38
+ been removed; use `Projects > Sessions > State` or the `projmux session-state`
39
+ CLI for inspection/actions.
40
+
41
+ Settings > Session State is global settings only: global auto-save, auto-save
42
+ interval, and storage/retention policy. It does not show the current
43
+ snapshot tree. Delete for current-session snapshots and destructive restore
44
+ execution stay deferred until those actions have a dedicated safe policy for
45
+ existing or non-empty live sessions.
46
+
47
+ Named snapshots may currently be backed by legacy project files in
48
+ `<project>/.projmux/layouts/*.toml`. They reuse the same window, pane, cwd, and
49
+ startup recipe concepts as session snapshots, but the user-facing restore model
50
+ is still `Latest snapshot`, `Named snapshot`, or `Empty session`. The legacy
51
+ files are imported read-only by Project open
52
+ when building `Named snapshot` candidates; new primary surfaces should describe
53
+ the restore unit as a snapshot, not as a separate layout or preset feature.
54
+
55
+ Project open from the Alt-1 sidebar defaults to opening a closed project as an
56
+ `Empty session`. `Settings > Labs > Sidebar startup picker` is an opt-in toggle;
57
+ when it is on, closed project open advances inside the sidebar to the native
58
+ `Start project` step. Rows are ordered `Latest snapshot`, named snapshot rows,
59
+ `Empty session`, then `Back`. `Latest snapshot` is the auto-saved snapshot that
60
+ keeps changing as auto-save runs. `Named snapshot` is a fixed, user-named
61
+ snapshot and is not updated by auto-save. Rows include saved-at date/time
62
+ metadata when available. `Back` returns to the project list without creating,
63
+ replaying, or opening a session. After the startup mode is selected, project
64
+ hook/config trust is evaluated if needed; approval continues the selected path
65
+ and deny/cancel aborts before session create, snapshot replay, or startup
66
+ command. Existing sessions switch directly without a startup picker.
67
+
68
+ Default `projmux shell` no longer opens a compatibility startup picker and no
69
+ longer accepts startup selector flags for session-state restore. It always
70
+ follows the normal empty attach path after resolving the target app session name
71
+ and startup directory. Use `Settings > Labs > Sidebar startup picker` for
72
+ interactive Latest snapshot / Named snapshot / Empty session selection.
@@ -0,0 +1,66 @@
1
+ # Settings Information Architecture
2
+
3
+ This branch finishes the current Settings/onboarding roadmap slice with a
4
+ view-first layout:
5
+
6
+ - Every Settings surface keeps the Global/Project tab strip visible so nested
7
+ pages still show their scope. Detail pages may render the strip as passive
8
+ context when changing tabs from that nested page would skip an explicit Back
9
+ boundary.
10
+ - Settings uses a view-first rule: overview rows open details; details show the
11
+ current state, source, and expected rendered result before offering mutation
12
+ rows. If a detail opens a dedicated `Change` page, that page is mutation-only
13
+ and does not repeat the same read-only view rows.
14
+ - `Settings > Project Picker > Workdirs` is the list/overview entry. Add/remove
15
+ actions live inside that view.
16
+ - `Settings > Project Picker > Project Root` shows effective and saved values
17
+ first, then the edit actions, then the explanatory hints.
18
+ - `Settings > Keybindings` is the single entry point for keybinding work. The
19
+ page is split into four chips: `Bindings`, `Diagnostic`, `Probe`, and `Init`.
20
+ - `Settings > Notifications` owns notification delivery IA. Desktop notification
21
+ mode, delivery source diagnostics, in-app queue status, and
22
+ `PROJMUX_NOTIFY_HOOK` visibility live together without mixing mutation
23
+ boundaries.
24
+ - `Settings > Notifications > Desktop notifications` owns the desktop
25
+ notification mode. The detail choices are `none`, `notify`, and `raise`.
26
+ - `Settings > Notifications > Delivery sources` shows Codex hooks, Claude, and
27
+ tmux producer diagnostics plus copyable install/remove/dry-run commands.
28
+ Settings copies command text only; it does not install or remove external
29
+ notify wiring. The legacy Codex notify source is intentionally omitted from
30
+ Settings.
31
+ - `Settings > Labs` keeps experimental toggles, but keybindings no longer have a
32
+ visible Labs row. The hidden compatibility action still redirects to the
33
+ unified Keybindings page.
34
+ - `Settings > Labs > Project Hooks` is overview-first. The Labs root opens the
35
+ overview, and the on/off mutation rows live one level deeper.
36
+ - `Settings > AI Settings` is view-first. The root contains `Default split
37
+ mode`; the detail contains the `Claude`, `Codex`, and `Shell` choices.
38
+ - `Settings > Project > Project recipe` is the functional label for
39
+ `.projmux/config.toml`. Search still matches `config.toml` as an alias.
40
+ - `Settings > Project > Project recipe` is view-first. The root contains section
41
+ rows such as `Startup command`, `Kube`, and `Environment`; mutation actions
42
+ such as `Set startup command...`, `Clear startup command`, kube edits, and env
43
+ add/remove rows live inside those details.
44
+ - `Settings > Appearance` is view-first. The root opens `Path icon`, `Git icon`,
45
+ and `Notify icon` details. Each detail shows the current mode and off/symbol/
46
+ emoji rendered previews first, and its `Change` page contains only the
47
+ off/symbol/emoji mutation choices.
48
+
49
+ Hooks remain the reference pattern for this IA:
50
+
51
+ - project-scoped rows stay editable in-app
52
+ - global/system rows are read-only in-app
53
+ - project overrides are created intentionally from the project surface or
54
+ `projmux hook edit <event> --project`
55
+ - `Settings > Project > Hooks` lists hook events only. It does not nest the
56
+ Project recipe row; Project recipe belongs under the Project tab.
57
+ - The Settings hook event list includes `send-noti` for both global and project
58
+ hook views.
59
+
60
+ Shell bootstrap UX is phase-split:
61
+
62
+ - Phase 1 is complete in this branch: `projmux welcome`, the About-screen
63
+ Welcome entry, and `pending_attach_welcome` state make the guide revisit-able.
64
+ - Phase 2 is complete in this branch: the generated projmux shell tmux config
65
+ runs the low-noise `projmux welcome --popup` attach hook, which claims the
66
+ pending marker once and displays the welcome guide after attach.
package/docs/statusbar.md CHANGED
@@ -8,13 +8,17 @@ segment only requires one wiring point.
8
8
  ## Layout
9
9
 
10
10
  ```
11
- row 0 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git>  %H:%M
11
+ row 0 #[range=user|notify] <notify HUD pill> #[norange]
12
+ #[range=user|usage] <usage HUD bar> #[norange]
13
+ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git>  %H:%M
12
14
  └────────── native tmux window list (one entry per window) ──────────┘
13
- row 1 #[range=user|notify] <notify HUD pill> #[norange]
14
- #[range=user|usage] <usage HUD bar> #[norange]
15
15
  ```
16
16
 
17
- - Row 0 keeps tmux's native `window-status-format` so clicking a tab
17
+ - Row 0 splits the line with `#[align=left]` (the pending AI notify
18
+ queue, capped at 80 cells) and `#[align=right]` (usage, capped at 120
19
+ cells). `notify` is the explicit-ack pending queue; live pane attention
20
+ badges are a separate state surface.
21
+ - Row 1 keeps tmux's native `window-status-format` so clicking a tab
18
22
  selects the window. The bind uses `if-shell -F
19
23
  "#{==:#{mouse_status_range},window}"` to run `select-window -t =`
20
24
  natively when the click lands on the window list, so the
@@ -29,18 +33,15 @@ row 1 #[range=user|notify] <notify HUD pill> #[norange]
29
33
  in `#[range=user|<id>]` ranges and dispatched through the projmux
30
34
  handler. The standalone config also wraps the right-side `projmux`
31
35
  badge as the `settings` range; the app config renders a compact
32
- `⚙` settings chip after the clock. The git segment shows the current
36
+ `` settings chip after the clock. The git segment shows the current
33
37
  branch or detached commit,
34
38
  then compact state indicators when available: `*` for local changes,
35
39
  `+N` for staged entries, and `↑N`/`↓N` for ahead/behind counts. Each
36
40
  state token gets its own compact foreground color while preserving the
37
- existing branch block background.
38
- - Row 1 splits the line with `#[align=left]` (the pending AI notify
39
- queue, capped at 80
40
- cells) and `#[align=right]` (usage, capped at 120 cells). `notify` is the
41
- explicit-ack pending queue; live pane attention badges are a separate
42
- state surface. Both
43
- segments degrade gracefully when the cell budget is tight; see
41
+ existing branch block background. Window tab indexes stay left of each tab,
42
+ and tab titles are centered in a fixed-width trim so long active pane names
43
+ do not resize the status row.
44
+ - Both HUD segments degrade gracefully when the cell budget is tight; see
44
45
  [notify-queue.md](notify-queue.md) and [usage-tracking.md](usage-tracking.md)
45
46
  for the per-segment tier ladder.
46
47
 
@@ -65,7 +66,7 @@ bind-key -n MouseDown1Status if-shell -F "#{==:#{mouse_status_range},window}" \
65
66
  | `git` | 0 | `projmux tmux popup-toggle sessionizer` | `prefix s g` |
66
67
  | `settings` | 0 | `projmux tmux popup-toggle --client <tty> ai-split-settings` | mouse only; `prefix s s` remains `session` |
67
68
  | `usage` | 1 | show a native-framed usage HUD popup from cached usage state | `prefix s u` |
68
- | `notify` | 1 | `projmux focus --target <newest> --source status-bar --kind segment-click`, then ack on focus success | `prefix s n` |
69
+ | `notify` | 1 | `projmux focus --target <newest> --source status-bar --kind segment-click [--client <tty>]`, then ack on focus success | `prefix s n` |
69
70
 
70
71
  `notify` reads the pending queue only. For a live pane-state view that is
71
72
  independent of queued reminders, use `projmux attention list`. To explain why
@@ -79,6 +80,9 @@ the cached usage state in-process, keeps the existing `projmux usage` CLI
79
80
  output shape unchanged for external consumers, aligns model/window rows with
80
81
  right-aligned numeric values, dims unavailable values, and colors rows at the
81
82
  same alert thresholds as the popup: amber at 80% and red at 95%.
83
+ Session State inspection lives under `Projects > Sessions > State`; global
84
+ Settings > Session State is settings-only and the statusbar no longer exposes a
85
+ duplicate State button.
82
86
 
83
87
  The path popup uses the native picker frame chrome, a one-line title,
84
88
  the full wrapped current path, cheap project/git metadata when available, and
@@ -89,12 +93,10 @@ does not leave terminal key state behind. The usage popup uses the same
89
93
  single-payload print and plain Enter-close pattern. It shows the authoritative
90
94
  last collect timestamp when present, falls back to the cache file mtime when
91
95
  needed, and colors that sync line amber once it is more than 60 seconds old.
92
- The notification HUD detail surface
93
- (`Alt-2` / `User2`) opens the
94
- right-side notification popup with newest-first rows. The popup itself is
95
- untitled; when decoration mode is `symbol` or `emoji`, the bell appears before
96
- the fzf header text instead. Selecting a row still focuses and acknowledges
97
- that notification.
96
+ The notification HUD detail surface (`Alt-2` / `User2`) opens the right-side
97
+ notification popup with newest-first rows and an amber title. When notification
98
+ icon decoration is `symbol` or `emoji`, the bell appears before the title text.
99
+ Selecting a row still focuses and acknowledges that notification.
98
100
 
99
101
  Empty `#{mouse_status_range}` (a click on whitespace) falls through to
100
102
  `select-window -t @<mouse_window>` when `--mouse-window` is non-empty,
@@ -126,7 +128,7 @@ click. Each handler therefore swallows runtime failures and surfaces
126
128
  them as `display-message` toasts:
127
129
 
128
130
  - `notify` click whose focus dispatch exits 2 (target unresolved):
129
- keep the entry pending, toast `notify target gone; ack to clear`.
131
+ ack the entry, toast `notify target gone; cleared`.
130
132
  - Any other focus failure: keep the entry, toast `focus failed:
131
133
  <reason>`.
132
134
  - `session`, `kube`, or `git` popup launch failure: toast
@@ -151,12 +153,16 @@ emits a deterministic block per segment), regenerate, and re-apply:
151
153
  projmux tmux apply
152
154
  ```
153
155
 
154
- Settings > Appearance controls the optional decoration mode used by
155
- the path, git branch, and notification sidebar header. The persisted enum lives at
156
- `~/.config/projmux/statusbar-decoration`; valid values are `off` (default,
157
- font-safe), `symbol` (Nerd Font-style folder/GitHub/bell icons), and `emoji`.
158
- Settings also updates tmux `@projmux_statusbar_decoration` for the live
159
- server when run inside tmux.
156
+ Settings > Appearance controls optional icon decoration separately for the
157
+ path/cwd marker, git branch marker, and notification sidebar header. Each row
158
+ can be `off` (default, font-safe), `symbol` (Nerd Font-style folder,
159
+ git-provider, or bell icon), or `emoji`. Git branch decoration follows
160
+ `remote.origin.url`: GitHub remotes use a cat-style mark, GitLab remotes use a
161
+ fox-style mark, and other remotes use a generic git branch mark. Settings also
162
+ updates the matching live tmux option
163
+ (`@projmux_statusbar_decoration_cwd`, `_git`, or `_notify`) when run inside
164
+ tmux. The legacy `~/.config/projmux/statusbar-decoration` and
165
+ `@projmux_statusbar_decoration` remain fallback defaults for older configs.
160
166
 
161
167
  To add a new clickable segment:
162
168
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projmux",
3
- "version": "0.5.3",
3
+ "version": "0.6.1",
4
4
  "description": "tmux project session manager",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/crevissepartners/projmux#readme",
@@ -24,10 +24,10 @@
24
24
  "LICENSE"
25
25
  ],
26
26
  "optionalDependencies": {
27
- "@projmux/darwin-arm64": "0.5.3",
28
- "@projmux/darwin-x64": "0.5.3",
29
- "@projmux/linux-arm64": "0.5.3",
30
- "@projmux/linux-x64": "0.5.3"
27
+ "@projmux/darwin-arm64": "0.6.1",
28
+ "@projmux/darwin-x64": "0.6.1",
29
+ "@projmux/linux-arm64": "0.6.1",
30
+ "@projmux/linux-x64": "0.6.1"
31
31
  },
32
32
  "scripts": {
33
33
  "package:npm": "scripts/package-npm.sh",