projmux 0.6.6 → 0.7.0

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.
@@ -87,14 +87,15 @@ language.
87
87
  ## Keymap File
88
88
 
89
89
  Settings > Keybindings is the normal in-app editor for action keys. It lists
90
- actions with current keybinding summaries, opens a simple detail screen, and
91
- offers `Add alias` plus reset. Saving writes safe tmux plain chords to
92
- `~/.config/projmux/keymap.toml`, rewrites `~/.config/projmux/tmux.conf`, and,
93
- when Settings is running inside tmux, sources that app config so tmux-level
94
- chords take effect immediately.
95
-
96
- Raw sequences that cannot be safely represented as a tmux plain chord are not
97
- persisted. Use Settings to save a safe direct alias. When key delivery needs
90
+ actions with the current active keys and one state: Default, Custom, Available,
91
+ or Unbound. Open an action to see the action label, state, a flat Keys list,
92
+ Options, and a collapsed Troubleshooting row. Create flows start from
93
+ `+ Add key`, deletion lives under each key's detail, and action-level state
94
+ changes such as Unbind or Reset live under Options. Successful saves report the
95
+ keybinding as saved; failures identify the stage that failed.
96
+
97
+ Raw sequences that cannot be safely represented as a direct keybinding are not
98
+ persisted. Use Settings to save a custom key. When key delivery needs
98
99
  terminal-layer remediation, first try the key in `projmux shell`, then run
99
100
  `projmux setup` from the raw terminal, then use `projmux init` for supported
100
101
  terminal adapters.
@@ -127,7 +128,13 @@ do not break. Settings preserves existing prefix entries when rewriting the
127
128
  file, but does not create new prefix keys, and generated tmux config no longer
128
129
  binds the old action prefix chords.
129
130
 
130
- Use an empty `keys` list to disable direct plain aliases for the action when
131
+ Settings > Keybindings names `AISplitPickerToggle` as the AI split popup
132
+ picker toggle. That action opens or closes the picker UI and is separate from
133
+ the direct AI pane actions, `ai-split-right` and `ai-split-down`. The direct
134
+ actions create a new managed AI pane each time they run, leaving existing AI
135
+ panes in place. `right` and `down` choose where the new pane is created.
136
+
137
+ Use an empty `keys` list to disable direct plain keys for the action when
131
138
  editing the file by hand:
132
139
 
133
140
  ```toml
@@ -151,7 +158,7 @@ The file currently affects generated tmux config from `projmux tmux
151
158
  print-config`, `projmux tmux install`, `projmux tmux print-app-config`,
152
159
  `projmux tmux install-app`, and `projmux shell`. Terminal init adapters such as
153
160
  Ghostty and Windows Terminal install built-in plain-byte mappings where needed;
154
- they do not read `keymap.toml` or copy saved aliases into terminal configs.
161
+ they do not read `keymap.toml` or copy saved keys into terminal configs.
155
162
  Changing terminal-layer mappings still requires rerunning `projmux init` and
156
163
  restarting the terminal where that terminal requires it.
157
164
 
@@ -183,6 +190,14 @@ picker frames also apply the built-in fallback `background`/`foreground` tokens
183
190
  so picker-owned padding, empty rows, footer rows, and preview gaps do not
184
191
  inherit the terminal default background.
185
192
 
193
+ Native picker popups launched through `projmux tmux popup-toggle` also pass a
194
+ per-popup tmux 3.4 `display-popup -s` body style using the effective theme
195
+ `background`/`foreground` tmux tokens. This styles only the tmux popup body
196
+ before the native renderer draws. It does not set global `popup-style` or
197
+ `popup-border-style`, and it does not change shell pane backgrounds,
198
+ `default-style`, `window-style`, OSC terminal backgrounds, or the general
199
+ status/window palette.
200
+
186
201
  Resolver schema shape:
187
202
 
188
203
  ```toml
@@ -273,11 +288,12 @@ user/global preference in this release.
273
288
  | `PROJMUX_FOCUS_DEBUG` | When non-empty, `projmux focus` prints one telemetry line to stderr. |
274
289
  | `PROJMUX_PICKER_BACKEND` | Legacy picker backend override. Any value, including old `fzf` settings, now resolves to the native picker. |
275
290
  | `PROJMUX_INSTALLER` | Installer source hint used by update flows. npm installs set this automatically; advanced release installs can set `github-release`. |
