projmux 0.6.2 → 0.6.4
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.md +3 -3
- package/docs/agent-workflow.md +22 -9
- package/docs/architecture.md +24 -0
- package/docs/cli.md +23 -19
- package/docs/configuration.md +71 -53
- package/docs/globalization.md +244 -0
- package/docs/hooks.md +7 -5
- package/docs/install.md +14 -11
- package/docs/keybindings.md +159 -309
- package/docs/native-picker-no-fzf-poc.md +8 -4
- package/docs/native-picker-parity.md +4 -4
- package/docs/notify-queue.md +8 -6
- package/docs/session-restore.md +20 -1
- package/docs/settings-ia.md +22 -5
- package/docs/statusbar.md +25 -12
- package/docs/testing.md +3 -2
- package/docs/theme-palette.md +105 -0
- package/docs/tmux-surface-inventory.md +794 -0
- package/docs/usage-tracking.md +4 -4
- package/package.json +5 -5
package/docs/keybindings.md
CHANGED
|
@@ -1,63 +1,33 @@
|
|
|
1
1
|
# Terminal Keybindings
|
|
2
2
|
|
|
3
|
-
projmux is keyboard-driven
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
projmux is keyboard-driven, but the guaranteed launch contract is small:
|
|
4
|
+
fresh installs bind `Alt-1` through `Alt-5` as plain Meta sequences
|
|
5
|
+
(`M-1`..`M-5`, bytes `\x1b1`..`\x1b5`). Other actions remain discoverable in
|
|
6
|
+
Settings > Keybindings. Transport-dependent actions keep their built-in
|
|
7
|
+
transport default key, and Settings can add separate safe tmux plain aliases to
|
|
8
|
+
the same action. They are not installed as terminal-specific User-key
|
|
9
|
+
fallbacks. `UserN` and `CSI-u` are legacy/removal/unsupported targets, not
|
|
10
|
+
supported fallback guidance.
|
|
6
11
|
|
|
7
12
|
The recommended path when a key does not fire:
|
|
8
13
|
|
|
9
|
-
1.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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.
|
|
37
|
-
|
|
38
|
-
> 한국어 요약: 대부분의 터미널은 `projmux shell` 만으로 아래 키가 바로 동작합니다.
|
|
39
|
-
> 동작하지 않으면 `projmux setup` 으로 어떤 키가 막혔는지 진단하고,
|
|
40
|
-
> `projmux init [terminal]` 으로 자동 설정하세요. 자동 설정이 없는 터미널은
|
|
41
|
-
> 마지막 [Manual fallback / advanced (CSI-u)](#manual-fallback--advanced-csi-u)
|
|
42
|
-
> 섹션의 매핑표를 참고해 직접 설정하면 됩니다.
|
|
43
|
-
|
|
44
|
-
The tmux **prefix** is the upstream default `Ctrl-b`. Inside the running
|
|
45
|
-
session, `Ctrl-b ?` lists every binding live.
|
|
46
|
-
|
|
47
|
-
## Quick start (no setup)
|
|
48
|
-
|
|
49
|
-
These shortcuts are bound by projmux's generated tmux config. On terminals
|
|
50
|
-
that pass `Alt-*` and `Ctrl-*` chords through to the running process
|
|
51
|
-
unchanged (e.g. Linux gnome-terminal, foot, most stock Linux terminals),
|
|
52
|
-
they work the moment you launch `projmux shell` — no terminal config
|
|
53
|
-
required.
|
|
54
|
-
|
|
55
|
-
If a key does nothing, jump to
|
|
56
|
-
[Diagnose: `projmux setup`](#diagnose-projmux-setup) instead of guessing.
|
|
57
|
-
|
|
58
|
-
### Pickers
|
|
59
|
-
|
|
60
|
-
These open the projmux popups and the sidebar. No prefix needed.
|
|
14
|
+
1. Try the key inside `projmux shell`.
|
|
15
|
+
2. Open Settings > Keybindings > Diagnostic, or run `projmux setup` outside
|
|
16
|
+
tmux, to see which bytes reach the process.
|
|
17
|
+
3. For supported terminals, preview `projmux init [terminal]`; add `--apply`
|
|
18
|
+
only after reviewing the merge.
|
|
19
|
+
4. For unsupported terminals, configure plain Meta bytes or add a tmux alias in
|
|
20
|
+
Settings > Keybindings.
|
|
21
|
+
|
|
22
|
+
Settings writes safe tmux plain chords to
|
|
23
|
+
`~/.config/projmux/keymap.toml`, regenerates
|
|
24
|
+
`~/.config/projmux/tmux.conf`, and hot-reloads the live tmux config when it is
|
|
25
|
+
running inside tmux. Raw escape payloads, Windows Terminal `sendInput` strings,
|
|
26
|
+
and tmux User keys are rejected as aliases.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
These shortcuts are the guaranteed launch defaults. They need no tmux prefix.
|
|
61
31
|
|
|
62
32
|
| Shortcut | Action |
|
|
63
33
|
| --- | --- |
|
|
@@ -66,263 +36,164 @@ These open the projmux popups and the sidebar. No prefix needed.
|
|
|
66
36
|
| `Alt-3` | Existing session popup |
|
|
67
37
|
| `Alt-4` | AI split picker |
|
|
68
38
|
| `Alt-5` | Settings |
|
|
69
|
-
| `Alt-6` | Project switcher popup |
|
|
70
39
|
|
|
71
|
-
|
|
40
|
+
The tmux prefix remains the upstream default `Ctrl-b`. Inside a running
|
|
41
|
+
session, `Ctrl-b ?` lists the live tmux bindings.
|
|
72
42
|
|
|
73
|
-
|
|
74
|
-
| --- | --- |
|
|
75
|
-
| `Ctrl-n` | New tmux window in the current pane's directory |
|
|
76
|
-
| `Alt-Shift-Left` / `Alt-Shift-Right` | Previous / next window |
|
|
77
|
-
| `Alt-Left` / `Right` / `Up` / `Down` | Move focus between panes |
|
|
78
|
-
| `Alt-r` | Rename the current window |
|
|
79
|
-
|
|
80
|
-
When a pane closes, projmux re-spreads remaining panes so the surviving split
|
|
81
|
-
does not stretch lopsided.
|
|
82
|
-
|
|
83
|
-
The AI split bindings use the configured default mode. For one-shot launches,
|
|
84
|
-
run `projmux ai split --agent claude|codex|shell|selective right|down`. Extra
|
|
85
|
-
args after `--` are appended to the resolved `claude` or `codex` executable for
|
|
86
|
-
a managed pane. To register user-level skills, slash commands, editor actions,
|
|
87
|
-
or launcher shortcuts that call those one-shot launches, see
|
|
88
|
-
[AI Agent Shortcuts](ai-agent-shortcuts.md).
|
|
89
|
-
|
|
90
|
-
### Inside the pickers
|
|
91
|
-
|
|
92
|
-
| Surface | Shortcut | Action |
|
|
93
|
-
| --- | --- | --- |
|
|
94
|
-
| Existing session popup | `Ctrl-X` | Kill the focused session and reopen the popup |
|
|
95
|
-
| Existing session popup | `Left/Right` | Preview previous/next window |
|
|
96
|
-
| Existing session popup | `Alt-Up/Alt-Down` | Preview previous/next pane |
|
|
97
|
-
| Project switcher | `Ctrl-X` | Kill the focused existing session and reopen the picker |
|
|
98
|
-
| Project switcher | `Alt-P` | Pin or unpin the focused directory |
|
|
99
|
-
|
|
100
|
-
### Conditional rename keys
|
|
101
|
-
|
|
102
|
-
These two only fire if your terminal forwards CSI-u sequences for `Ctrl-M`
|
|
103
|
-
and `Ctrl-Shift-M`. Without that wiring, plain `Ctrl-M` is just `Enter` and
|
|
104
|
-
`Ctrl-Shift-M` typically does nothing. `projmux init` configures this for
|
|
105
|
-
the terminals it supports; otherwise see
|
|
106
|
-
[Manual fallback / advanced (CSI-u)](#manual-fallback--advanced-csi-u).
|
|
43
|
+
## Discoverable Actions
|
|
107
44
|
|
|
108
|
-
|
|
45
|
+
Settings > Keybindings lists the full action catalogue. In particular, sidebar
|
|
46
|
+
keymap actions, pane switching, window switching, and rename actions remain
|
|
47
|
+
visible. Transport-dependent rows show the default transport key separately
|
|
48
|
+
from editable plain aliases.
|
|
49
|
+
|
|
50
|
+
Optional direct aliases can be added for actions such as:
|
|
51
|
+
|
|
52
|
+
| Canonical action | Meaning |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `ProjectSwitcherToggle` | Project switcher popup |
|
|
55
|
+
| `new-window` | New tmux window in the current pane directory |
|
|
56
|
+
| `rename-window` | Rename the current tmux window |
|
|
57
|
+
|
|
58
|
+
Pane switching is catalogued as transport-dependent and the generated app tmux
|
|
59
|
+
config binds `M-Left`, `M-Right`, `M-Up`, and `M-Down` to `select-pane`
|
|
60
|
+
movement. Previous/next window remain transport-dependent and the generated app
|
|
61
|
+
tmux config binds `M-S-Left` / `M-S-Right` to the tmux window navigation
|
|
62
|
+
commands. These default transport keys are always rendered by projmux, because
|
|
63
|
+
delivery still depends on the terminal forwarding the modifier-arrow sequence.
|
|
64
|
+
Settings can add extra safe plain aliases, such as `M-[` for
|
|
65
|
+
`previous-window`; those aliases are saved to `keymap.toml` as `keys = [...]`
|
|
66
|
+
without storing or replacing the transport default. Rename actions no longer
|
|
67
|
+
have a built-in terminal fallback; use tmux's prefix rename flow or configure
|
|
68
|
+
an explicit safe alias where the action is editable.
|
|
69
|
+
|
|
70
|
+
## Roadmap Requirements
|
|
71
|
+
|
|
72
|
+
Follow-up Phase 2 keeps Settings > Keybindings as a discovery surface. It must
|
|
73
|
+
continue to expose launch toggles, sidebar keymap actions, picker-local actions,
|
|
74
|
+
pane switching, window switching, and rename actions. Transport-dependent rows
|
|
75
|
+
should explain the default transport key and offer only additive safe plain
|
|
76
|
+
aliases; diagnostic-only rows should explain why they are not editable.
|
|
77
|
+
|
|
78
|
+
Follow-up Phase 3 removes the `UserN` / `CSI-u` route from the product model.
|
|
79
|
+
Windows Terminal and Ghostty-centered replacements should use plain
|
|
80
|
+
Meta/control chords or xterm modifier sequences where possible. If a key cannot
|
|
81
|
+
be represented that way, leave it as a non-editable unsupported or diagnostic
|
|
82
|
+
row instead of preserving a User-key or CSI-u fallback.
|
|
83
|
+
|
|
84
|
+
## Picker Actions
|
|
85
|
+
|
|
86
|
+
Picker-internal commands are surface-scoped. The same physical key can be used
|
|
87
|
+
by different picker surfaces, while conflicts inside one surface are rejected.
|
|
88
|
+
|
|
89
|
+
| Surface action | Meaning |
|
|
109
90
|
| --- | --- |
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
91
|
+
| `Sidebar:KillSession` | Kill the focused existing session |
|
|
92
|
+
| `Sidebar:PinProject` | Pin or unpin the focused directory |
|
|
93
|
+
| `SessionPopup:KillSession` | Kill the focused session |
|
|
94
|
+
| `SessionPopup:CyclePreviewWindowPrev` / `SessionPopup:CyclePreviewWindowNext` | Preview windows |
|
|
95
|
+
| `SessionPopup:CyclePreviewPanePrev` / `SessionPopup:CyclePreviewPaneNext` | Preview panes |
|
|
96
|
+
| `NotifySidebar:Ack` / `NotifySidebar:ClearNonCritical` / `NotifySidebar:ClearAll` | Manage notifications |
|
|
112
97
|
|
|
113
|
-
|
|
98
|
+
Runtime picker footers render key guides from the merged keymap, preferring the
|
|
99
|
+
default alias when it is present and otherwise using the first configured alias.
|
|
100
|
+
|
|
101
|
+
## Keymap File
|
|
102
|
+
|
|
103
|
+
Settings writes the action-centered multi-alias schema:
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
[bindings.ProjectSidebarToggle]
|
|
107
|
+
keys = ["M-1", "M-a"]
|
|
114
108
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
terminal, then asks you to press each shortcut in turn and classifies the
|
|
118
|
-
result:
|
|
109
|
+
[bindings.new-window]
|
|
110
|
+
keys = ["C-t"]
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
[bindings.previous-window]
|
|
113
|
+
keys = ["M-["] # additive alias; default M-S-Left is not stored here
|
|
114
|
+
|
|
115
|
+
[bindings."Sidebar:PinProject"]
|
|
116
|
+
keys = ["M-p", "p"]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Legacy files using `plain = "M-a"` still read as a single-primary replacement
|
|
120
|
+
override. New writes use `keys = [...]`. `keys = []` disables the direct plain
|
|
121
|
+
aliases for that action. Legacy `prefix = ...` entries still parse during
|
|
122
|
+
migration and are preserved when Settings rewrites the file, but Settings does
|
|
123
|
+
not create new prefix entries.
|
|
124
|
+
|
|
125
|
+
Legacy popup IDs such as `sessionizer-sidebar`, `notify-sidebar`,
|
|
126
|
+
`session-popup`, `ai-split-picker-right`, `ai-split-settings`, and
|
|
127
|
+
`sessionizer` still read. Settings and new docs show the canonical toggle
|
|
128
|
+
names: `ProjectSidebarToggle`, `NotifySidebarToggle`, `SessionPopupToggle`,
|
|
129
|
+
`AISplitPickerToggle`, `SettingsToggle`, and `ProjectSwitcherToggle`.
|
|
130
|
+
|
|
131
|
+
## Diagnose: `projmux setup`
|
|
132
|
+
|
|
133
|
+
Run `projmux setup` outside tmux to find out which projmux keys reach the raw
|
|
134
|
+
terminal. The same diagnostic is available in-app at Settings > Keybindings >
|
|
135
|
+
Diagnostic.
|
|
123
136
|
|
|
124
137
|
| Status | Meaning |
|
|
125
138
|
| --- | --- |
|
|
126
|
-
| `OK plain` | The terminal forwarded the
|
|
127
|
-
| `
|
|
128
|
-
| `MISS
|
|
129
|
-
| `MISS unknown` | Something arrived, but it does not match either expected sequence — the terminal bound this combo to a different action. |
|
|
130
|
-
|
|
131
|
-
The summary at the end lists the failing keys and a remediation hint
|
|
132
|
-
tailored to the detected terminal (Ghostty, WezTerm, kitty, iTerm2,
|
|
133
|
-
Alacritty, Windows Terminal, foot, VS Code, …). When projmux ships an init
|
|
134
|
-
adapter for the terminal, the summary gives both the dry-run preview and the
|
|
135
|
-
exact apply command, e.g. `projmux init ghostty --apply`. Settings >
|
|
136
|
-
Keybindings shows the same delivery categories after a capture.
|
|
139
|
+
| `OK plain` | The terminal forwarded the expected bytes, such as `\x1b1` for `Alt-1`; tmux can bind this directly. |
|
|
140
|
+
| `MISS timeout` | No bytes arrived because the terminal swallowed the key. |
|
|
141
|
+
| `MISS unknown` | Bytes arrived, but they do not match the expected plain sequence. |
|
|
137
142
|
|
|
138
143
|
Useful flags:
|
|
139
144
|
|
|
140
145
|
```sh
|
|
141
|
-
projmux setup
|
|
142
|
-
projmux setup --timeout 10s
|
|
143
|
-
projmux setup --non-interactive
|
|
146
|
+
projmux setup
|
|
147
|
+
projmux setup --timeout 10s
|
|
148
|
+
projmux setup --non-interactive
|
|
144
149
|
```
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
shell, which `plain` and `csi-u` byte sequences projmux is listening for —
|
|
148
|
-
useful for hand-rolling a config in a terminal projmux does not yet know
|
|
149
|
-
about.
|
|
151
|
+
## Auto-Config: `projmux init`
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
## Auto-config: `projmux init`
|
|
156
|
-
|
|
157
|
-
`projmux init` is the supported fallback when `projmux setup` shows that a
|
|
158
|
-
terminal swallows shortcuts projmux already binds in tmux. It merges the
|
|
159
|
-
projmux CSI-u keybindings into a terminal emulator's config file. Default
|
|
160
|
-
mode is **dry-run** — nothing is written until you pass `--apply`. Applying
|
|
161
|
-
the merge always creates a timestamped backup of the previous file.
|
|
153
|
+
`projmux init` previews and optionally applies supported terminal mappings.
|
|
154
|
+
Default mode is dry-run; pass `--apply` to write changes with a timestamped
|
|
155
|
+
backup.
|
|
162
156
|
|
|
163
157
|
```sh
|
|
164
|
-
projmux init
|
|
165
|
-
projmux init ghostty
|
|
166
|
-
projmux init ghostty --apply
|
|
167
|
-
projmux init
|
|
168
|
-
projmux init --
|
|
169
|
-
projmux init --
|
|
158
|
+
projmux init
|
|
159
|
+
projmux init ghostty
|
|
160
|
+
projmux init ghostty --apply
|
|
161
|
+
projmux init windows-terminal --apply
|
|
162
|
+
projmux init --config /path/to/file
|
|
163
|
+
projmux init --allow-symlink
|
|
170
164
|
```
|
|
171
165
|
|
|
172
|
-
The merge is idempotent: bindings
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
`projmux init`
|
|
180
|
-
|
|
181
|
-
or `projmux shell`, use Settings > Keybindings or edit
|
|
182
|
-
`~/.config/projmux/keymap.toml`; see [Configuration](configuration.md#keymap-file).
|
|
183
|
-
|
|
184
|
-
### Supported terminals
|
|
185
|
-
|
|
186
|
-
#### Ghostty
|
|
187
|
-
|
|
188
|
-
- Detection: `TERM_PROGRAM=ghostty` or `GHOSTTY_RESOURCES_DIR` set.
|
|
189
|
-
- Config candidates (resolved in this order):
|
|
190
|
-
1. `<config-dir>/ghostty/config` — canonical default
|
|
191
|
-
2. `<config-dir>/ghostty/config.ghostty` — common dotfiles convention
|
|
192
|
-
|
|
193
|
-
`<config-dir>` honours `$XDG_CONFIG_HOME` first, then `$HOME/.config`.
|
|
194
|
-
When both candidates exist, init refuses to guess and asks for
|
|
195
|
-
`--config <path>` to disambiguate.
|
|
196
|
-
- Idempotency marker: bindings live inside a managed block delimited by
|
|
197
|
-
|
|
198
|
-
```text
|
|
199
|
-
# >>> projmux managed keybindings (do not edit between markers)
|
|
200
|
-
...
|
|
201
|
-
# <<< projmux managed keybindings
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Edit anything *outside* those markers freely; init re-renders the inside
|
|
205
|
-
on every `--apply`.
|
|
206
|
-
- Symlink guard: if the resolved config path is a symlink (typical when
|
|
207
|
-
dotfiles users symlink the terminal config into a tracked repo), init
|
|
208
|
-
refuses by default to avoid silently mutating the symlink target. Pass
|
|
209
|
-
`--allow-symlink` to opt in, or `--config <path>` to point at a
|
|
210
|
-
non-symlinked file.
|
|
211
|
-
|
|
212
|
-
#### Windows Terminal (including WSL)
|
|
213
|
-
|
|
214
|
-
- Detection: `WT_SESSION` set (native Windows), or `WSL_DISTRO_NAME` /
|
|
215
|
-
`WSL_INTEROP` set (running from inside WSL where the host terminal is
|
|
216
|
-
Windows Terminal).
|
|
217
|
-
- Config resolution:
|
|
218
|
-
- Native Windows: `%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_*\LocalState\settings.json`.
|
|
219
|
-
- WSL: resolves `%LOCALAPPDATA%` through `cmd.exe /c echo %LOCALAPPDATA%`
|
|
220
|
-
interop, then translates `C:\Users\...` into `/mnt/c/Users/...` so the
|
|
221
|
-
Linux side can read/write the file directly.
|
|
222
|
-
- Merge model: settings.json is JSONC (allows `//` and `/* */` comments,
|
|
223
|
-
trailing commas). Init parses, locates `actions[]` and `keybindings[]`,
|
|
224
|
-
and merges entries identified by the `User.projmux*` ID prefix. Anything
|
|
225
|
-
outside that prefix is preserved verbatim, including comments and
|
|
226
|
-
formatting where reasonable.
|
|
227
|
-
- Conflicts: a key already mapped to a non-projmux action is skipped with a
|
|
228
|
-
warning, same as Ghostty.
|
|
229
|
-
|
|
230
|
-
### Reading dry-run output
|
|
231
|
-
|
|
232
|
-
Each line of the dry-run plan prefixes the action with one of:
|
|
233
|
-
|
|
234
|
-
| Prefix | Meaning |
|
|
235
|
-
| --- | --- |
|
|
236
|
-
| `+` | New binding will be added. |
|
|
237
|
-
| `=` | Binding already matches; no change. |
|
|
238
|
-
| `!` | Trigger is already mapped to a *different* action; will be skipped. Resolve manually before applying. |
|
|
239
|
-
|
|
240
|
-
The trailing `note:` lines flag whether the config file does not yet exist
|
|
241
|
-
(it will be created), and the final line tells you whether you still need
|
|
242
|
-
`--apply` or whether the file is already up to date.
|
|
243
|
-
|
|
244
|
-
> 한국어 요약: `projmux init` 은 dry-run 이 기본이고 `--apply` 시 timestamped
|
|
245
|
-
> 백업 후 머지합니다. Ghostty 는 managed 블록 마커로 idempotent, 두 표준
|
|
246
|
-
> path 모두 인식하며 symlink 는 default 거절. Windows Terminal 은 WSL interop
|
|
247
|
-
> 으로 `/mnt/c/...` 경로까지 알아서 풀고, JSONC 형식의 `settings.json` 에
|
|
248
|
-
> `User.projmux*` prefix 로 머지합니다. 사용자가 같은 키를 다른 액션에 매핑한
|
|
249
|
-
> 경우 skip + warning.
|
|
250
|
-
|
|
251
|
-
## Manual fallback / advanced (CSI-u)
|
|
252
|
-
|
|
253
|
-
Use this section when:
|
|
254
|
-
|
|
255
|
-
- your terminal is not yet supported by `projmux init`, or
|
|
256
|
-
- you prefer to manage the config by hand, or
|
|
257
|
-
- `projmux setup` shows specific keys still being swallowed and you want to
|
|
258
|
-
redirect just those.
|
|
259
|
-
|
|
260
|
-
The fix is to map the swallowed keystroke to a CSI-u sequence the terminal
|
|
261
|
-
forwards to tmux unchanged. projmux binds CSI-u escapes to tmux's
|
|
262
|
-
`User0`–`User11` keys, so once the terminal forwards the sequence the
|
|
263
|
-
action fires.
|
|
264
|
-
|
|
265
|
-
### CSI-u Map
|
|
266
|
-
|
|
267
|
-
| CSI-u sequence | tmux key | Action |
|
|
268
|
-
| --- | --- | --- |
|
|
269
|
-
| `ESC [ 9001 u` | `User0` | Open AI split to the right |
|
|
270
|
-
| `ESC [ 9002 u` | `User1` | Open AI split below |
|
|
271
|
-
| `ESC [ 9003 u` | `User2` | Notify sidebar |
|
|
272
|
-
| `ESC [ 9004 u` | `User3` | Existing session popup |
|
|
273
|
-
| `ESC [ 9005 u` | `User4` | Project sidebar |
|
|
274
|
-
| `ESC [ 9006 u` | `User5` | AI split picker |
|
|
275
|
-
| `ESC [ 9007 u` | `User6` | Settings |
|
|
276
|
-
| `ESC [ 9008 u` | `User7` | New tmux window in the current pane directory |
|
|
277
|
-
| `ESC [ 9011 u` | `User10` | Rename the current tmux window |
|
|
278
|
-
| `ESC [ 9012 u` | `User11` | Rename the current tmux pane label |
|
|
279
|
-
| `ESC [ 9013 u` | `User12` | Project switcher popup |
|
|
280
|
-
|
|
281
|
-
> Previous/Next window (Alt-Shift-Left/Right) intentionally **do not** use a
|
|
282
|
-
> CSI-u detour — both projmux and modern terminals already agree on the
|
|
283
|
-
> xterm-standard modifier sequence (`\x1b[1;4D` / `\x1b[1;4C`), so tmux binds
|
|
284
|
-
> directly to `M-S-Left` / `M-S-Right`. This keeps the popup-side chord
|
|
285
|
-
> handler and the tmux root binding consuming the same sequence.
|
|
286
|
-
|
|
287
|
-
### Ghostty (manual)
|
|
288
|
-
|
|
289
|
-
`projmux init ghostty` is the maintained path. The block below is what the
|
|
290
|
-
managed region in `~/.config/ghostty/config` ends up looking like — useful
|
|
291
|
-
if you want to author it by hand or vet what init produces. Ghostty
|
|
292
|
-
keybinds use `keybind = trigger=action`; the `csi:` action sends a CSI
|
|
293
|
-
sequence without the leading `ESC [` bytes.
|
|
166
|
+
The merge is idempotent: matching bindings are no-ops, missing bindings are
|
|
167
|
+
added, and keys already mapped to a different user action are skipped with a
|
|
168
|
+
warning. `projmux init` does not read `keymap.toml`; direct tmux aliases still
|
|
169
|
+
belong in Settings > Keybindings or the keymap file.
|
|
170
|
+
|
|
171
|
+
### Ghostty
|
|
172
|
+
|
|
173
|
+
`projmux init ghostty` emits plain Meta bytes for `Alt-1` through `Alt-5` in
|
|
174
|
+
the managed block:
|
|
294
175
|
|
|
295
176
|
```text
|
|
296
|
-
|
|
297
|
-
keybind = alt+
|
|
298
|
-
keybind = alt+
|
|
299
|
-
keybind = alt+
|
|
300
|
-
keybind = alt+
|
|
301
|
-
keybind = alt+
|
|
302
|
-
|
|
303
|
-
keybind = ctrl+shift+r=csi:9001u
|
|
304
|
-
keybind = ctrl+shift+l=csi:9002u
|
|
305
|
-
|
|
306
|
-
keybind = ctrl+shift+n=csi:9008u
|
|
307
|
-
keybind = ctrl+m=csi:9011u
|
|
308
|
-
keybind = ctrl+shift+m=csi:9012u
|
|
177
|
+
# >>> projmux managed keybindings (do not edit between markers)
|
|
178
|
+
keybind = alt+1=text:\x1b1
|
|
179
|
+
keybind = alt+2=text:\x1b2
|
|
180
|
+
keybind = alt+3=text:\x1b3
|
|
181
|
+
keybind = alt+4=text:\x1b4
|
|
182
|
+
keybind = alt+5=text:\x1b5
|
|
183
|
+
# <<< projmux managed keybindings
|
|
309
184
|
```
|
|
310
185
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
projmux popup handlers.
|
|
316
|
-
|
|
317
|
-
Reload Ghostty or restart the terminal after changing the config.
|
|
186
|
+
Config candidates are `<config-dir>/ghostty/config` and
|
|
187
|
+
`<config-dir>/ghostty/config.ghostty`, with `$XDG_CONFIG_HOME` preferred over
|
|
188
|
+
`$HOME/.config`. If both exist, pass `--config <path>`. Symlinked configs are
|
|
189
|
+
refused by default; pass `--allow-symlink` to write through the link.
|
|
318
190
|
|
|
319
|
-
### Windows Terminal
|
|
191
|
+
### Windows Terminal
|
|
320
192
|
|
|
321
|
-
`projmux init windows-terminal`
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
shortcuts, while the split actions can send tmux prefix sequences directly. Escape bytes should be written as `\u001b`.
|
|
193
|
+
`projmux init windows-terminal` merges `sendInput` actions identified by the
|
|
194
|
+
`User.projmux*` ID prefix. The generated inputs use plain Meta bytes, tmux
|
|
195
|
+
prefix sequences for split actions, and xterm modifier-arrow sequences for
|
|
196
|
+
previous/next window:
|
|
326
197
|
|
|
327
198
|
```json
|
|
328
199
|
{
|
|
@@ -332,35 +203,14 @@ shortcuts, while the split actions can send tmux prefix sequences directly. Esca
|
|
|
332
203
|
{ "command": { "action": "sendInput", "input": "\u001b3" }, "id": "User.projmuxSessions" },
|
|
333
204
|
{ "command": { "action": "sendInput", "input": "\u001b4" }, "id": "User.projmuxAIPicker" },
|
|
334
205
|
{ "command": { "action": "sendInput", "input": "\u001b5" }, "id": "User.projmuxSettings" },
|
|
335
|
-
{ "command": { "action": "sendInput", "input": "\u001b6" }, "id": "User.projmuxSwitch" },
|
|
336
206
|
{ "command": { "action": "sendInput", "input": "\u0002r" }, "id": "User.projmuxAISplitRight" },
|
|
337
207
|
{ "command": { "action": "sendInput", "input": "\u0002l" }, "id": "User.projmuxAISplitDown" },
|
|
338
208
|
{ "command": { "action": "sendInput", "input": "\u000e" }, "id": "User.projmuxNewWindow" },
|
|
339
209
|
{ "command": { "action": "sendInput", "input": "\u001b[1;4D" }, "id": "User.projmuxPrevWindow" },
|
|
340
|
-
{ "command": { "action": "sendInput", "input": "\u001b[1;4C" }, "id": "User.projmuxNextWindow" }
|
|
341
|
-
{ "command": { "action": "sendInput", "input": "\u001b[9011u" }, "id": "User.projmuxRenameWindow" },
|
|
342
|
-
{ "command": { "action": "sendInput", "input": "\u001b[9012u" }, "id": "User.projmuxRenamePane" }
|
|
343
|
-
],
|
|
344
|
-
"keybindings": [
|
|
345
|
-
{ "id": "User.projmuxSidebar", "keys": "alt+1" },
|
|
346
|
-
{ "id": "User.projmuxNotifySidebar", "keys": "alt+2" },
|
|
347
|
-
{ "id": "User.projmuxSessions", "keys": "alt+3" },
|
|
348
|
-
{ "id": "User.projmuxAIPicker", "keys": "alt+4" },
|
|
349
|
-
{ "id": "User.projmuxSettings", "keys": "alt+5" },
|
|
350
|
-
{ "id": "User.projmuxSwitch", "keys": "alt+6" },
|
|
351
|
-
{ "id": "User.projmuxAISplitRight", "keys": "ctrl+shift+r" },
|
|
352
|
-
{ "id": "User.projmuxAISplitDown", "keys": "ctrl+shift+l" },
|
|
353
|
-
{ "id": "User.projmuxNewWindow", "keys": "ctrl+n" },
|
|
354
|
-
{ "id": "User.projmuxPrevWindow", "keys": "alt+shift+left" },
|
|
355
|
-
{ "id": "User.projmuxNextWindow", "keys": "alt+shift+right" },
|
|
356
|
-
{ "id": "User.projmuxRenameWindow", "keys": "ctrl+m" },
|
|
357
|
-
{ "id": "User.projmuxRenamePane", "keys": "ctrl+shift+m" }
|
|
210
|
+
{ "command": { "action": "sendInput", "input": "\u001b[1;4C" }, "id": "User.projmuxNextWindow" }
|
|
358
211
|
]
|
|
359
212
|
}
|
|
360
213
|
```
|
|
361
214
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
`Ctrl-Shift-M` still use CSI-u so tmux can distinguish rename commands from
|
|
365
|
-
plain Enter. If a key is already bound by Windows Terminal, remove or change
|
|
366
|
-
the conflicting `keybindings` entry before adding the projmux binding.
|
|
215
|
+
Windows Terminal config is JSONC; comments and unrelated user entries are
|
|
216
|
+
preserved where possible. Conflicting user-owned keys are skipped.
|
|
@@ -16,6 +16,9 @@ The fzf compatibility surface for the native engine is tracked in
|
|
|
16
16
|
execution, and result contracts, while moving visual composition into
|
|
17
17
|
`projmuxpicker` so projmux can evolve a native picker design without coupling
|
|
18
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.
|
|
19
22
|
- `internal/ui/pickercompat` remains an internal compatibility mapper between
|
|
20
23
|
the old option/result shape and the backend-neutral `picker.Options`
|
|
21
24
|
contract. It is not a runtime backend. App code should
|
|
@@ -29,7 +32,7 @@ The fzf compatibility surface for the native engine is tracked in
|
|
|
29
32
|
- The native picker supports ranked fuzzy search/filter, arrow-key selection in
|
|
30
33
|
normal CSI and tmux application-cursor modes, Enter, Esc, Ctrl-C, Backspace,
|
|
31
34
|
Ctrl-U, Ctrl-W, PageUp/PageDown, Home/End, modified CSI keys, custom expect
|
|
32
|
-
keys such as Ctrl-X/Alt-P, printable expect keys such as notify `x`, control
|
|
35
|
+
keys such as Ctrl-X/Alt-P, printable expect keys such as notify `a`/`x`, control
|
|
33
36
|
expect keys such as notify `Ctrl-X`, `start:pos(N)` initial focus, preview
|
|
34
37
|
command output, preview cycle command bindings, and sidebar focus command
|
|
35
38
|
bindings.
|
|
@@ -40,9 +43,10 @@ The fzf compatibility surface for the native engine is tracked in
|
|
|
40
43
|
- Typed-query prompts support cursor-aware insertion/deletion with a visible
|
|
41
44
|
prompt cursor, Left/Right, Ctrl-A/E, Delete, Backspace, Ctrl-U, and Ctrl-W
|
|
42
45
|
for settings path entry.
|
|
43
|
-
- The native key parser
|
|
44
|
-
such as `ESC [
|
|
45
|
-
|
|
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.
|
|
46
50
|
- Native interactive picker screens use an alternate screen lifecycle to better
|
|
47
51
|
match fzf fullscreen behavior and restore the tmux pane after exit. Frame
|
|
48
52
|
updates and screen exit both return to column 0 before emitting terminal
|
|
@@ -27,10 +27,10 @@ native picker engine and is not a public dependency-policy change.
|
|
|
27
27
|
| selected multi-line marker | selected switch/session/notify cards | Covered for app multiline rows | native uses the same compact pointer-width red `▌` gutter as the first selected project line, and metadata lines align to the project-name column without the old deeper indent; `nativeContinuation`; `TestNativeInteractiveRendersSelectedMultilineContinuationMarker`; `TestInteractiveRowLinesUsesCompactSelectedMetaIndent`; `TestInteractiveRowLinesAlignsUnselectedMetaWithProjectName` |
|
|
28
28
|
| fzf current row colors | simple and multi-line rows | Covered for app rows | `nativeCurrentStart`, `nativePointer`; pointer/continuation gutter tokens carry the current-row background; `TestNativeSelectedContentKeepsCurrentStyleAfterReset`; `TestNativeInteractiveUsesCurrentStyleForSimpleSelection` |
|
|
29
29
|
| `--expect` keys | Enter/Ctrl-X/Alt-P/notify keys | Covered | `pickercompat.PickerOptions`; `TestNativeInteractiveSupportsCustomExpectKeys` |
|
|
30
|
-
| printable expect keys | notify sidebar `
|
|
30
|
+
| printable expect keys | notify sidebar `a` ack and `x` non-critical clear | Covered | `TestNativeInteractiveSupportsPrintableExpectKeys`; Docker no-fzf e2e |
|
|
31
31
|
| control expect keys | notify sidebar `Ctrl-X`, settings `Ctrl-Alt-S` close | Covered | `TestNativeInteractiveSupportsControlExpectKeys`; `TestNativeInteractiveSupportsControlAltCloseKeys` |
|
|
32
32
|
| close `--bind key:abort` | Esc, Ctrl-C, Alt-N, Ctrl-Alt-S variants | Covered | `CloseActions`; `TestNativeRunnerUsesSharedCloseActions` |
|
|
33
|
-
| terminal
|
|
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
35
|
| `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
36
|
| `start:pos(N)` | switch sidebar initial row | Covered | `pickercompat.PickerOptions`/`pickercompat.OptionsFromPicker`; `TestPickerOptionsFromCompatPickerMapsStartPosToInitialIndex`; `TestPickerOptionsMapsCompatBindingsToContractActions` |
|
|
@@ -43,7 +43,7 @@ native picker engine and is not a public dependency-policy change.
|
|
|
43
43
|
| query cursor editing | typed settings path prompts | Covered | Left/Right, Ctrl-A/E, Delete, Backspace, Ctrl-U/W query editing plus visible prompt cursor; `TestNativeInteractiveEditsTypedQueryAtCursor`; `TestNativeInteractiveSupportsQueryLineEditingKeys`; `TestNativeInteractiveCtrlUDeletesBeforeCursor`; `TestNativePromptLineRendersQueryCursor` |
|
|
44
44
|
| terminal arrow key variants | interactive selection in tmux/docker | Covered | CSI, SS3/application cursor, modified CSI tests; up/down movement wraps at list boundaries while PageUp/PageDown and Home/End remain clamped or explicit jumps; app TTY `/dev/tty` fallback; raw TTY EOF polling keeps split ESC sequences from leaking into the query; `TestNativeInteractiveWrapsPreviousNavigationKeys`; `TestNativeInteractiveWrapsNextNavigationKeys`; `TestNativeInteractiveJumpNavigationRemainsClamped` |
|
|
45
45
|
| mouse support | optional fzf mouse picker interaction | Partially covered | native enables SGR mouse reporting in interactive alternate-screen mode, primary mouse down focuses the clicked row, primary mouse up applies it, and wheel input moves selection; drag gestures remain outside this POC; `TestNativeInteractiveSelectsOnMouseRelease`; `TestNativeInteractiveMouseDownOnlyFocuses`; `TestNativeInteractiveIgnoresMouseReleaseBeforeDown`; `TestNativeInteractiveSupportsMouseWheelSelection`; Docker no-fzf e2e clicks the AI settings row under a PTY |
|
|
46
|
-
| fzf navigation keys | interactive selection in searchable lists | Covered | native maps
|
|
46
|
+
| fzf navigation keys | interactive selection in searchable lists | Covered | native maps modified-key `Ctrl-J` plus `Ctrl-N` to down and `Ctrl-P`/`Ctrl-K` to up when not claimed by a custom action; up/down-family movement is safe on empty lists; raw LF remains Enter for PTY compatibility; `TestNativeInteractiveSupportsFZFNavigationKeys`; `TestNativeInteractiveNavigationKeysAreNoopForEmptyList` |
|
|
47
47
|
| 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
48
|
| frame content width | fzf border inner width | Covered | `ContentLayout` uses the frame inner width so separators and rows reach the right border; `TestRendererContentLayoutUsesFrameInnerWidth` |
|
|
49
49
|
| 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` |
|
|
@@ -108,7 +108,7 @@ contract; native popups still rely on the existing borderless tmux popup path.
|
|
|
108
108
|
preview cursor, selects `bravo-web`, and asserts tmux reports the selected
|
|
109
109
|
session's active target on the expected window with the expected pane path.
|
|
110
110
|
- `notify sidebar`: native routing is unit-covered; Docker no-fzf e2e pushes a
|
|
111
|
-
notification, presses printable expect key `
|
|
111
|
+
notification, presses printable expect key `a`, and verifies the row is acked.
|
|
112
112
|
|
|
113
113
|
## Experimental Boundaries
|
|
114
114
|
|
package/docs/notify-queue.md
CHANGED
|
@@ -93,12 +93,14 @@ repeatable filters. Without `--live`, this command reads only the queue and
|
|
|
93
93
|
preserves the stable JSON array used by scripts.
|
|
94
94
|
|
|
95
95
|
`--ui=sidebar` opens the notify queue as an interactive right-side list when
|
|
96
|
-
run inside the tmux popup surface.
|
|
97
|
-
acks the row after focus succeeds.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
run inside the tmux popup surface. Selecting a row focuses the selected target
|
|
97
|
+
pane and acks the row after focus succeeds. The surface actions
|
|
98
|
+
`NotifySidebar:Ack`, `NotifySidebar:ClearNonCritical`, and
|
|
99
|
+
`NotifySidebar:ClearAll` are internal picker actions; direct launch aliases
|
|
100
|
+
are edited in Settings, while internal picker aliases are adjusted in
|
|
101
|
+
`keymap.toml` when needed. Runtime footer key guides read the merged keymap
|
|
102
|
+
and show the default alias when present, otherwise the first configured alias,
|
|
103
|
+
so custom aliases do not make the UI stale. Rows are intentionally compact: the visible label keeps
|
|
102
104
|
notification text first, then age, project, window, and pane metadata; hidden
|
|
103
105
|
queue ids remain action values but the sidebar has no search input and
|
|
104
106
|
intentionally does not expose a separate metadata detail view.
|