projmux 0.6.4 → 0.6.6
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/docs/agent-workflow.md +17 -8
- package/docs/ai-agent-shortcuts.md +15 -1
- package/docs/cli.md +38 -17
- package/docs/configuration.md +119 -19
- package/docs/globalization.md +212 -7
- package/docs/hooks.md +5 -1
- package/docs/keybindings.md +21 -15
- package/docs/native-picker-parity.md +7 -4
- package/docs/npm-distribution.md +5 -3
- package/docs/pr-guideline.md +10 -0
- package/docs/settings-ia.md +25 -6
- package/docs/statusbar.md +30 -2
- package/docs/testing.md +122 -0
- package/docs/theme-palette.md +93 -14
- package/package.json +7 -7
package/docs/globalization.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Globalization Contract
|
|
2
2
|
|
|
3
3
|
This document is the inventory and contract for globalization. Phase 0 defined
|
|
4
|
-
the user-facing text boundary and literal preservation rules.
|
|
5
|
-
message catalog
|
|
6
|
-
|
|
4
|
+
the user-facing text boundary and literal preservation rules. Later phases add
|
|
5
|
+
the message catalog, locale formatters, runtime surface migration, and the
|
|
6
|
+
Phase 5 user locale override surface.
|
|
7
7
|
|
|
8
8
|
## Phase 0 Scope
|
|
9
9
|
|
|
@@ -210,13 +210,16 @@ Catalog policy:
|
|
|
210
210
|
|
|
211
211
|
Locale resolution:
|
|
212
212
|
|
|
213
|
-
- Precedence is explicit override/API, then `
|
|
214
|
-
`LANG`, then `en-US`.
|
|
213
|
+
- Precedence is explicit override/API, then `PROJMUX_LOCALE`, then global
|
|
214
|
+
`[ui] locale`, then `LC_ALL`, then `LC_MESSAGES`, then `LANG`, then `en-US`.
|
|
215
215
|
- Common POSIX forms normalize into catalog tags: `ko`, `ko_KR.UTF-8`, and
|
|
216
216
|
`ko-KR` become `ko-KR`; `en` and `en_US` become `en-US`.
|
|
217
217
|
- `C` and `POSIX` locales are skipped as non-user-language values.
|
|
218
|
-
-
|
|
219
|
-
|
|
218
|
+
- Supported UI locales are currently `en-US` and `ko-KR`. Unsupported locale
|
|
219
|
+
tags fall back to `en-US` and keep their source attached so Settings can show
|
|
220
|
+
a warning.
|
|
221
|
+
- `auto` is a setting value, not a catalog locale. It means "use the
|
|
222
|
+
auto-detected environment rung".
|
|
220
223
|
|
|
221
224
|
API convention:
|
|
222
225
|
|
|
@@ -242,3 +245,205 @@ Contribution convention:
|
|
|
242
245
|
- Runtime migrations should be narrow by surface. Move a string family behind
|
|
243
246
|
the catalog only when its tests can show literal preservation and fallback
|
|
244
247
|
behavior for that surface.
|
|
248
|
+
|
|
249
|
+
## Phase 2 Formatter Foundation
|
|
250
|
+
|
|
251
|
+
Package:
|
|
252
|
+
|
|
253
|
+
- `internal/i18n`
|
|
254
|
+
|
|
255
|
+
Formatter policy:
|
|
256
|
+
|
|
257
|
+
- Formatter functions localize grammar, units, and labels. They do not translate
|
|
258
|
+
caller-owned payload values such as product names, commands, paths, provider
|
|
259
|
+
text, tmux targets, or enum/source data inserted as arguments.
|
|
260
|
+
- Formatter functions accept a `FormatVariant`. `i18n.FormatFull` is for
|
|
261
|
+
prose-friendly labels and `i18n.FormatCompact` is for dense terminal surfaces.
|
|
262
|
+
- Unsupported or empty locales fall back to `en-US`. Korean locale forms
|
|
263
|
+
normalized by `ResolveLocale` use the Korean formatter rules.
|
|
264
|
+
- Relative age has a pinned just-now window of less than five seconds:
|
|
265
|
+
`just now` for `en-US`, `방금 전` for `ko-KR`.
|
|
266
|
+
|
|
267
|
+
Formatter API:
|
|
268
|
+
|
|
269
|
+
- `i18n.FormatRelativeAge(age, locale, variant)` renders elapsed age, for
|
|
270
|
+
example `3m ago` in compact `en-US` and `36초 전` in compact `ko-KR`.
|
|
271
|
+
- `i18n.FormatDuration(duration, locale, variant)` renders the largest whole
|
|
272
|
+
unit in seconds, minutes, hours, or days.
|
|
273
|
+
- `i18n.FormatCount(count, subject, locale, variant)` applies locale-specific
|
|
274
|
+
count grammar for owned subjects such as `i18n.CountNotifications` while
|
|
275
|
+
preserving the numeric value.
|
|
276
|
+
- `i18n.FormatList(items, locale, variant)` joins caller-owned item strings
|
|
277
|
+
using locale-specific separators without translating item payloads.
|
|
278
|
+
- `i18n.FormatStatusToken(token, locale, variant)` renders shared terminal
|
|
279
|
+
status labels for known `i18n.StatusToken` values.
|
|
280
|
+
- `i18n.FormatTargetLabel(kind, number, locale, variant)` localizes the target
|
|
281
|
+
label, such as window or pane, while preserving the target number.
|
|
282
|
+
|
|
283
|
+
Width-safe rendering:
|
|
284
|
+
|
|
285
|
+
- Use `i18n.TerminalCellWidth(value)` when terminal layout depends on visible
|
|
286
|
+
width. It measures terminal cells, not bytes or runes.
|
|
287
|
+
- Use `i18n.TruncateTerminalCells(value, maxCells)` before placing
|
|
288
|
+
locale-specific output into fixed-width terminal columns.
|
|
289
|
+
- ANSI escape sequences and tmux style wrappers such as `#[fg=red]` are
|
|
290
|
+
zero-width for measurement and truncation. Truncation preserves those wrappers
|
|
291
|
+
in the returned string while clipping only visible content.
|
|
292
|
+
- Width clipping is a rendering concern. Keep translated format strings and
|
|
293
|
+
caller-owned payload arguments separate until the final render step.
|
|
294
|
+
|
|
295
|
+
## Phase 3 Notify Runtime Migration
|
|
296
|
+
|
|
297
|
+
Runtime surfaces migrated:
|
|
298
|
+
|
|
299
|
+
- AI desktop notification summaries for Codex and Claude hook payloads.
|
|
300
|
+
- In-app notify queue table/sidebar/statusbar display text for AI entries.
|
|
301
|
+
- Notify live explanation text for `projmux notify list --live`.
|
|
302
|
+
- Sidebar/table/statusbar age formatting and sidebar stale/gone/target labels.
|
|
303
|
+
|
|
304
|
+
Storage and dispatch policy:
|
|
305
|
+
|
|
306
|
+
- Queue storage remains schema-compatible. Stored `notify.Notification.Text`,
|
|
307
|
+
metadata keys, severity/source enum values, targets, and timestamps are not
|
|
308
|
+
localized before persistence.
|
|
309
|
+
- OS notification click/focus routing is unchanged. Locale formatting only
|
|
310
|
+
changes the summary/body strings sent to the configured notifier.
|
|
311
|
+
- JSON queue payloads keep raw queue entries. Localized live-row explanation
|
|
312
|
+
and row display text may appear in `notify list --live` row fields because
|
|
313
|
+
those fields are render/report output, not stored queue schema.
|
|
314
|
+
|
|
315
|
+
Literal preservation and parity rules:
|
|
316
|
+
|
|
317
|
+
- Translate only catalog-owned category labels such as `Response complete`,
|
|
318
|
+
`Approval required`, `Input required`, `Error`, `Subagent stopped`, and
|
|
319
|
+
`Teammate waiting`.
|
|
320
|
+
- Preserve provider-owned payloads verbatim: `Codex`, `Claude`, tool names,
|
|
321
|
+
commands, paths, URLs, query strings, transcript excerpts, teammate IDs, and
|
|
322
|
+
subagent IDs.
|
|
323
|
+
- Desktop notification summaries and in-app queue display text must use the
|
|
324
|
+
same rendered category labels for the same locale while preserving the same
|
|
325
|
+
literal payload body.
|
|
326
|
+
- The response-complete fallback body `Ready` is treated as provider state and
|
|
327
|
+
suppressed in display detail; it is not translated or persisted differently.
|
|
328
|
+
|
|
329
|
+
## Phase 4 Settings And Popup Guidance
|
|
330
|
+
|
|
331
|
+
Runtime surfaces migrated:
|
|
332
|
+
|
|
333
|
+
- Settings root title, scope chips, root row labels/descriptions, common row
|
|
334
|
+
labels/previews/disabled reasons, and shared Settings footer guidance.
|
|
335
|
+
- Native picker search label, empty-list row, and line-mode close prompt.
|
|
336
|
+
- Shell welcome/update guide text and Settings > About > Welcome viewer body.
|
|
337
|
+
|
|
338
|
+
Literal preservation:
|
|
339
|
+
|
|
340
|
+
- Key names such as `Enter`, `Esc`, `Ctrl-b d`, and `s` remain literal inside
|
|
341
|
+
localized guide sentences.
|
|
342
|
+
- Commands, env/config names, and paths such as `projmux shell`,
|
|
343
|
+
`tmux -L projmux kill-server`, `PROJMUX_PROJDIR`, and
|
|
344
|
+
`~/.config/projmux/projdir` remain literal payload text.
|
|
345
|
+
- Project names, paths, provider payloads, enum values, and hook/action IDs are
|
|
346
|
+
not translated.
|
|
347
|
+
|
|
348
|
+
Width policy:
|
|
349
|
+
|
|
350
|
+
- Settings row padding now uses terminal cell width instead of byte length so
|
|
351
|
+
Korean labels align with the existing ANSI row chrome.
|
|
352
|
+
- Native picker prompt, empty row, and footer render through existing frame
|
|
353
|
+
truncation/padding helpers; focused tests cover `en-US`/`ko-KR` output and
|
|
354
|
+
long Korean styled guidance.
|
|
355
|
+
|
|
356
|
+
## Phase 5 Locale Settings And Docs
|
|
357
|
+
|
|
358
|
+
User override surface:
|
|
359
|
+
|
|
360
|
+
- Environment override: `PROJMUX_LOCALE=auto|en-US|ko-KR`.
|
|
361
|
+
- Global config override: `~/.config/projmux/config.toml`:
|
|
362
|
+
|
|
363
|
+
```toml
|
|
364
|
+
[ui]
|
|
365
|
+
locale = "auto" # auto | en-US | ko-KR
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
- Settings surface: `Settings > Appearance > Language / Locale`.
|
|
369
|
+
|
|
370
|
+
Resolution policy:
|
|
371
|
+
|
|
372
|
+
1. API/explicit override used by tests and internal callers.
|
|
373
|
+
2. `PROJMUX_LOCALE` when set to a non-empty value other than `auto`.
|
|
374
|
+
3. Global/user `[ui] locale` when set to a non-empty value other than `auto`.
|
|
375
|
+
4. Auto-detected environment, in order: `LC_ALL`, `LC_MESSAGES`, `LANG`.
|
|
376
|
+
5. Built-in fallback `en-US`.
|
|
377
|
+
|
|
378
|
+
`auto` in either `PROJMUX_LOCALE` or `[ui] locale` does not pin the UI to a
|
|
379
|
+
literal `auto` locale. It re-enters the auto-detection path and Settings shows
|
|
380
|
+
the currently detected locale and source, such as `ko-KR from LC_MESSAGES env`.
|
|
381
|
+
|
|
382
|
+
Fallback and warning policy:
|
|
383
|
+
|
|
384
|
+
- `en-US` and `ko-KR` are the only supported UI locales in Phase 5.
|
|
385
|
+
- Unsupported locale tags such as `ja-JP` or `fr-FR` fall back to `en-US`.
|
|
386
|
+
- Settings displays the unsupported tag, its source (`PROJMUX_LOCALE`,
|
|
387
|
+
`~/.config/projmux/config.toml`, `LC_ALL`, `LC_MESSAGES`, or `LANG`), and
|
|
388
|
+
the effective `en-US` fallback.
|
|
389
|
+
- Project-local locale override is intentionally out of scope. Any parser
|
|
390
|
+
support for `[ui] locale` exists only because global and project config share
|
|
391
|
+
the same TOML implementation; runtime locale resolution consumes only the
|
|
392
|
+
global/user config path.
|
|
393
|
+
|
|
394
|
+
Literal preservation remains unchanged:
|
|
395
|
+
|
|
396
|
+
- Config keys and env vars such as `[ui].locale`, `PROJMUX_LOCALE`,
|
|
397
|
+
`LC_MESSAGES`, and `~/.config/projmux/config.toml` remain literal.
|
|
398
|
+
- Locale enum values such as `auto`, `en-US`, and `ko-KR` remain literal.
|
|
399
|
+
- Commands, paths, provider payloads, tmux format strings, and key names remain
|
|
400
|
+
caller-owned payload text and are not translated.
|
|
401
|
+
|
|
402
|
+
## Phase 6 Governance
|
|
403
|
+
|
|
404
|
+
Phase 6 prevents new user-facing strings from bypassing the catalog while
|
|
405
|
+
keeping the existing literal/data/debug boundary explicit.
|
|
406
|
+
|
|
407
|
+
Governance checks:
|
|
408
|
+
|
|
409
|
+
- `internal/i18n` owns the Go string-literal audit helper.
|
|
410
|
+
- `go test ./internal/i18n` includes synthetic audit coverage for Korean
|
|
411
|
+
candidates, English user-facing candidates, and ignored literal/data/debug
|
|
412
|
+
examples.
|
|
413
|
+
- The current repo guard scans runtime Go files for new Korean string literals
|
|
414
|
+
outside the catalog, formatter locale fragments, tests, `testdata`, and
|
|
415
|
+
comments.
|
|
416
|
+
- The catalog completeness tests require `en-US` fallback coverage for every
|
|
417
|
+
embedded default catalog key and required `ko-KR` coverage for migrated
|
|
418
|
+
`notify.ai.`, `notify.live.`, `settings.`, `picker.`, `welcome.`, `update.`,
|
|
419
|
+
and `help.` surfaces.
|
|
420
|
+
|
|
421
|
+
Contributor rule:
|
|
422
|
+
|
|
423
|
+
- New normal UX copy must be added as a stable catalog key with an `en-US`
|
|
424
|
+
fallback entry and focused test coverage.
|
|
425
|
+
- If the string is intentionally not translated, classify it in review notes as
|
|
426
|
+
`literal`, `data`, or `debug-only` and preserve it verbatim.
|
|
427
|
+
- Korean product UI strings must not be hardcoded in runtime Go. Add or extend
|
|
428
|
+
the catalog entry instead.
|
|
429
|
+
- Do not translate provider payloads, commands, config keys, paths, env vars,
|
|
430
|
+
locale enum values, product names, or tmux/ANSI syntax.
|
|
431
|
+
|
|
432
|
+
Debug/log/internal error classification:
|
|
433
|
+
|
|
434
|
+
- Catalog text when it is normal UX, shown in a picker/sidebar/statusbar,
|
|
435
|
+
desktop notification, user-actionable CLI output, or other expected product
|
|
436
|
+
surface.
|
|
437
|
+
- Do not catalog debug logs, trace strings, diagnostics, or wrapped internal Go
|
|
438
|
+
errors unless that exact text is promoted into normal UX.
|
|
439
|
+
- If a diagnostic is surfaced as normal UX, split it: catalog the user-facing
|
|
440
|
+
explanation or action hint, and keep raw error details, paths, commands, and
|
|
441
|
+
provider payload as preserved data.
|
|
442
|
+
|
|
443
|
+
Audit operation:
|
|
444
|
+
|
|
445
|
+
- Run `GOCACHE=/tmp/projmux-go-cache go test ./internal/i18n` after changing
|
|
446
|
+
i18n helpers, catalog data, migrated UI strings, or locale policy.
|
|
447
|
+
- For broader validation, continue to run the standard repo gates in order:
|
|
448
|
+
`make fmt`, `make fix`, `make test`, `make test-integration`, and
|
|
449
|
+
`make test-e2e` where applicable.
|
package/docs/hooks.md
CHANGED
|
@@ -206,7 +206,11 @@ desktop sender and receives positional arguments
|
|
|
206
206
|
`summary body urgency app-name tag group icon-path`. That `urgency` value is
|
|
207
207
|
the OS notification urgency, not the notify-queue severity. AI approval,
|
|
208
208
|
input, selection, and confirmation rows can stay critical in the queue and UI
|
|
209
|
-
while the desktop notification hook receives `normal`.
|
|
209
|
+
while the desktop notification hook receives `normal`. Live AI status badges
|
|
210
|
+
are a third surface: permission/input-required panes use the action-required
|
|
211
|
+
amber-orange status role, response-complete panes use success green, and
|
|
212
|
+
in-progress panes use progress yellow. They do not inherit the critical queue
|
|
213
|
+
severity, and permission/input status badges do not use red.
|
|
210
214
|
|
|
211
215
|
## Codex Hooks Engine
|
|
212
216
|
|
package/docs/keybindings.md
CHANGED
|
@@ -12,8 +12,7 @@ supported fallback guidance.
|
|
|
12
12
|
The recommended path when a key does not fire:
|
|
13
13
|
|
|
14
14
|
1. Try the key inside `projmux shell`.
|
|
15
|
-
2.
|
|
16
|
-
tmux, to see which bytes reach the process.
|
|
15
|
+
2. Run `projmux setup` outside tmux to see which bytes reach the process.
|
|
17
16
|
3. For supported terminals, preview `projmux init [terminal]`; add `--apply`
|
|
18
17
|
only after reviewing the merge.
|
|
19
18
|
4. For unsupported terminals, configure plain Meta bytes or add a tmux alias in
|
|
@@ -47,6 +46,12 @@ keymap actions, pane switching, window switching, and rename actions remain
|
|
|
47
46
|
visible. Transport-dependent rows show the default transport key separately
|
|
48
47
|
from editable plain aliases.
|
|
49
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
|
+
|
|
50
55
|
Optional direct aliases can be added for actions such as:
|
|
51
56
|
|
|
52
57
|
| Canonical action | Meaning |
|
|
@@ -67,19 +72,20 @@ without storing or replacing the transport default. Rename actions no longer
|
|
|
67
72
|
have a built-in terminal fallback; use tmux's prefix rename flow or configure
|
|
68
73
|
an explicit safe alias where the action is editable.
|
|
69
74
|
|
|
70
|
-
##
|
|
75
|
+
## Product Requirements
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
Settings > Keybindings stays a discovery surface. It must continue to expose
|
|
78
|
+
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.
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
Windows Terminal and Ghostty-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
The product model does not support `UserN` or `CSI-u` as fallback guidance.
|
|
85
|
+
Windows Terminal and Ghostty adapters use built-in plain Meta/control bytes or
|
|
86
|
+
xterm modifier sequences where possible. If a key cannot be represented that
|
|
87
|
+
way, leave it as a non-editable unsupported or diagnostic row instead of
|
|
88
|
+
preserving a User-key or CSI-u fallback.
|
|
83
89
|
|
|
84
90
|
## Picker Actions
|
|
85
91
|
|
|
@@ -131,8 +137,8 @@ names: `ProjectSidebarToggle`, `NotifySidebarToggle`, `SessionPopupToggle`,
|
|
|
131
137
|
## Diagnose: `projmux setup`
|
|
132
138
|
|
|
133
139
|
Run `projmux setup` outside tmux to find out which projmux keys reach the raw
|
|
134
|
-
terminal.
|
|
135
|
-
|
|
140
|
+
terminal. Settings > Keybindings remains the action/alias editor; setup is the
|
|
141
|
+
terminal delivery diagnostic.
|
|
136
142
|
|
|
137
143
|
| Status | Meaning |
|
|
138
144
|
| --- | --- |
|
|
@@ -46,8 +46,9 @@ native picker engine and is not a public dependency-policy change.
|
|
|
46
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
|
+
| 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` |
|
|
49
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` |
|
|
50
|
-
| 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
|
|
51
|
+
| 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` |
|
|
51
52
|
| 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` |
|
|
52
53
|
|
|
53
54
|
## Native Surface Architecture
|
|
@@ -72,9 +73,11 @@ native picker engine and is not a public dependency-policy change.
|
|
|
72
73
|
## Frame Chrome ANSI
|
|
73
74
|
|
|
74
75
|
Native picker frame chrome is normalized in `internal/ui/projmuxpicker`.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
Titlebar text, title dividers, and chip-strip gaps inherit the frame
|
|
77
|
+
background/foreground instead of applying a second titlebar overlay ANSI layer;
|
|
78
|
+
chip bodies can still carry active/inactive/disabled tones. Search prompt and
|
|
79
|
+
footer separators fill the available frame width, and header, row, footer, and
|
|
80
|
+
preview lines close any active SGR style before padding or frame borders can
|
|
78
81
|
inherit it. This phase does not add popup modes or change the `popup-toggle`
|
|
79
82
|
contract; native popups still rely on the existing borderless tmux popup path.
|
|
80
83
|
|
package/docs/npm-distribution.md
CHANGED
|
@@ -31,13 +31,15 @@ make npm-pack
|
|
|
31
31
|
or:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
scripts/package-npm.sh --version
|
|
34
|
+
scripts/package-npm.sh --version 1.2.3 --out /tmp/projmux-npm --pack
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
The script stages package directories under `dist/npm` by default. It builds
|
|
38
38
|
the Go binary for each supported platform, copies package metadata and docs,
|
|
39
|
-
updates package versions in the staged copies,
|
|
40
|
-
|
|
39
|
+
updates package versions in the staged copies, generates root
|
|
40
|
+
`optionalDependencies` for the supported platform packages using the same
|
|
41
|
+
version, verifies the staged metadata is internally consistent, then runs
|
|
42
|
+
`npm pack --dry-run` when `--pack` is set.
|
|
41
43
|
|
|
42
44
|
## Publish Order
|
|
43
45
|
|
package/docs/pr-guideline.md
CHANGED
|
@@ -69,6 +69,11 @@ Use this template:
|
|
|
69
69
|
- [ ] make fmt-check
|
|
70
70
|
- [ ] make test
|
|
71
71
|
- [ ] manual verification step (if relevant)
|
|
72
|
+
|
|
73
|
+
## Globalization
|
|
74
|
+
- [ ] No user-facing string changes.
|
|
75
|
+
- [ ] User-facing strings are behind `internal/i18n` catalog keys with tests.
|
|
76
|
+
- [ ] Non-translated strings are classified as literal/data/debug-only.
|
|
72
77
|
```
|
|
73
78
|
|
|
74
79
|
Notes:
|
|
@@ -76,6 +81,11 @@ Notes:
|
|
|
76
81
|
- **Why** matters more than **what**. Diff already shows the what.
|
|
77
82
|
- Reference issues with `Closes #<n>` so they auto-close on merge.
|
|
78
83
|
- Mention follow-ups explicitly when scope was deliberately deferred.
|
|
84
|
+
- For any new or changed user-facing text, check exactly one Globalization
|
|
85
|
+
item. Normal UX copy needs a catalog key and test coverage. Commands, paths,
|
|
86
|
+
config keys, env vars, provider payloads, locale enum values, product names,
|
|
87
|
+
debug logs, and internal diagnostics may stay out of the catalog only when
|
|
88
|
+
explicitly classified in the PR body.
|
|
79
89
|
|
|
80
90
|
## Branch protection in effect
|
|
81
91
|
|
package/docs/settings-ia.md
CHANGED
|
@@ -15,13 +15,21 @@ view-first layout:
|
|
|
15
15
|
actions live inside that view.
|
|
16
16
|
- `Settings > Project Picker > Project Root` shows effective and saved values
|
|
17
17
|
first, then the edit actions, then the explanatory hints.
|
|
18
|
-
- `Settings > Keybindings` is
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
- `Settings > Keybindings` is a single action list plus a simple action detail.
|
|
19
|
+
It does not expose `Bindings`, `Diagnostic`, `Probe`, or `Init` as first-class
|
|
20
|
+
chips/tabs in the Settings root flow.
|
|
21
|
+
- `Settings > Keybindings` is a keybinding discovery surface, not only a
|
|
22
|
+
launch-toggle editor. It must show `Toggle Project Sidebar` with the
|
|
22
23
|
guaranteed `Alt-1` / `M-1` default, plus sidebar-local commands, picker-local
|
|
23
24
|
commands, `Pane navigation`, `Window navigation`, and `Rename` groups or
|
|
24
25
|
equivalent searchable rows.
|
|
26
|
+
- `Settings > Keybindings > Action` keeps the user-facing edit path small:
|
|
27
|
+
action, current keybinding/aliases, `Add alias`, and reset. It does not offer
|
|
28
|
+
replace-primary, disable-default, typed-fallback, terminal mapping preview, or
|
|
29
|
+
terminal mapping apply rows.
|
|
30
|
+
- Terminal delivery remediation lives outside Settings primary flow. The
|
|
31
|
+
supported order is `projmux shell` first, then `projmux setup`, then
|
|
32
|
+
`projmux init` for supported terminal adapters.
|
|
25
33
|
- Rows that cannot safely be edited still stay visible. Mark diagnostic-only
|
|
26
34
|
rows with the delivery path and reason instead of hiding them or turning them
|
|
27
35
|
into unsupported editable aliases. Transport-dependent rows stay visible with
|
|
@@ -55,8 +63,8 @@ view-first layout:
|
|
|
55
63
|
`${XDG_CONFIG_HOME:-$HOME/.config}/projmux/ai-hook-actions.json`. It does not
|
|
56
64
|
edit catalog `install` values or run agent install/remove commands.
|
|
57
65
|
- `Settings > Labs` keeps experimental toggles, but keybindings no longer have a
|
|
58
|
-
visible Labs row. The hidden compatibility action
|
|
59
|
-
|
|
66
|
+
visible Labs row. The hidden compatibility action redirects to the
|
|
67
|
+
`Settings > Keybindings` action list, not to a diagnostic default.
|
|
60
68
|
- `Settings > Labs > Project Hooks` is overview-first. The Labs root opens the
|
|
61
69
|
overview, and the on/off mutation rows live one level deeper.
|
|
62
70
|
- `Settings > AI Settings` is view-first. The root contains `Default split
|
|
@@ -71,6 +79,17 @@ view-first layout:
|
|
|
71
79
|
and `Notify icon` details. Each detail shows the current mode plus
|
|
72
80
|
immediately selectable off/symbol/emoji preview rows. There is no separate
|
|
73
81
|
`Change` page for icon decoration.
|
|
82
|
+
- `Settings > Appearance` also shows a read-only `Theme font` status row.
|
|
83
|
+
`font_family` and `font_size` are desired values from the effective
|
|
84
|
+
project/global theme, and unsupported terminal paths must say `not applied`
|
|
85
|
+
instead of implying tmux changed the font.
|
|
86
|
+
- `Settings > Appearance > Language / Locale` is the global/user language
|
|
87
|
+
detail. The root row shows the saved `[ui].locale` value and the currently
|
|
88
|
+
effective locale. The detail shows `Current`, `[ui].locale`, optional
|
|
89
|
+
`PROJMUX_LOCALE` env override, and direct choices for `auto`, `en-US`, and
|
|
90
|
+
`ko-KR`. When `auto` is active it must show the detected source (`LC_ALL`,
|
|
91
|
+
`LC_MESSAGES`, `LANG`, or fallback). Unsupported locale tags must remain
|
|
92
|
+
visible as warnings and fall back to `en-US`.
|
|
74
93
|
|
|
75
94
|
Hooks remain the reference pattern for this IA:
|
|
76
95
|
|
package/docs/statusbar.md
CHANGED
|
@@ -33,6 +33,25 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git> %H:%M
|
|
|
33
33
|
clicks on tmux 3.4+, so a `run-shell` handler can't recover the
|
|
34
34
|
target after the fact — the Go dispatcher's
|
|
35
35
|
`isWindowListRangeToken` fallback is now defense-in-depth only.
|
|
36
|
+
Each window tab reserves a one-cell live pane attention prefix from
|
|
37
|
+
`projmux attention window #{window_id}` before the index. AI panes use the
|
|
38
|
+
semantic `@projmux_ai_badge_kind` first: approval/input-required panes use
|
|
39
|
+
the action-required amber-orange role, response-complete panes use the
|
|
40
|
+
non-critical success green role, and in-progress panes use the progress
|
|
41
|
+
yellow role. Red/critical is reserved for error, failure, and risk chrome;
|
|
42
|
+
permission or input-required status badges do not use it.
|
|
43
|
+
Legacy busy/reply title and attention-state markers remain the fallback, and
|
|
44
|
+
no-state windows render a blank placeholder so title alignment and click range
|
|
45
|
+
width stay stable. This live window-list badge is independent from the row-0
|
|
46
|
+
notify queue segment and from notify queue severity or desktop notification
|
|
47
|
+
urgency. For example, an approval request may remain a critical queued
|
|
48
|
+
notification while its live status badge renders action-required amber-orange.
|
|
49
|
+
Window-list badges and app pane-border badges use the same semantic priority,
|
|
50
|
+
with display style controlled by Settings > Appearance > AI badge style and persisted in
|
|
51
|
+
`~/.config/projmux/ai-badge-style`. The default is `dot`; `emoji` renders
|
|
52
|
+
`⏳` for approval/input-required, `✅` for response-complete, and `🔄` for
|
|
53
|
+
in-progress. `off` (also accepted as `minimal` when read from disk) preserves
|
|
54
|
+
the same spacing without drawing a marker.
|
|
36
55
|
The session, pwd, kube, and git segments on this row are wrapped
|
|
37
56
|
in `#[range=user|<id>]` ranges and dispatched through the projmux
|
|
38
57
|
handler. The standalone config also wraps the right-side `projmux`
|
|
@@ -86,13 +105,19 @@ The notify segment renders the newest queued item as a single notification
|
|
|
86
105
|
block: project, state (`NEED`/`INFO`/`WARN`/`CRIT`), optional agent, text,
|
|
87
106
|
age, and `+N` for older pending entries. Window/pane ids are not shown in the
|
|
88
107
|
compact status segment.
|
|
108
|
+
The compact age text is locale-formatted through `internal/i18n` (`2m ago` in
|
|
109
|
+
`en-US`, `36초 전` in `ko-KR`). AI notify body text uses catalog-owned category
|
|
110
|
+
labels while preserving agent names, commands, paths, URLs, and provider
|
|
111
|
+
payload excerpts. The `+N` older-entry count remains a numeric compact badge so
|
|
112
|
+
it does not expand the status segment.
|
|
89
113
|
When the notify block is wider than its cell budget, clipping shrinks the body
|
|
90
114
|
text first and appends an ellipsis while preserving project, state, agent, age,
|
|
91
115
|
and count metadata. If the segment is still too wide, the age is dropped next
|
|
92
116
|
while badges and the `+N` count stay visible when possible. Very narrow widths
|
|
93
|
-
fall back to
|
|
117
|
+
fall back to dotless clipped text plus the `+N` count when it fits; the final
|
|
94
118
|
hard-truncate path still closes with `#[default]` so later status segments do
|
|
95
|
-
not inherit notification styling.
|
|
119
|
+
not inherit notification styling. That dotless narrow fallback applies only to
|
|
120
|
+
the queued notify segment, not to the separate window-list live attention badge.
|
|
96
121
|
`usage` opens a native-framed detail HUD for the compact usage bar. It reads
|
|
97
122
|
the cached usage state in-process, keeps the existing `projmux usage` CLI
|
|
98
123
|
output shape unchanged for external consumers, aligns model/window rows with
|
|
@@ -186,6 +211,9 @@ updates the matching live tmux option
|
|
|
186
211
|
(`@projmux_statusbar_decoration_cwd`, `_git`, or `_notify`) when run inside
|
|
187
212
|
tmux. The legacy `~/.config/projmux/statusbar-decoration` and
|
|
188
213
|
`@projmux_statusbar_decoration` remain fallback defaults for older configs.
|
|
214
|
+
Appearance also shows the effective desired theme font. This is a status row,
|
|
215
|
+
not a font editor: tmux status strings and ANSI output cannot force terminal
|
|
216
|
+
font family or size, so unsupported environments report `not applied`.
|
|
189
217
|
|
|
190
218
|
To add a new clickable segment:
|
|
191
219
|
|
package/docs/testing.md
CHANGED
|
@@ -54,3 +54,125 @@ desktop shell, or OS integration:
|
|
|
54
54
|
|
|
55
55
|
Keep those checks as manual or host-run smoke validation until a dedicated
|
|
56
56
|
host harness exists.
|
|
57
|
+
|
|
58
|
+
Use this smoke checklist when a change touches terminal delivery, host desktop
|
|
59
|
+
notifications, or reviewer confidence around those boundaries. If the change
|
|
60
|
+
does not touch those areas, copy the PR-note block below and mark the relevant
|
|
61
|
+
rows `not run`.
|
|
62
|
+
|
|
63
|
+
### Terminal Key Delivery
|
|
64
|
+
|
|
65
|
+
Run the raw key probe outside tmux, in the terminal emulator being claimed:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
projmux setup --timeout 10s
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Observe:
|
|
72
|
+
|
|
73
|
+
- `Alt-1` through `Alt-5` report `OK plain`. These are the guaranteed
|
|
74
|
+
zero-config launch defaults.
|
|
75
|
+
- If a guaranteed key reports `MISS timeout`, preview a supported terminal
|
|
76
|
+
mapping with `projmux init ghostty` or `projmux init windows-terminal`,
|
|
77
|
+
apply it with the same command plus `--apply`, restart that terminal if
|
|
78
|
+
required, and rerun `projmux setup --timeout 10s`.
|
|
79
|
+
- Optional direct aliases and transport-dependent chords may be reported by
|
|
80
|
+
the probe, but they are not part of the guaranteed host smoke unless the PR
|
|
81
|
+
explicitly changes them.
|
|
82
|
+
|
|
83
|
+
Then run the app in the same terminal:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
projmux shell
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Observe:
|
|
90
|
+
|
|
91
|
+
- `Alt-1` opens the project sidebar.
|
|
92
|
+
- `Alt-2` opens the notification sidebar.
|
|
93
|
+
- `Alt-3` opens the existing-session picker.
|
|
94
|
+
- `Alt-4` opens the AI split picker.
|
|
95
|
+
- `Alt-5` opens Settings.
|
|
96
|
+
- Pressing the same launch key again closes the popup instead of typing escape
|
|
97
|
+
bytes into the shell or picker input.
|
|
98
|
+
|
|
99
|
+
### WSL Toast
|
|
100
|
+
|
|
101
|
+
Run this from WSL with Windows Terminal available. The detached tmux server is
|
|
102
|
+
intentional: `projmux focus` falls back to the product desktop notification
|
|
103
|
+
path when there is no attached client to switch.
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
sock="${TMPDIR:-/tmp}/projmux-host-smoke.sock"
|
|
107
|
+
tmux -S "$sock" kill-server 2>/dev/null || true
|
|
108
|
+
tmux -S "$sock" new-session -d -s projmux-host-smoke 'sleep 600'
|
|
109
|
+
PROJMUX_DESKTOP_NOTIFY_MODE=notify \
|
|
110
|
+
projmux focus --socket "$sock" --target projmux-host-smoke --json
|
|
111
|
+
tmux -S "$sock" kill-server
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Observe:
|
|
115
|
+
|
|
116
|
+
- The JSON includes `"ok":true`, `"dispatch":"notify-only"`, and
|
|
117
|
+
`"reason":"no-attached-client"`.
|
|
118
|
+
- Windows shows a short projmux toast with `session ready:
|
|
119
|
+
projmux-host-smoke`.
|
|
120
|
+
- No visible PowerShell or console window remains open after the toast.
|
|
121
|
+
|
|
122
|
+
If the PR changes click-to-focus behavior, repeat with
|
|
123
|
+
`PROJMUX_DESKTOP_NOTIFY_MODE=raise`, click the toast, and record whether the
|
|
124
|
+
host terminal returns to the target. Otherwise leave click callbacks marked as
|
|
125
|
+
manual/not run.
|
|
126
|
+
|
|
127
|
+
### macOS GUI Notification
|
|
128
|
+
|
|
129
|
+
The built-in desktop sender is Linux/WSL-oriented. On macOS, smoke the
|
|
130
|
+
documented `PROJMUX_NOTIFY_HOOK` escape hatch with an `osascript` sender:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
hook="${TMPDIR:-/tmp}/projmux-macos-notify.sh"
|
|
134
|
+
cat >"$hook" <<'SH'
|
|
135
|
+
#!/bin/sh
|
|
136
|
+
title=${1:-projmux}
|
|
137
|
+
body=${2:-}
|
|
138
|
+
osascript \
|
|
139
|
+
-e 'on run argv' \
|
|
140
|
+
-e 'display notification (item 2 of argv) with title (item 1 of argv)' \
|
|
141
|
+
-e 'end run' \
|
|
142
|
+
"$title" "$body"
|
|
143
|
+
SH
|
|
144
|
+
chmod 0755 "$hook"
|
|
145
|
+
|
|
146
|
+
sock="${TMPDIR:-/tmp}/projmux-host-smoke.sock"
|
|
147
|
+
tmux -S "$sock" kill-server 2>/dev/null || true
|
|
148
|
+
tmux -S "$sock" new-session -d -s projmux-host-smoke 'sleep 600'
|
|
149
|
+
PROJMUX_NOTIFY_HOOK="$hook" \
|
|
150
|
+
projmux focus --socket "$sock" --target projmux-host-smoke --json
|
|
151
|
+
tmux -S "$sock" kill-server
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Observe:
|
|
155
|
+
|
|
156
|
+
- The JSON includes `"ok":true`, `"dispatch":"notify-only"`, and
|
|
157
|
+
`"reason":"no-attached-client"`.
|
|
158
|
+
- macOS shows a Notification Center banner with `session ready:
|
|
159
|
+
projmux-host-smoke`.
|
|
160
|
+
- If macOS prompts for notification permission, record that state in the PR
|
|
161
|
+
instead of treating the product command as verified.
|
|
162
|
+
|
|
163
|
+
### PR Note Template
|
|
164
|
+
|
|
165
|
+
```markdown
|
|
166
|
+
Host-only smoke validation:
|
|
167
|
+
|
|
168
|
+
- Docker-covered checks: `make test-integration`, `make test-install-smoke`,
|
|
169
|
+
and `make test-e2e` cover portable Linux tmux/config/notify behavior only.
|
|
170
|
+
- Terminal key delivery: not run / run on <terminal>; `projmux setup --timeout
|
|
171
|
+
10s` showed <result>; `Alt-1..5` app popup smoke <passed/failed/not run>.
|
|
172
|
+
- WSL toast: not run / run on <Windows + WSL distro>; detached-focus smoke
|
|
173
|
+
produced `dispatch=notify-only`; observed <toast/no toast/notes>.
|
|
174
|
+
- macOS GUI notification: not run / run on <macOS version>; hook smoke
|
|
175
|
+
produced `dispatch=notify-only`; observed <banner/permission prompt/notes>.
|
|
176
|
+
- Desktop notification click callbacks: not run unless this PR changes
|
|
177
|
+
click-to-focus behavior; result <notes>.
|
|
178
|
+
```
|