291
+ | `PROJMUX_SHELL_UPDATE_CHECK_TIMEOUT_MS` | Timeout in milliseconds for the best-effort release check attempted by `projmux shell` when the update cache is missing or stale. Invalid, zero, or negative values use the default. |
276
292
 
277
293
  ## Welcome State
278
294
 
279
- `projmux shell` stores per-version welcome state under the projmux state
280
- directory, normally:
295
+ `projmux shell` still reads and rewrites legacy per-version welcome state under
296
+ the projmux state directory for attach-popup compatibility, normally:
281
297
 
282
298
  ```text
283
299
  ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/welcomed-v<version>.json
@@ -295,11 +311,21 @@ The current schema is:
295
311
  }
296
312
  ```
297
313
 
298
- `skip_version` is the only field that suppresses the shell welcome. When it
299
- matches the current projmux version, `projmux shell` skips the welcome. When it
300
- is absent or names a different version, the welcome is shown again. Older state
301
- files that contain only `last_welcomed_version` remain readable, but that field
302
- does not count as a skip.
314
+ This file no longer suppresses the shell-entry welcome. `skip_version` and
315
+ `last_welcomed_version` remain readable for legacy state and pending attach
316
+ popup compatibility, but the automatic shell prompt now uses release skip state
317
+ instead.
318
+
319
+ Update prompt skips are stored by latest release tag under the update cache
320
+ directory:
321
+
322
+ ```text
323
+ ${XDG_CACHE_HOME:-$HOME/.cache}/projmux/update-skip.json
324
+ ```
325
+
326
+ When `tag_name` matches the fresh cached latest release tag, `projmux shell`
327
+ continues without offering the update actions. A newer latest tag makes the
328
+ prompt eligible again.
303
329
 
304
330
  Example:
305
331
 
@@ -401,7 +427,9 @@ only a generic in-app queue/sidebar/statusbar row; it does not fire OS toast,
401
427
  The OS-level dispatch carries three modes. The in-app notify queue, the
402
428
  statusbar segment, and the attention badge stay live regardless of which
403
429
  mode is active — only the toast / notify-send / auto-raise fan-out is
404
- gated here.
430
+ gated here. The same mode also gates `projmux focus` post-switch osfocus
431
+ dispatch: only `raise` asks the host terminal to come forward after a
432
+ successful tmux focus.
405
433
 
406
434
  | Mode | On push | On click |
407
435
  | --- | --- | --- |
@@ -413,7 +441,8 @@ Click activation is wired only for `raise`. The `projmux://` URI handler is
413
441
  registered on the first `raise` Notify of each tmux server (gated by the
414
442
  `@projmux_uri_protocol_registered_v6` marker). The
415
443
  mode only controls whether a toast fires at all and whether to follow it
416
- up with an on-push auto-raise.
444
+ up with an on-push auto-raise. `off` / `none` and `notify` also suppress
445
+ the focus-triggered osfocus raise after `projmux focus`.
417
446
 
418
447
  Resolution order (highest priority first):
419
448
 
@@ -551,8 +580,8 @@ while `on` and `off` take precedence. Auto-save only updates the latest
551
580
  snapshot. Named snapshots are manual and are never updated by auto-save.
552
581
 
553
582
  Project open from the Alt-1 sidebar defaults to opening a closed project as an
