projmux 0.9.0 → 0.10.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.
- package/README-ko.md +89 -72
- package/README.md +21 -14
- package/docs/agent-workflow.md +26 -14
- package/docs/architecture.md +2 -2
- package/docs/cli.md +197 -69
- package/docs/configuration.md +4 -3
- package/docs/globalization.md +11 -9
- package/docs/hooks.md +28 -15
- package/docs/keybindings.md +5 -2
- package/docs/legacy-diagnostics-inventory.md +23 -0
- package/docs/native-picker.md +103 -0
- package/docs/operational-diagnostics.md +215 -8
- package/docs/pr-guideline.md +1 -1
- package/docs/resource-attribution.md +77 -4
- package/docs/session-restore.md +18 -0
- package/docs/settings-ia.md +10 -9
- package/docs/statusbar.md +28 -18
- package/docs/tmux-surface-inventory.md +0 -1
- package/docs/upgrading.md +29 -7
- package/docs/usage-tracking.md +63 -19
- package/package.json +5 -5
- package/docs/native-picker-no-fzf-poc.md +0 -227
- package/docs/native-picker-parity.md +0 -155
- package/docs/picker-ui-plan.md +0 -91
package/docs/statusbar.md
CHANGED
|
@@ -68,7 +68,8 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git> CPU 12% MEM 41%
|
|
|
68
68
|
without dominating the status row. Window tab indexes stay left of each tab,
|
|
69
69
|
and tab titles are centered in a fixed-width trim so long active pane names
|
|
70
70
|
do not resize the status row.
|
|
71
|
-
- `Settings > Labs > Live system resources` adds the compact
|
|
71
|
+
- `Settings > Labs > Live system resources` adds the compact
|
|
72
|
+
`CPU N% MEM N%`
|
|
72
73
|
segment between git and the clock on macOS, Linux, and WSL. It is global,
|
|
73
74
|
default off, and updates with tmux's existing five-second status interval.
|
|
74
75
|
CPU and memory are host-scoped telemetry, not pane, window, project, or
|
|
@@ -76,8 +77,13 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git> CPU 12% MEM 41%
|
|
|
76
77
|
normal below 70%, warning at 70–89%, and critical at 90% or above; memory is
|
|
77
78
|
normal below 75%, warning at 75–89%, and critical at 90% or above. Normal and
|
|
78
79
|
unavailable (`--`) values use the secondary status-text role, warnings use
|
|
79
|
-
the warning role, and critical values use the bold critical role.
|
|
80
|
-
value
|
|
80
|
+
the warning role, and critical values use the bold critical role. Severity
|
|
81
|
+
words are omitted. Each percent value, including `%`, occupies one fixed
|
|
82
|
+
four-column slot (` 9%`, ` 15%`, `100%`, or ` --%`), so styling or changing
|
|
83
|
+
either metric cannot move the following segment. Styling one value never
|
|
84
|
+
promotes the other value. The Resource Inspector uses this same classifier
|
|
85
|
+
and semantic roles for host and attributed CPU/memory while rendering
|
|
86
|
+
unavailable metrics as `--` without severity suffixes.
|
|
81
87
|
Linux CPU is the aggregate delta from `/proc/stat`; memory is
|
|
82
88
|
`(MemTotal - MemAvailable) / MemTotal` from `/proc/meminfo`. macOS CPU uses
|
|
83
89
|
the aggregate Mach host tick delta; memory is total physical memory minus
|
|
@@ -116,16 +122,16 @@ bind-key -n MouseDown1Status if-shell -F "#{==:#{mouse_status_range},window}" \
|
|
|
116
122
|
|
|
117
123
|
## Range catalogue
|
|
118
124
|
|
|
119
|
-
| Range id |
|
|
120
|
-
| -------- |
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
125
|
+
| Range id | Generated row | Click action | Keyboard |
|
|
126
|
+
| -------- | ------------- | ----------------------------------------- | ------------- |
|
|
127
|
+
| `notify` | `status-format[0]` | `projmux focus --target <newest> --source status-bar --kind segment-click [--client <tty>]`, then ack on focus success | `prefix s n` |
|
|
128
|
+
| `usage` | `status-format[0]` | show a native-framed usage HUD popup from cached usage state | `prefix s u` |
|
|
129
|
+
| `session` | `status-format[1]` | `projmux tmux popup-toggle sessionizer-sidebar` | `prefix s s` |
|
|
130
|
+
| `pwd` | `status-format[1]` | show a native-framed current-path popup; no clipboard or tmux buffer copy | `prefix s p` |
|
|
131
|
+
| `kube` | `status-format[1]` | `projmux tmux popup-toggle sessionizer` | `prefix s k` |
|
|
132
|
+
| `git` | `status-format[1]` | `projmux tmux popup-toggle sessionizer` | `prefix s g` |
|
|
133
|
+
| `resources` | `status-format[1]` | `projmux tmux popup-toggle --client <tty> resource-inspector` | mouse or custom `Resources:Open`; no default key |
|
|
134
|
+
| `settings` | `status-format[1]` | `projmux tmux popup-toggle --client <tty> ai-split-settings` | mouse only; `prefix s s` remains `session` |
|
|
129
135
|
|
|
130
136
|
`notify` reads the pending queue only. For a live pane-state view that is
|
|
131
137
|
independent of queued reminders, use `projmux attention list`. To explain why
|
|
@@ -155,7 +161,11 @@ Antigravity rows keep conversation-local `context` separate from account
|
|
|
155
161
|
`quota/<exact upstream bucket ID>` rows; the popup displays an absolute reset
|
|
156
162
|
when provided and otherwise the exact optional relative reset seconds. Opaque
|
|
157
163
|
bucket IDs are escaped for terminal/tmux safety and are never assigned a
|
|
158
|
-
`5h`/`weekly` cadence.
|
|
164
|
+
`5h`/`weekly` cadence. Claude retains aggregate `5h`/`weekly` rows alongside
|
|
165
|
+
typed named/model `limits[]` rows in this popup: model-scoped rows display the
|
|
166
|
+
exact upstream group plus model display identity with a bounded terminal-safe
|
|
167
|
+
label, reset, and per-row age. The compact status line excludes every Claude
|
|
168
|
+
named/model row and continues to use only the aggregate official windows.
|
|
159
169
|
Session State inspection lives under `Projects > Sessions > State`; global
|
|
160
170
|
Settings > Session State is settings-only and the statusbar no longer exposes a
|
|
161
171
|
duplicate State button.
|
|
@@ -169,7 +179,8 @@ does not leave terminal key state behind. The usage popup uses the same
|
|
|
169
179
|
single-payload print and plain Enter-close pattern. It shows the authoritative
|
|
170
180
|
last collect timestamp when present, falls back to the cache file mtime when
|
|
171
181
|
needed, and keeps stale sync metadata muted instead of escalating it to a
|
|
172
|
-
warning color.
|
|
182
|
+
warning color. Percent-only named rows do not synthesize `USED`, `LIMIT`, or
|
|
183
|
+
`LEFT` counts.
|
|
173
184
|
The notification HUD detail surface opens the right-side notification popup
|
|
174
185
|
through the notify sidebar action, showing the grouped pane/session inbox with
|
|
175
186
|
collapsed group rows and the same attention-tinted title. When notification
|
|
@@ -273,9 +284,8 @@ updates the matching live tmux option
|
|
|
273
284
|
(`@projmux_statusbar_decoration_cwd`, `_git`, or `_notify`) when run inside
|
|
274
285
|
tmux. The legacy `~/.config/projmux/statusbar-decoration` and
|
|
275
286
|
`@projmux_statusbar_decoration` remain fallback defaults for older configs.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
font family or size, so unsupported environments report `not applied`.
|
|
287
|
+
Settings > Theme controls the bottom status bar background through
|
|
288
|
+
`status_background`; `surface` controls popup and native frame backgrounds.
|
|
279
289
|
|
|
280
290
|
Settings > Labs controls the experimental live resource segment. Its saved
|
|
281
291
|
value is `~/.config/projmux/live-resources`; changing it inside tmux updates
|
package/docs/upgrading.md
CHANGED
|
@@ -40,6 +40,27 @@ still requires an explicit `PROJMUX_INSTALLER=github-release`.
|
|
|
40
40
|
|
|
41
41
|
## Behavior Changes
|
|
42
42
|
|
|
43
|
+
### Terminal init command removed
|
|
44
|
+
|
|
45
|
+
The deprecated top-level `projmux init` command and its legacy-only
|
|
46
|
+
`--dry-run` flag have been removed. Use the exact replacement
|
|
47
|
+
`projmux setup terminal`; it previews by default, and accepts `--apply`,
|
|
48
|
+
`--config <path>`, and `--allow-symlink` when those behaviors are needed.
|
|
49
|
+
|
|
50
|
+
### Pane rename keymap action ID removed
|
|
51
|
+
|
|
52
|
+
The deprecated `rename-pane-topic` keybinding action ID has been removed. If
|
|
53
|
+
`~/.config/projmux/keymap.toml` still contains
|
|
54
|
+
`[bindings.rename-pane-topic]`, rename that table to
|
|
55
|
+
`[bindings.rename-pane-label]` before running Settings or
|
|
56
|
+
`projmux tmux apply`. Projmux now rejects the stale table with that exact
|
|
57
|
+
replacement instead of silently applying its keys to the user-label action.
|
|
58
|
+
|
|
59
|
+
This removal does not change `projmux ai topic set/clear`: those advanced CLI
|
|
60
|
+
commands continue to write only AI topic and manual-ownership state. User pane
|
|
61
|
+
rename continues to write only the pane label, and raw pane title remains an
|
|
62
|
+
independent fallback.
|
|
63
|
+
|
|
43
64
|
### Theme is now global-only
|
|
44
65
|
|
|
45
66
|
Theme is a global user preference. The effective theme resolves from the global
|
|
@@ -95,13 +116,14 @@ through separate terminal preset variants.
|
|
|
95
116
|
|
|
96
117
|
### Pane body vs popup backgrounds
|
|
97
118
|
|
|
98
|
-
The general
|
|
99
|
-
separate public tokens. The pane body follows `background` (unset
|
|
100
|
-
terminal default),
|
|
101
|
-
settings/notify/recent/picker frames follow
|
|
102
|
-
fallback equals `background`, leaving
|
|
103
|
-
|
|
104
|
-
body.
|
|
119
|
+
The general pane, bottom status bar, and popup/native frame backgrounds are now
|
|
120
|
+
driven by separate public tokens. The pane body follows `background` (unset
|
|
121
|
+
keeps the terminal default), the status bar follows `status_background`, and
|
|
122
|
+
native popup bodies plus the settings/notify/recent/picker frames follow
|
|
123
|
+
`surface`. Because the `surface` fallback equals `background`, leaving those two
|
|
124
|
+
unset looks exactly as before; set `surface` separately to make popups read as a
|
|
125
|
+
distinct surface from the pane body. Set `status_background` separately to
|
|
126
|
+
repaint only the bottom status bar.
|
|
105
127
|
|
|
106
128
|
### Theme font keys removed
|
|
107
129
|
|
package/docs/usage-tracking.md
CHANGED
|
@@ -11,12 +11,17 @@ requests such as `projmux usage --model claude`, `--model codex`, or
|
|
|
11
11
|
still collect and render that provider even when it is disabled.
|
|
12
12
|
|
|
13
13
|
Claude and Codex adapters read the upstream's own account view. Antigravity
|
|
14
|
-
reads only the official managed statusline payload: `context_window` remains
|
|
15
|
-
conversation-local
|
|
16
|
-
account row. Projmux preserves the upstream bucket ID and
|
|
17
|
-
undocumented ID means `5h` or `weekly`. It does not infer
|
|
18
|
-
timestamps, or account limits from screen scraping,
|
|
19
|
-
OAuth/cache files, or binary strings.
|
|
14
|
+
reads only the official managed statusline payload: `context_window` remains
|
|
15
|
+
private conversation-local diagnostic metadata, while each valid `quota` map
|
|
16
|
+
entry is a separate account row. Projmux preserves the upstream bucket ID and
|
|
17
|
+
never guesses that an undocumented ID means `5h` or `weekly`. It does not infer
|
|
18
|
+
quota, cadence, reset timestamps, or account limits from screen scraping,
|
|
19
|
+
tokens, history, OAuth/cache files, or binary strings.
|
|
20
|
+
|
|
21
|
+
Claude keeps the canonical aggregate `five_hour` and `seven_day` rows and also
|
|
22
|
+
preserves structurally valid typed `limits[]` rows as named account quotas for
|
|
23
|
+
inspection surfaces. These named rows never participate in the ambient status
|
|
24
|
+
projection.
|
|
20
25
|
|
|
21
26
|
## Adapters
|
|
22
27
|
|
|
@@ -45,6 +50,23 @@ floor.
|
|
|
45
50
|
- A clean 200 resets the consecutive counter.
|
|
46
51
|
- `--force` (BackoffResetter) clears the persisted state and attempts
|
|
47
52
|
the call regardless of streak.
|
|
53
|
+
- Canonical `five_hour` and `seven_day` blocks remain `5h` and `weekly`.
|
|
54
|
+
Each valid typed `limits[]` row becomes `window=quota` with the exact opaque
|
|
55
|
+
`group` copied to `bucket`. The snapshot also preserves `kind`, `severity`,
|
|
56
|
+
`is_active`, and nullable `scope`, model ID, and surface metadata; percent and
|
|
57
|
+
reset remain the authoritative common snapshot fields.
|
|
58
|
+
- A model-scoped quota renders as `quota/<group> · <model display name>` in
|
|
59
|
+
text and popup inspection. Control characters and tmux format introducers are
|
|
60
|
+
escaped and the display label is bounded, while the stored identity remains
|
|
61
|
+
byte-for-byte unchanged. No model-family inference, aliasing, aggregation, or
|
|
62
|
+
percentage-to-count derivation is performed.
|
|
63
|
+
- `limits[]` is capped at 64 rows and required field presence/types are checked
|
|
64
|
+
explicitly. A malformed container or row fails that adapter collection so the
|
|
65
|
+
manager retains the complete last-known-good Claude slice. A valid
|
|
66
|
+
aggregate-only response succeeds and therefore removes obsolete named rows.
|
|
67
|
+
- Null legacy top-level model hints and unknown experiment keys are ignored.
|
|
68
|
+
Billing/credit blocks such as `extra_usage` and `spend` are not ingested or
|
|
69
|
+
rendered.
|
|
48
70
|
|
|
49
71
|
### Codex (`internal/core/usage/adapters/codex`)
|
|
50
72
|
|
|
@@ -74,12 +96,12 @@ read.
|
|
|
74
96
|
|
|
75
97
|
Local managed-statusline sidecars. No network or credential reads.
|
|
76
98
|
|
|
77
|
-
- `context_window.used_percentage`
|
|
78
|
-
|
|
79
|
-
percentage remains a
|
|
99
|
+
- `context_window.used_percentage` and its conversation ID remain in the
|
|
100
|
+
private context sidecar for hook/notify diagnostics. They do not become
|
|
101
|
+
Usage snapshots. The legacy string percentage remains a writer fallback.
|
|
80
102
|
- The official `quota` map is sorted by its exact bucket ID. Each valid bucket
|
|
81
103
|
becomes `window=quota`, `bucket=<upstream ID>` and renders as
|
|
82
|
-
`quota/<upstream ID>`
|
|
104
|
+
`quota/<upstream ID>` on account-inspection surfaces.
|
|
83
105
|
- Used percent is `100 * (1 - remaining_fraction)`. Non-finite or values
|
|
84
106
|
outside `[0,1]`, empty IDs, null/disabled entries, and negative relative
|
|
85
107
|
resets are ignored safely.
|
|
@@ -89,8 +111,8 @@ Local managed-statusline sidecars. No network or credential reads.
|
|
|
89
111
|
- Context and quota use independent private sidecars. A context-only payload
|
|
90
112
|
does not erase the last quota observation. An explicit empty/null quota map
|
|
91
113
|
records no buckets; the manager's existing rule still preserves prior model
|
|
92
|
-
rows when an adapter returns zero total rows
|
|
93
|
-
|
|
114
|
+
rows when an adapter returns zero total rows. Context never participates in
|
|
115
|
+
that account-row replacement decision.
|
|
94
116
|
|
|
95
117
|
## Snapshot store
|
|
96
118
|
|
|
@@ -101,7 +123,9 @@ ${PROJMUX_USAGE_STATE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/projmux/usage}/
|
|
|
101
123
|
JSON document keyed by adapter, recording:
|
|
102
124
|
|
|
103
125
|
- per-window `Snapshot{Model, Window, Bucket, Pct, Limit, ResetsAt,
|
|
104
|
-
ResetInSeconds, UpdatedAt}`; `Bucket` is populated only for
|
|
126
|
+
ResetInSeconds, UpdatedAt, NamedQuota}`; `Bucket` is populated only for
|
|
127
|
+
`window=quota`, and `NamedQuota` is populated only when an upstream typed
|
|
128
|
+
named-quota contract supplies the metadata
|
|
105
129
|
- per-adapter `last_collect` timestamp (drives the throttle)
|
|
106
130
|
- per-adapter `Backoff{Until, Consecutive}` (drives the cooldown)
|
|
107
131
|
|
|
@@ -126,8 +150,8 @@ Enabled agents, filters by window, and renders the tab-aligned table:
|
|
|
126
150
|
```
|
|
127
151
|
MODEL WINDOW PCT RESETS_AT RESET_IN STALE
|
|
128
152
|
claude 5h 80% 2026-05-07T14:00:00+09:00 -
|
|
129
|
-
antigravity context 14% - -
|
|
130
153
|
antigravity quota/gemini-weekly 6% 2026-07-06T16:50:32+09:00 560580s
|
|
154
|
+
claude quota/group-redacted · Model Redacted Alpha 38% 2031-02-03T15:05:06+09:00 - *
|
|
131
155
|
```
|
|
132
156
|
|
|
133
157
|
`STALE` is `*` when `now - UpdatedAt > 10m`. A `--json` payload returns
|
|
@@ -144,9 +168,11 @@ provider rows and prints a short Settings hint. `--json` returns an
|
|
|
144
168
|
empty array. Explicit `--model claude`, `--model codex` and
|
|
145
169
|
`--model antigravity` bypass the enabled-agent filter for read-only
|
|
146
170
|
inspection and collect/render only the requested adapter. Antigravity reports
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`--window weekly` never matches an opaque quota bucket named
|
|
171
|
+
zero or more account `quota/<bucket-id>` rows. Legacy cached `window=context`
|
|
172
|
+
rows are suppressed in text and JSON output. `--window quota` selects only
|
|
173
|
+
account buckets; `--window weekly` never matches an opaque quota bucket named
|
|
174
|
+
`weekly`. `--window context` remains an accepted compatibility filter and
|
|
175
|
+
returns no Usage rows.
|
|
150
176
|
|
|
151
177
|
### `projmux status usage`
|
|
152
178
|
|
|
@@ -162,12 +188,19 @@ are swallowed unless `PROJMUX_USAGE_DEBUG` is set. Then it loads the
|
|
|
162
188
|
cache, filters to the same enabled-agent scope, and renders. If no AI
|
|
163
189
|
agents are enabled, the status segment emits nothing.
|
|
164
190
|
|
|
191
|
+
The HUD first derives an ambient projection separate from lossless account
|
|
192
|
+
snapshots. Canonical `5h`/`weekly` rows are eligible for every provider;
|
|
193
|
+
Antigravity's exact `quota/gemini-weekly` identity is projected as `weekly`
|
|
194
|
+
without rewriting the cache. Context, `3p-weekly`, and unknown quota buckets
|
|
195
|
+
do not participate in status width. Claude `limits[]` named/model rows are also
|
|
196
|
+
excluded; only its aggregate official `5h` and `weekly` rows reach the HUD.
|
|
197
|
+
|
|
165
198
|
Output degrades through six tiers as `--max-width` shrinks:
|
|
166
199
|
|
|
167
200
|
1. Long form with last-sync age + bars: `Claude (3m) 5h [████████░░]
|
|
168
|
-
80% · weekly [...] Antigravity
|
|
201
|
+
80% · weekly [...] Antigravity weekly [...]`
|
|
169
202
|
2. Drop the age indicator (legacy long form).
|
|
170
|
-
3.
|
|
203
|
+
3. Keep one primary bar per provider (`5h`, or `weekly` when 5h is absent).
|
|
171
204
|
4. Drop bars entirely (`Claude 5h:80% weekly:30%`).
|
|
172
205
|
5. Single-letter labels (`C 5h:80% weekly:30%`).
|
|
173
206
|
6. Hard rune-truncate with trailing `…`.
|
|
@@ -186,6 +219,17 @@ sync metadata to the same enabled-agent scope as the ambient HUD. This keeps
|
|
|
186
219
|
tmux click path a structured table with aligned rows, right-aligned numeric
|
|
187
220
|
values, dim unavailable cells, amber usage at 80%, and red usage at 95%.
|
|
188
221
|
|
|
222
|
+
The popup suppresses legacy cached context rows while preserving every valid
|
|
223
|
+
named quota ID, reset, and freshness value. Its columns are data-driven: when
|
|
224
|
+
no displayed row has authoritative absolute token counts, `USED`, `LIMIT`, and
|
|
225
|
+
`LEFT` are omitted together. If any row has real counts, all three columns are
|
|
226
|
+
shown; percent-only rows use unavailable cells. Counts are never derived from
|
|
227
|
+
percentages.
|
|
228
|
+
|
|
229
|
+
Named Claude rows use the same bounded, injection-safe label as text output and
|
|
230
|
+
include their reset plus per-row `AGE`. JSON retains exact opaque group/model
|
|
231
|
+
identity, nullable scope fields, `updated_at`, and the derived `stale` flag.
|
|
232
|
+
|
|
189
233
|
The popup sync line uses the maximum authoritative `LastCollect` timestamp from
|
|
190
234
|
the cache. If that field is unavailable, it falls back to the snapshots file
|
|
191
235
|
mtime. The sync line turns amber when the timestamp is more than 60 seconds
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projmux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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.
|
|
32
|
-
"@projmux/linux-arm64": "0.
|
|
33
|
-
"@projmux/darwin-x64": "0.
|
|
34
|
-
"@projmux/darwin-arm64": "0.
|
|
31
|
+
"@projmux/linux-x64": "0.10.1",
|
|
32
|
+
"@projmux/linux-arm64": "0.10.1",
|
|
33
|
+
"@projmux/darwin-x64": "0.10.1",
|
|
34
|
+
"@projmux/darwin-arm64": "0.10.1"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# Native Picker Engine
|
|
2
|
-
|
|
3
|
-
This note tracks the native picker engine. Native is the only picker backend.
|
|
4
|
-
|
|
5
|
-
The fzf compatibility surface for the native engine is tracked in
|
|
6
|
-
[native-picker-parity.md](native-picker-parity.md).
|
|
7
|
-
|
|
8
|
-
## What This Covers
|
|
9
|
-
|
|
10
|
-
- `internal/ui/picker` is the backend-neutral contract for native picker rows,
|
|
11
|
-
actions, filtering, and typed-query prompts.
|
|
12
|
-
- `internal/ui/projmuxpicker` is the projmux-specific native picker surface for
|
|
13
|
-
frame, redraw updates, theme tokens, ANSI width/truncation,
|
|
14
|
-
prompt/footer/list rendering, and preview pane layout. The POC keeps `picker`
|
|
15
|
-
responsible for backend routing, keyboard input, filtering, preview command
|
|
16
|
-
execution, and result contracts, while moving visual composition into
|
|
17
|
-
`projmuxpicker` so projmux can evolve a native picker design without coupling
|
|
18
|
-
every visual tweak to the compatibility option/result shape.
|
|
19
|
-
Built-in fallback colors are centralized as semantic tokens in
|
|
20
|
-
`internal/theme/palette.go`; see [theme-palette.md](theme-palette.md) for the
|
|
21
|
-
current inventory and truecolor to tmux 256-color mapping policy.
|
|
22
|
-
- `internal/ui/pickercompat` remains an internal compatibility mapper between
|
|
23
|
-
the old option/result shape and the backend-neutral `picker.Options`
|
|
24
|
-
contract. It is not a runtime backend. App code should
|
|
25
|
-
describe picker intent as rows, actions, preview commands, and initial focus,
|
|
26
|
-
then route through the native picker.
|
|
27
|
-
- Settings > Labs remains available for Live system resources and Project
|
|
28
|
-
Hooks, but picker backend/source information has been retired.
|
|
29
|
-
- Picker flows covered by the native path include AI picker/settings, shell
|
|
30
|
-
update prompt, settings hub sections, switch settings/add-pin, the main
|
|
31
|
-
project switcher list, recent sessions, and notify sidebar.
|
|
32
|
-
- The native picker supports ranked fuzzy search/filter, arrow-key selection in
|
|
33
|
-
normal CSI and tmux application-cursor modes, Enter, Esc, Ctrl-C, Backspace,
|
|
34
|
-
Ctrl-U, Ctrl-W, PageUp/PageDown, Home/End, modified CSI keys, custom expect
|
|
35
|
-
keys such as Ctrl-X/Alt-P, printable expect keys such as notify `a`/`x`, control
|
|
36
|
-
expect keys such as notify `Ctrl-X`, `start:pos(N)` initial focus, preview
|
|
37
|
-
command output, preview cycle command bindings, and sidebar focus command
|
|
38
|
-
bindings.
|
|
39
|
-
- FZF-style movement keys are supported for native selection: `Ctrl-N` and
|
|
40
|
-
`Ctrl-J` move down, while `Ctrl-P` and `Ctrl-K` move up unless the app claims
|
|
41
|
-
the key as a custom action. Up/down-family movement wraps at list boundaries;
|
|
42
|
-
PageUp/PageDown and Home/End remain clamped or explicit jumps.
|
|
43
|
-
- Typed-query prompts support cursor-aware insertion/deletion with a visible
|
|
44
|
-
prompt cursor, Left/Right, Ctrl-A/E, Delete, Backspace, Ctrl-U, and Ctrl-W
|
|
45
|
-
for settings path entry.
|
|
46
|
-
- The native key parser keeps legacy parser-fixture coverage for app-specific
|
|
47
|
-
modified-key escapes, plus generic modified forms such as `ESC [ 115 ; 7 u`
|
|
48
|
-
for `Ctrl-Alt-S`. This is backend parser parity, not a product fallback
|
|
49
|
-
route.
|
|
50
|
-
- Native interactive picker screens use an alternate screen lifecycle to better
|
|
51
|
-
match fzf fullscreen behavior and restore the tmux pane after exit. Frame
|
|
52
|
-
updates and screen exit both return to column 0 before emitting terminal
|
|
53
|
-
control sequences, and screen exit resets styles plus clears the alternate
|
|
54
|
-
buffer from the home cursor before restore. This keeps restore/update escapes
|
|
55
|
-
and selected-session handoff output from visually trailing the bottom border
|
|
56
|
-
in tmux/script captures. Native also gives real TTY screen restore a short
|
|
57
|
-
settle window before returning to callers that may immediately draw a tmux
|
|
58
|
-
session.
|
|
59
|
-
- Native interactive picker screens render inside a full-screen border frame to
|
|
60
|
-
match the app's fzf `--height 100% --border` surface more closely.
|
|
61
|
-
- Popup-toggle commands use tmux `display-popup -B` when the native backend is
|
|
62
|
-
active so the native picker owns the visible frame and does not double-draw
|
|
63
|
-
with tmux's outer popup border.
|
|
64
|
-
- Native Alt-1 sidebar popups use the same responsive width calculation as fzf
|
|
65
|
-
with a smaller native-only minimum, so the borderless native frame is not
|
|
66
|
-
wider than the existing fzf sidebar surface on normal terminals.
|
|
67
|
-
- Native Alt-2 notify sidebar popups keep the fzf baseline width contract
|
|
68
|
-
(`24%`, minimum `64`) while still letting the native picker own the frame.
|
|
69
|
-
- Native sidebar popups reserve two rows at the bottom for the tmux statusbar
|
|
70
|
-
area instead of using the full client height.
|
|
71
|
-
- Simple native lists use the available terminal height after header, prompt,
|
|
72
|
-
footer, and preview reservations instead of a fixed page-sized viewport.
|
|
73
|
-
- Navigation-only native lists mirror fzf `--disabled --no-input`: the input
|
|
74
|
-
prompt is hidden, printable non-action keys do not alter the query, and
|
|
75
|
-
expect/action keys still work.
|
|
76
|
-
- Native frame content now uses the full inner border width so prompt/list/footer
|
|
77
|
-
separators reach the right border like fzf.
|
|
78
|
-
- Native frames can render an optional picker-owned titlebar row below the top
|
|
79
|
-
border when `picker.Options.Title` is set; empty titles keep the default frame
|
|
80
|
-
unchanged. Non-empty titles use a distinct neutral titlebar surface with rule
|
|
81
|
-
fill and a divider row separating the title section from the search/content
|
|
82
|
-
section. The native Alt-1 project sidebar uses this for a
|
|
83
|
-
`Projects` titlebar.
|
|
84
|
-
- Native width/truncation uses terminal cell width for Korean/CJK text, emoji,
|
|
85
|
-
and combining marks instead of raw rune count, so localized project names and
|
|
86
|
-
decorated notify headers do not push the right frame border out of alignment.
|
|
87
|
-
- Searchable native pickers draw an explicit `Search` label and a muted
|
|
88
|
-
separator under the prompt/count line so the query area reads as distinct
|
|
89
|
-
chrome rather than the first row of the list. Footer, down-preview, and
|
|
90
|
-
multi-line card gaps share the same `projmuxpicker` separator primitive for a
|
|
91
|
-
more consistent native surface.
|
|
92
|
-
- Native interactive mode enables SGR mouse reporting while the alternate screen
|
|
93
|
-
is active. Primary mouse down focuses the row under the cursor, primary mouse
|
|
94
|
-
up applies it, mouse wheel moves selection up/down, and reporting is disabled
|
|
95
|
-
again during screen restore.
|
|
96
|
-
- When terminal size detection is unavailable, native picker falls back to a
|
|
97
|
-
conservative 80x24 terminal instead of assuming a wider surface. Interactive
|
|
98
|
-
tmux popups still use the detected popup size when `stty size` is available.
|
|
99
|
-
- Simple and multi-line native rows share the same projmux current-row style and
|
|
100
|
-
pointer marker rather than falling back to terminal inverse video for simple
|
|
101
|
-
pickers.
|
|
102
|
-
- Selected multi-line rows use the same pointer-width `▌` continuation marker
|
|
103
|
-
as the first selected project line, so switch/session/notify cards read as one
|
|
104
|
-
focused block.
|
|
105
|
-
- Pointer and continuation markers render inside the current-row gutter style so
|
|
106
|
-
selected cards do not visually break between the marker and row content.
|
|
107
|
-
- Native redraws use terminal synchronized-update wrappers and coalesced
|
|
108
|
-
row-diff updates after the first frame. The frame/redraw renderer lives in
|
|
109
|
-
`projmuxpicker` rather than the backend loop, skips unchanged frames, and
|
|
110
|
-
avoids a trailing newline after the bottom border. This reduces visible
|
|
111
|
-
keyboard-navigation flicker and prevents exact-height popups from scrolling
|
|
112
|
-
the top border off screen.
|
|
113
|
-
- Native preview panes normalize tabs and control bytes before horizontal
|
|
114
|
-
clipping, preventing long preview rows from wrapping and consuming extra
|
|
115
|
-
vertical viewport rows in session popups.
|
|
116
|
-
- Native sidebar list scrollbars use the fixed list viewport as their track and
|
|
117
|
-
measure multi-line cards in rendered rows, so the thumb does not shrink or
|
|
118
|
-
jump when card heights vary.
|
|
119
|
-
- Switch picker git branch badges are capped more tightly for the native card
|
|
120
|
-
surface, so inactive branch backgrounds do not dominate narrow Alt-1 sidebar
|
|
121
|
-
rows.
|
|
122
|
-
- Native selection changes render their frame diff before running sidebar focus
|
|
123
|
-
commands, so tmux focus/switch side effects do not delay the visible picker
|
|
124
|
-
movement.
|
|
125
|
-
- In app TTY contexts, the native picker opens the controlling terminal
|
|
126
|
-
(`/dev/tty`) before entering raw mode. This avoids stdin/stdout mismatch and
|
|
127
|
-
line-mode escape leakage such as arrow keys appearing as `^[[`.
|
|
128
|
-
- Raw TTY reads keep polling briefly across empty reads while decoding
|
|
129
|
-
escape-key sequences, so split arrow/Alt key bytes are consumed by the picker
|
|
130
|
-
instead of leaking into the query or parent shell.
|
|
131
|
-
|
|
132
|
-
## Experimental Boundaries
|
|
133
|
-
|
|
134
|
-
- The `projmuxpicker` package is intended as a foundation that can be carried
|
|
135
|
-
forward, along with the compatibility option/result mapping in
|
|
136
|
-
`internal/ui/pickercompat`.
|
|
137
|
-
Its frame, row, preview, theme, ANSI, and redraw modules are foundation code;
|
|
138
|
-
Docker sandbox scripts and dependency-policy notes remain support
|
|
139
|
-
scaffolding.
|
|
140
|
-
- Switch and sessions preview panes are native previews for the concrete
|
|
141
|
-
projmux option shapes. Wide right-side preview windows render beside the
|
|
142
|
-
list, and sidebar-style `down,25%,border-top` previews render below the list
|
|
143
|
-
without a synthetic preview title row, using fzf-measured percent sizing.
|
|
144
|
-
Preview rows are padded by `projmuxpicker` to the full preview surface width
|
|
145
|
-
before the outer frame is applied, which keeps split/down preview columns
|
|
146
|
-
visually stable during redraws.
|
|
147
|
-
The full fzf preview-window grammar remains outside this POC surface.
|
|
148
|
-
- Preview cycle state is covered in Docker e2e against real tmux sessions: the
|
|
149
|
-
switch and sessions popup flows type a filtered query, send `Right` and
|
|
150
|
-
`Alt-Down`, and assert the stored preview window/pane cursor for the selected
|
|
151
|
-
session.
|
|
152
|
-
- Public doctor dependency policy no longer includes an external picker binary.
|
|
153
|
-
|
|
154
|
-
## Interactive No-fzf Sandbox
|
|
155
|
-
|
|
156
|
-
Use this when you want to enter a Docker container and experience this build
|
|
157
|
-
directly without `fzf`. It builds the no-fzf dependency image, mounts this
|
|
158
|
-
worktree, builds `projmux` inside the container, creates sample projects under
|
|
159
|
-
`/workspace/projects`, stores the native picker backend in the sandbox config,
|
|
160
|
-
writes a tmux config with the same backend in the tmux server environment, and
|
|
161
|
-
launches `projmux shell`. It also forces UTF-8 locale inside the container. This
|
|
162
|
-
uses `wt path` instead of `wt run` because `docker run -it` needs the current
|
|
163
|
-
terminal TTY:
|
|
164
|
-
|
|
165
|
-
```sh
|
|
166
|
-
bash "$(wt path poc/native-picker-no-fzf)/scripts/poc-native-picker-no-fzf-sandbox.sh"
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Inside the tmux shell, try:
|
|
170
|
-
|
|
171
|
-
```sh
|
|
172
|
-
projmux switch
|
|
173
|
-
projmux settings
|
|
174
|
-
projmux doctor --json
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Manual UX checks for the Docker sandbox:
|
|
178
|
-
|
|
179
|
-
- Alt-1 opens with the top border/title visible, not clipped.
|
|
180
|
-
- Vertical borders stay continuous while moving Up/Down.
|
|
181
|
-
- Alt-1 closes the sidebar immediately when pressed again.
|
|
182
|
-
- Alt-2, Alt-4, Alt-5, and Alt-7 open their matching native popups and close
|
|
183
|
-
on the same Alt key immediately.
|
|
184
|
-
- Alt-3 opens Recent Windows.
|
|
185
|
-
- Arrow keys move selection without leaking `^[[` text into the query.
|
|
186
|
-
|
|
187
|
-
`fzf` is intentionally not installed in the image.
|
|
188
|
-
|
|
189
|
-
## Automated No-fzf Docker E2E Command
|
|
190
|
-
|
|
191
|
-
Run this from the repository root. It builds a Go 1.25 Trixie no-fzf
|
|
192
|
-
dependency image from `test/docker/no-fzf-poc.Dockerfile`, including Go module
|
|
193
|
-
cache, then mounts the repository into an isolated `--network none` container,
|
|
194
|
-
builds `projmux`, asserts `fzf` is not on `PATH`, runs the focused native-picker
|
|
195
|
-
tests, opens Settings > Labs with legacy `fzf` env/file values, verifies native
|
|
196
|
-
operation without a Labs picker-source row or config rewrite, exercises `projmux switch --ui=sidebar`
|
|
197
|
-
search/selection under a container PTY,
|
|
198
|
-
exercises `projmux switch --ui=popup` and `projmux sessions --ui=popup` against
|
|
199
|
-
existing tmux sessions under a wide 150x30 PTY, sends `Right` and `Alt-Down`
|
|
200
|
-
once to smoke the preview-cycle bindings, asserts those popup flows stay on the
|
|
201
|
-
right-side preview layout instead of falling back to inline preview, asserts the
|
|
202
|
-
explicit `Search` chrome across the searchable native picker surfaces, launches
|
|
203
|
-
`projmux shell` under a container PTY, verifies
|
|
204
|
-
that it creates a tmux session, verifies immediate launch-key close behavior for
|
|
205
|
-
Alt-1 through Alt-5 native popup surfaces, exercises `notify list --ui=sidebar`
|
|
206
|
-
with the printable `x` expect key, and exercises the settings picker under a PTY
|
|
207
|
-
using Enter and arrow-key navigation through the native backend. Settings flows
|
|
208
|
-
must keep stderr clean in the no-tmux-server container; tmux no-server noise is
|
|
209
|
-
treated as an e2e failure.
|
|
210
|
-
|
|
211
|
-
Short tmux-friendly form:
|
|
212
|
-
|
|
213
|
-
```sh
|
|
214
|
-
wt run poc/native-picker-no-fzf -- scripts/poc-native-picker-no-fzf-e2e.sh
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
The script contains the Docker image build and isolated `docker run` command:
|
|
218
|
-
|
|
219
|
-
```sh
|
|
220
|
-
scripts/poc-native-picker-no-fzf-e2e.sh
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
To compare another base image without editing the repo, override the build arg:
|
|
224
|
-
|
|
225
|
-
```sh
|
|
226
|
-
PROJMUX_POC_NO_FZF_BASE_IMAGE=golang:1.24-bookworm bash "$(wt path poc/native-picker-no-fzf)/scripts/poc-native-picker-no-fzf-sandbox.sh"
|
|
227
|
-
```
|