554
- `Empty session`. The optional `Settings > Labs > Sidebar startup picker` toggle
555
- enables the native sidebar `Start project` step. Rows appear as `Latest
583
+ `Empty session`. The optional `Settings > Session State > Sidebar startup
584
+ picker` toggle enables the native sidebar `Start project` step. Rows appear as `Latest
556
585
  snapshot`, named snapshot rows, `Empty session`, and `Back`. `Latest snapshot`
557
586
  is the snapshot auto-save that changes as auto-save runs; named snapshots are
558
587
  fixed snapshots. Rows include saved-at date/time metadata when projmux can
@@ -571,7 +600,7 @@ Default `projmux shell` no longer opens a startup picker or replays session-stat
571
600
  snapshots before attach. It still derives the default app session identity and
572
601
  startup directory from the current project context when available; otherwise it
573
602
  uses the `home` target and home directory. Session-state restore selection is
574
- limited to the Labs sidebar startup picker.
603
+ limited to the Session State sidebar startup picker.
575
604
 
576
605
  Settings > Session State is global settings only: global auto-save, auto-save
577
606
  interval, and storage/retention policy. Settings > Project > Session State
package/docs/hooks.md CHANGED
@@ -606,6 +606,39 @@ Codex and are managed from `Settings > Notifications > Hook quiet policy`.
606
606
  They only affect ingest delivery; `projmux ai integrate claude` still uses the
607
607
  catalog `install` field for installed hook events.
608
608
 
609
+ ## Antigravity Hook Ingest
610
+
611
+ `projmux ai ingest antigravity-hook < payload.json` is available for manual
612
+ Antigravity CLI `agy` hook/statusline payloads. Projmux does not provide
613
+ `projmux ai integrate antigravity`, does not install Antigravity hooks, and
614
+ does not mutate Antigravity user config. The Delivery sources diagnostic
615
+ therefore reports Antigravity as a read-only unsupported/manual row. If users
616
+ wire it by hand, use an absolute `projmux` command path or a known cwd because
617
+ relative command paths failed the Phase 0b smoke.
618
+
619
+ The default known Antigravity catalog records only observed Phase 0b signals
620
+ and marks them `install: false`:
621
+
622
+ | Event/signal | Behavior |
623
+ | --- | --- |
624
+ | `PostInvocation` | marks the matched pane hook-active and writes a quiet ingest diagnostic; no notify queue entry is pushed |
625
+ | `Stop` | pushes a completion row, or a critical error row when `error` is present or `terminationReason` is non-normal |
626
+ | `Statusline` with `tool_confirmation_pending=true` | pushes a critical approval row; this is only active when a statusline/manual status payload is wired to ingest |
627
+ | `Statusline` without `tool_confirmation_pending=true` | marks the matched pane hook-active and writes a quiet ingest diagnostic |
628
+ | unknown events | mark the matched pane hook-active and write quiet ingest diagnostics only |
629
+
630
+ Antigravity notify rows use `agent=antigravity` metadata. Accepted fields
631
+ include `conversationId`/`conversation_id`, `cwd`, `workspace.path`,
632
+ `transcriptPath`, `terminationReason`, `error`, `fullyIdle`, `agent_state`,
633
+ `context_window`, and nested `statusline.tool_confirmation_pending`.
634
+ Antigravity ingest uses `conversationId` as pane thread metadata for matching
635
+ and as session-state resume metadata. Session restore uses
636
+ `agy --conversation <uuid>` only when that id is present and UUID-shaped;
637
+ otherwise preview and doctor render `resume unavailable`. Antigravity usage
638
+ quota HUD support remains unsupported because the stable usage signal is
639
+ `context-window-only` statusline data, not 5-hour/weekly quota data. Raw
640
+ payloads or transcript contents are not stored.
641
+
609
642
  ## Ingest Debug Log
610
643
 
611
644
  Every `projmux ai ingest ...` path appends compact JSONL diagnostics to
package/docs/install.md CHANGED
@@ -28,14 +28,14 @@ projmux shell
28
28
  ```
29
29
 
30
30
  Each `projmux shell` launch prints a short welcome with the current version,
31
- detach/exit keys, core app shortcuts, and cached update status when available.
32
- Press Enter to continue for this run, or press `s` to skip the welcome for the
33
- current projmux version. The next projmux version shows the welcome again.
34
-
35
- If an installer-supported update is available, the same prompt keeps update
36
- actions separate from welcome skip: press `u` to run `projmux update apply`,
37
- `n` to print the manual update command, or `d` to skip daily update prompts for
38
- that release.
31
+ detach/exit keys, a bootstrap reminder, and cached release status when
32
+ available. Press Enter to continue into the shell.
33
+
34
+ If an update is available, the same shell-entry prompt uses one action
35
+ vocabulary: Enter continues, `u` upgrades by invoking `projmux update apply`,
36
+ and `s` skips that latest release tag until a newer tag appears. For `source`
37
+ or unknown installer sources, `u` prints installer guidance and then continues
38
+ shell entry.
39
39
 
40
40
  To revisit the guide later, run `projmux welcome`, or use Settings > About >
41
41
  Welcome inside the app to open it in a visible viewer. Set `PROJMUX_WELCOME=off` before
@@ -4,7 +4,7 @@ projmux is keyboard-driven, but the guaranteed launch contract is small:
4
4
  fresh installs bind `Alt-1` through `Alt-5` as plain Meta sequences
5
5
  (`M-1`..`M-5`, bytes `\x1b1`..`\x1b5`). Other actions remain discoverable in
6
6
  Settings > Keybindings. Transport-dependent actions keep their built-in
7
- transport default key, and Settings can add separate safe tmux plain aliases to
7
+ transport default key, and Settings can add separate safe tmux plain keys to
8
8
  the same action. They are not installed as terminal-specific User-key
9
9
  fallbacks. `UserN` and `CSI-u` are legacy/removal/unsupported targets, not
10
10
  supported fallback guidance.
@@ -15,14 +15,13 @@ The recommended path when a key does not fire:
15
15
  2. Run `projmux setup` outside tmux to see which bytes reach the process.
16
16
  3. For supported terminals, preview `projmux init [terminal]`; add `--apply`
17
17
  only after reviewing the merge.
18
- 4. For unsupported terminals, configure plain Meta bytes or add a tmux alias in
18
+ 4. For unsupported terminals, configure plain Meta bytes or add a custom key in
19
19
  Settings > Keybindings.
20
20
 
21
- Settings writes safe tmux plain chords to
22
- `~/.config/projmux/keymap.toml`, regenerates
23
- `~/.config/projmux/tmux.conf`, and hot-reloads the live tmux config when it is
24
- running inside tmux. Raw escape payloads, Windows Terminal `sendInput` strings,
25
- and tmux User keys are rejected as aliases.
21
+ Settings saves safe tmux plain chords for actions. Successful saves use simple
22
+ confirmation copy; if saving, runtime config update, or active tmux reload
23
+ fails, the error names the failed stage. Raw escape payloads, Windows Terminal
24
+ `sendInput` strings, and tmux User keys are rejected as action keys.
26
25
 
27
26
  ## Quick Start
28
27
 
@@ -32,10 +31,14 @@ These shortcuts are the guaranteed launch defaults. They need no tmux prefix.
32
31
  | --- | --- |
33
32
  | `Alt-1` | Project sidebar |
34
33
  | `Alt-2` | Notify sidebar |
35
- | `Alt-3` | Existing session popup |
36
- | `Alt-4` | AI split picker |
34
+ | `Alt-3` | Recent Windows |
35
+ | `Alt-4` | AI split popup picker |
37
36
  | `Alt-5` | Settings |
38
37
 
38
+ `RecentWindows:Open` opens the cross-project recent windows queue. It switches
39
+ to the selected live tmux window using that window's current active pane; it is
40
+ separate from `last-pane` and from the existing-session popup.
41
+
39
42
  The tmux prefix remains the upstream default `Ctrl-b`. Inside a running
40
43
  session, `Ctrl-b ?` lists the live tmux bindings.
41
44
 
@@ -43,43 +46,60 @@ session, `Ctrl-b ?` lists the live tmux bindings.
43
46
 
44
47
  Settings > Keybindings lists the full action catalogue. In particular, sidebar
45
48
  keymap actions, pane switching, window switching, and rename actions remain
46
- visible. Transport-dependent rows show the default transport key separately
47
- from editable plain aliases.
48
-
49
- The Settings flow is intentionally simple: the root is one action list with
50
- current key summaries, and each action detail shows the action, current
51
- keybinding/aliases, `Add alias`, and reset. Diagnostic/probe/init workflows are
52
- not first-class Settings tabs; use `projmux setup` and `projmux init` from the
53
- terminal when key delivery needs remediation.
54
-
55
- Optional direct aliases can be added for actions such as:
49
+ visible.
50
+
51
+ The Settings flow is intentionally simple: the root is one action list with a
52
+ key summary and state. The key summary uses the first key plus `+N`, or
53
+ `Not bound` when no key is active. State vocabulary is limited to Default,
54
+ Custom, Available, and Unbound. Each action detail shows the action label,
55
+ state, a flat Keys list with `+ Add key`, Options, and a collapsed
56
+ Troubleshooting row with Test key delivery and Advanced... entry points. Key
57
+ rows open key detail for Remove key and Test key. Add key opens Press a key by
58
+ default; Enter key name and raw diagnostics live under Advanced. Options covers
59
+ unbinding the action and reset/use-default flows. Diagnostic/probe/init
60
+ workflows are not first-class Settings tabs; use `projmux setup` and
61
+ `projmux init` from the terminal when key delivery needs remediation.
62
+
63
+ Optional direct keys can be added for actions such as:
56
64
 
57
65
  | Canonical action | Meaning |
58
66
  | --- | --- |
67
+ | `RecentWindows:Open` | Recent windows queue across projects |
59
68
  | `ProjectSwitcherToggle` | Project switcher popup |
69
+ | `AISplitPickerToggle` | AI split popup picker; pressing again closes the picker popup |
70
+ | `ai-split-right` | Open a new direct AI split to the right |
71
+ | `ai-split-down` | Open a new direct AI split below |
60
72
  | `new-window` | New tmux window in the current pane directory |
61
73
  | `rename-window` | Rename the current tmux window |
62
74
 
75
+ `AISplitPickerToggle` is the `Alt-4` popup picker toggle. It opens or closes
76
+ the picker UI where the user chooses the AI split mode. It is separate from
77
+ the direct `ai-split-right` and `ai-split-down` actions.
78
+
79
+ The direct AI split actions create a new managed AI pane each time they run.
80
+ Existing AI panes are left in place; the requested direction controls where the
81
+ new pane is created.
82
+
63
83
  Pane switching is catalogued as transport-dependent and the generated app tmux
64
84
  config binds `M-Left`, `M-Right`, `M-Up`, and `M-Down` to `select-pane`
65
85
  movement. Previous/next window remain transport-dependent and the generated app
66
86
  tmux config binds `M-S-Left` / `M-S-Right` to the tmux window navigation
67
87
  commands. These default transport keys are always rendered by projmux, because
68
88
  delivery still depends on the terminal forwarding the modifier-arrow sequence.
69
- Settings can add extra safe plain aliases, such as `M-[` for
70
- `previous-window`; those aliases are saved to `keymap.toml` as `keys = [...]`
89
+ Settings can add extra safe plain keys, such as `M-[` for
90
+ `previous-window`; those keys are saved to `keymap.toml` as `keys = [...]`
71
91
  without storing or replacing the transport default. Rename actions no longer
72
92
  have a built-in terminal fallback; use tmux's prefix rename flow or configure
73
- an explicit safe alias where the action is editable.
93
+ an explicit safe key where the action is editable.
74
94
 
75
95
  ## Product Requirements
76
96
 
77
97
  Settings > Keybindings stays a discovery surface. It must continue to expose
78
98
  launch toggles, sidebar keymap actions, picker-local actions, pane switching,
79
- window switching, and rename actions. The primary Settings flow is not the
80
- terminal remediation surface: replace-primary, disable-default, typed fallback,
81
- terminal mapping preview/apply, and init execution rows stay out of the action
82
- detail.
99
+ window switching, and rename actions. The basic Settings flow is not the
100
+ terminal remediation surface: key-role replacement, disable-default, typed
101
+ fallback, terminal mapping preview/apply, and init execution rows stay out of
102
+ the action detail.
83
103
 
84
104
  The product model does not support `UserN` or `CSI-u` as fallback guidance.
85
105
  Windows Terminal and Ghostty adapters use built-in plain Meta/control bytes or
@@ -99,10 +119,14 @@ by different picker surfaces, while conflicts inside one surface are rejected.
99
119
  | `SessionPopup:KillSession` | Kill the focused session |
100
120
  | `SessionPopup:CyclePreviewWindowPrev` / `SessionPopup:CyclePreviewWindowNext` | Preview windows |
101
121
  | `SessionPopup:CyclePreviewPanePrev` / `SessionPopup:CyclePreviewPaneNext` | Preview panes |
102
- | `NotifySidebar:Ack` / `NotifySidebar:ClearNonCritical` / `NotifySidebar:ClearAll` | Manage notifications |
122
+ | `NotifySidebar:Ack` / `NotifySidebar:AckGroup` / `NotifySidebar:ClearNonCritical` / `NotifySidebar:ClearAll` | Manage notifications |
123
+
124
+ Notify sidebar Right/Left child-row show/hide behavior is picker-local and is
125
+ not part of the Settings action catalog. `NotifySidebar:AckGroup` defaults to
126
+ uppercase `A`, distinct from `NotifySidebar:Ack` on lowercase `a`.
103
127
 
104
- Runtime picker footers render key guides from the merged keymap, preferring the
105
- default alias when it is present and otherwise using the first configured alias.
128
+ Runtime picker footers render key guides from the merged keymap, using the
129
+ first active key as the representative key.
106
130
 
107
131
  ## Keymap File
108
132
 
@@ -132,12 +156,14 @@ Legacy popup IDs such as `sessionizer-sidebar`, `notify-sidebar`,
132
156
  `session-popup`, `ai-split-picker-right`, `ai-split-settings`, and
133
157
  `sessionizer` still read. Settings and new docs show the canonical toggle
134
158
  names: `ProjectSidebarToggle`, `NotifySidebarToggle`, `SessionPopupToggle`,
135
- `AISplitPickerToggle`, `SettingsToggle`, and `ProjectSwitcherToggle`.
159
+ `AISplitPickerToggle`, `SettingsToggle`, and `ProjectSwitcherToggle`. Direct
160
+ AI split actions keep their command IDs, `ai-split-right` and `ai-split-down`,
161
+ so Settings can distinguish them from the `Alt-4` popup picker toggle.
136
162
 
137
163
  ## Diagnose: `projmux setup`
138
164
 
139
165
  Run `projmux setup` outside tmux to find out which projmux keys reach the raw
140
- terminal. Settings > Keybindings remains the action/alias editor; setup is the
166
+ terminal. Settings > Keybindings remains the action-key editor; setup is the
141
167
  terminal delivery diagnostic.
142
168
 
143
169
  | Status | Meaning |
@@ -171,7 +197,7 @@ projmux init --allow-symlink
171
197
 
172
198
  The merge is idempotent: matching bindings are no-ops, missing bindings are
173
199
  added, and keys already mapped to a different user action are skipped with a
174
- warning. `projmux init` does not read `keymap.toml`; direct tmux aliases still
200
+ warning. `projmux init` does not read `keymap.toml`; direct tmux keys still
175
201
  belong in Settings > Keybindings or the keymap file.
176
202
 
177
203
  ### Ghostty
@@ -206,7 +232,7 @@ previous/next window:
206
232
  "actions": [
207
233
  { "command": { "action": "sendInput", "input": "\u001b1" }, "id": "User.projmuxSidebar" },
208
234
  { "command": { "action": "sendInput", "input": "\u001b2" }, "id": "User.projmuxNotifySidebar" },
209
- { "command": { "action": "sendInput", "input": "\u001b3" }, "id": "User.projmuxSessions" },
235
+ { "command": { "action": "sendInput", "input": "\u001b3" }, "id": "User.projmuxRecentWindows" },
210
236
  { "command": { "action": "sendInput", "input": "\u001b4" }, "id": "User.projmuxAIPicker" },
211
237
  { "command": { "action": "sendInput", "input": "\u001b5" }, "id": "User.projmuxSettings" },
212
238
  { "command": { "action": "sendInput", "input": "\u0002r" }, "id": "User.projmuxAISplitRight" },
@@ -112,7 +112,7 @@ The fzf compatibility surface for the native engine is tracked in
112
112
  the top border off screen.
113
113
  - Native preview panes normalize tabs and control bytes before horizontal
114
114
  clipping, preventing long preview rows from wrapping and consuming extra
115
- vertical viewport rows in Alt-3/session popups.
115
+ vertical viewport rows in session popups.
116
116
  - Native sidebar list scrollbars use the fixed list viewport as their track and
117
117
  measure multi-line cards in rendered rows, so the thumb does not shrink or
118
118
  jump when card heights vary.
@@ -179,8 +179,9 @@ Manual UX checks for the Docker sandbox:
179
179
  - Alt-1 opens with the top border/title visible, not clipped.
180
180
  - Vertical borders stay continuous while moving Up/Down.
181
181
  - Alt-1 closes the sidebar immediately when pressed again.
182
- - Alt-2, Alt-3, Alt-4, and Alt-5 open their matching native popups and close
182
+ - Alt-2, Alt-4, and Alt-5 open their matching native popups and close
183
183
  on the same Alt key immediately.
184
+ - Alt-3 opens Recent Windows.
184
185
  - Arrow keys move selection without leaking `^[[` text into the query.
185
186
 
186
187
  `fzf` is intentionally not installed in the image.
@@ -32,6 +32,7 @@ native picker engine and is not a public dependency-policy change.
32
32
  | close `--bind key:abort` | Esc, Ctrl-C, Alt-N, Ctrl-Alt-S variants | Covered | `CloseActions`; `TestNativeRunnerUsesSharedCloseActions` |
33
33
  | terminal modified-key encoding | legacy parser fixtures, Ghostty/kitty-style modified keys | Covered | native handles app-specific parser fixtures, generic modified letters/digits, and non-text keys such as Enter/Esc/Backspace/Tab; this is backend parity, not product fallback guidance; `TestNativeInteractiveSupportsCSIuAppKeyBindings` |
34
34
  | `execute-silent(...)+refresh-preview` | switch/session preview cycling | Covered for command execution and rerender loop | `pickercompat.PickerOptions`/`pickercompat.OptionsFromPicker`; `TestNativeInteractiveRunsCustomActionCommandAndRefreshes`; `TestPickerOptionsMapsCompatBindingsToContractActions`; Docker no-fzf e2e sends `Right` and `Alt-Down` before selection |
35
+ | action-local/event-backed mutable refresh | notify sidebar `a` ack and `x` non-critical clear; notify sidebar queue-write event refresh; switch sidebar `Ctrl-X` kill | Covered for in-session row/live-state refresh without picker restart | `picker.Action.Mutate` returns a `DeferredUpdate`, notify queue-write events trigger the same `DeferredUpdate` path after an event arrives, and both reuse the native frame diff renderer plus value-then-clamp selection preservation; `TestNativeInteractiveCustomActionMutatesItemsAndRefreshes`; `TestNativeInteractiveCustomActionRefreshPreservesSelectedValue`; `TestNativeInteractiveDeferredUpdateTriggerRefreshesRepeatedly`; notify sidebar app tests assert one picker invocation with refreshed rows/live state and event subscription; switch sidebar kill app tests assert one native picker invocation with refreshed rows/preview and previous-live-session guard preservation |
35
36
  | `focus:execute-silent(...)` | switch sidebar focus | Covered | native renders the selection frame diff before running sidebar focus commands so movement stays visible before tmux focus side effects; `runNativeFocusAction`; `TestNativeInteractiveRunsFocusActionOnSelectionChange` |
36
37
  | `start:pos(N)` | switch sidebar initial row | Covered | `pickercompat.PickerOptions`/`pickercompat.OptionsFromPicker`; `TestPickerOptionsFromCompatPickerMapsStartPosToInitialIndex`; `TestPickerOptionsMapsCompatBindingsToContractActions` |
37
38
  | `--preview` | switch, sessions | Covered by command output | `nativePreviewLines`; `TestNativeInteractiveRendersSelectedPreview` |
@@ -47,7 +48,7 @@ native picker engine and is not a public dependency-policy change.
47
48
  | alternate-screen lifecycle | fzf fullscreen picker screen restore | Covered | native frame updates and screen exit return to column 0 before terminal control sequences, screen exit resets styles plus clears the alternate buffer from the home cursor before restore, and real TTY restores get a short settle window before caller handoff; `nativeScreenEnter`; `TestNativeInteractiveUsesAlternateScreen`; `TestRenderFullFrameUpdateAlwaysHomesAndWritesFrame` |
48
49
  | frame content width | fzf border inner width | Covered | `ContentLayout` uses the frame inner width so separators and rows reach the right border; `TestRendererContentLayoutUsesFrameInnerWidth` |
49
50
  | picker-owned app background | native picker frame interior | Covered for renderer-owned cells | `ThemeFromEffective` applies built-in fallback and explicit effective background/foreground SGR to the native frame, and frame rows resume the app style after embedded resets so content padding, empty no-footer rows, footer rows, scrollbars, and preview gaps do not leak terminal default background; `TestThemeFromEffectiveFallbackPaintsFrameBackground`; `TestRendererFrameBackgroundResumesAfterContentResetBeforePadding`; `TestNativeInteractiveNoFooterBlankRowsUseThemeBackground`; `TestNativeInteractiveSplitPreviewGapsUseThemeBackground`; `TestNativeInteractiveSettingsAIBadgeStyleLongPreviewClampsFrameRows` |
50
- | tmux popup frame interaction | native picker popups launched through `popup-toggle` | Covered for native backend popups | `popup-toggle` passes tmux `display-popup -B` when `PROJMUX_PICKER_BACKEND=native`, so the native picker owns the visible frame instead of double-drawing with the tmux popup border; native Alt-1 uses a compact native-only minimum while fzf keeps the previous project sidebar minimum; Alt-1/Alt-2 sidebar heights reserve two bottom statusbar rows; Alt-2 notify sidebar keeps the fzf-like `24%` / min `64` baseline; `TestAppRunTmuxPopupToggleUsesBorderlessPopupForNativeBackend`; `TestSessionizerSidebarWidthKeepsFZFMinimum`; `TestSidebarPopupHeightLeavesStatusbarRows`; `TestNotifySidebarWidthMatchesFZFBaseline`; `TestAppRunTmuxPopupToggleKeepsNotifySidebarFZFSizingForNative` |
51
+ | tmux popup frame interaction | native picker popups launched through `popup-toggle` | Covered for native backend popups | `popup-toggle` passes tmux `display-popup -B` when `PROJMUX_PICKER_BACKEND=native`, so the native picker owns the visible frame instead of double-drawing with the tmux popup border; tmux 3.4 per-command `display-popup -s` is used for the popup body style, setting `bg`/`fg` from the same effective theme tmux background/foreground tokens so tmux's blank/pre-draw popup body matches the native picker surface before the app renderer paints; no global `popup-style`, `popup-border-style`, shell pane background, `default-style`, `window-style`, OSC background, or status/window palette options are changed; native Alt-1 uses a compact native-only minimum while fzf keeps the previous project sidebar minimum; Alt-1/Alt-2 sidebar heights reserve two bottom statusbar rows; Alt-2 notify sidebar keeps the fzf-like `24%` / min `64` baseline; `TestAppRunTmuxPopupToggleUsesBorderlessPopupForNativeBackend`; `TestAppRunTmuxPopupToggleUsesNativePopupBodyStyleFromEffectiveTheme`; `TestBuildPopupToggleWithPickerBackendStylesNativeOnly`; `TestBuildDisplayPopupArgsAddsBodyStyle`; `TestSessionizerSidebarWidthKeepsFZFMinimum`; `TestSidebarPopupHeightLeavesStatusbarRows`; `TestNotifySidebarWidthMatchesFZFBaseline`; `TestAppRunTmuxPopupToggleKeepsNotifySidebarFZFSizingForNative` |
51
52
  | optional native titlebar | native picker popup frame | Covered for empty-title compatibility and opt-in titles | `picker.Options.Title`; `RenderFrameWithTitle`; `ContentLayoutWithTitle`; empty titles keep the prior frame unchanged, non-empty titles render in a picker-owned section below the top border, titlebar text/divider/chip gaps inherit the frame background/foreground instead of a separate titlebar overlay ANSI layer, and the native Alt-1 project sidebar opts into `Projects`; `TestRendererRenderFrameWithTitleKeepsDefaultWhenTitleEmpty`; `TestRendererRenderFrameWithTitleUsesTitlebarRow`; `TestRendererContentLayoutWithTitleReservesTitlebarRow`; `TestNativeInteractiveRendersOptionalTitlebar`; `TestSwitchCommandNativeSidebarSetsTitle` |
52
53
  | redraw flicker/top clipping | keyboard navigation in exact-height tmux popup | Partially covered | native redraws use synchronized updates plus coalesced row diffs after the first frame, skip unchanged frames, render frame diffs before sidebar focus commands, frame rendering avoids trailing bottom-border CRLF, and screen exit clears the alternate buffer before restore; `TestNativeInteractiveWrapsRedrawsInSynchronizedUpdates`; `TestFrameUpdateRendererSkipsUnchangedFrame`; `TestFrameUpdateRendererCoalescesEachFrameUpdate`; `TestRendererRenderFrameUsesCRLFRowsForRawTTY`; `TestNativeInteractiveUsesAlternateScreen` |
53
54
 
@@ -110,8 +111,10 @@ contract; native popups still rely on the existing borderless tmux popup path.
110
111
  the right-side preview layout instead of inline preview, asserts the stored
111
112
  preview cursor, selects `bravo-web`, and asserts tmux reports the selected
112
113
  session's active target on the expected window with the expected pane path.
113
- - `notify sidebar`: native routing is unit-covered; Docker no-fzf e2e pushes a
114
- notification, presses printable expect key `a`, and verifies the row is acked.
114
+ - `notify sidebar`: native routing is unit-covered; app tests cover
115
+ queue-write event subscription and picker tests cover repeated
116
+ event-triggered deferred refresh. Docker no-fzf e2e pushes a notification,
117
+ presses printable expect key `a`, and verifies the row is acked.
115
118
 
116
119
  ## Experimental Boundaries
117
120