numux 2.13.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/man/numux.1 +40 -10
- package/dist/numux.js +222 -41
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -226,7 +226,7 @@ export default defineConfig({
|
|
|
226
226
|
| `--kill-others-on-fail` | Kill all processes when any exits with non-zero code |
|
|
227
227
|
| `--max-restarts` `<n>` | Max auto-restarts for crashed processes |
|
|
228
228
|
| `--no-watch` | Disable file watching even if config has watch patterns |
|
|
229
|
-
| `-t,` `--timestamps` `[<format>]` | Add timestamps to output (default HH:mm:ss, or pass a format string) |
|
|
229
|
+
| `-t,` `--timestamps` `[<format>]` | Add timestamps to output (default HH:mm:ss.SSS, or pass a format string) |
|
|
230
230
|
| `--log-dir` `<path>` | Write per-process logs to directory |
|
|
231
231
|
| `--debug` | Enable debug logging to .numux/debug.log |
|
|
232
232
|
| `-h,` `--help` | Show this help |
|
|
@@ -277,7 +277,7 @@ Top-level options apply to all processes (process-level settings override):
|
|
|
277
277
|
| `watch` | `string \| string[]` | Global watch patterns, inherited by processes without their own watch |
|
|
278
278
|
| `sort` | `'config' \| 'alphabetical' \| 'topological'` | Tab display order. `'config'` preserves definition order (package.json script order for wildcards), `'alphabetical'` sorts by process name, `'topological'` sorts by dependency tiers. |
|
|
279
279
|
| `prefix` | `boolean` | Use prefixed output mode instead of TUI (for CI/scripts) |
|
|
280
|
-
| `timestamps` | `boolean \| string` | Add timestamps to output lines. `true` uses default `HH:mm:ss` format, or pass a format string (e.g. `"HH:mm:ss
|
|
280
|
+
| `timestamps` | `boolean \| string` | Add timestamps to output lines. `true` uses default `HH:mm:ss.SSS` format, or pass a format string (e.g. `"HH:mm:ss"`) |
|
|
281
281
|
| `killOthers` | `boolean` | Kill all processes when any one exits (regardless of exit code) |
|
|
282
282
|
| `killOthersOnFail` | `boolean` | Kill all processes when any one exits with a non-zero exit code |
|
|
283
283
|
| `noWatch` | `boolean` | Disable file watching even if processes have watch patterns |
|
|
@@ -477,14 +477,19 @@ Keybindings are shown in the status bar at the bottom of the app. Panes are read
|
|
|
477
477
|
<!-- generated:keybindings -->
|
|
478
478
|
| Key | Action |
|
|
479
479
|
|-----|--------|
|
|
480
|
-
| `←`/`→` or `1`-`9` |
|
|
481
|
-
| `
|
|
480
|
+
| `←`/`→` or `1`-`9` | Switch tabs |
|
|
481
|
+
| `Enter` | Input mode |
|
|
482
|
+
| `F` | Search |
|
|
482
483
|
| `R` | Restart |
|
|
484
|
+
| `Shift+R` | Restart all |
|
|
483
485
|
| `S` | Stop/start |
|
|
484
|
-
| `F` | Search |
|
|
485
486
|
| `Y` | Copy all |
|
|
486
487
|
| `L` | Clear |
|
|
487
488
|
| `T` | Timestamps |
|
|
489
|
+
| `↑↓` | Scroll line |
|
|
490
|
+
| `Shift+↑↓` | Top/bottom |
|
|
491
|
+
| `G/Shift+G` | Top/bottom |
|
|
492
|
+
| `PgUp/PgDn` | Scroll page |
|
|
488
493
|
| `O` | Open logs |
|
|
489
494
|
| `Ctrl+Click` | Open link |
|
|
490
495
|
| `Ctrl+C` | Quit |
|
package/dist/man/numux.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH "NUMUX" "1" "April 2026" "2.
|
|
1
|
+
.TH "NUMUX" "1" "April 2026" "2.14.0" "numux manual"
|
|
2
2
|
.SH "NAME"
|
|
3
3
|
\fBnumux\fR
|
|
4
4
|
.P
|
|
@@ -339,7 +339,7 @@ _
|
|
|
339
339
|
T{
|
|
340
340
|
\fB\-t,\fP \fB\-\-timestamps\fP \fB[<format>]\fP
|
|
341
341
|
T}|T{
|
|
342
|
-
Add timestamps to output (default HH:mm:ss, or pass a format string)
|
|
342
|
+
Add timestamps to output (default HH:mm:ss\.SSS, or pass a format string)
|
|
343
343
|
T}
|
|
344
344
|
_
|
|
345
345
|
T{
|
|
@@ -502,7 +502,7 @@ T{
|
|
|
502
502
|
T}|T{
|
|
503
503
|
\fBboolean | string\fP
|
|
504
504
|
T}|T{
|
|
505
|
-
Add timestamps to output lines\. \fBtrue\fP uses default \fBHH:mm:ss\fP format, or pass a format string (e\.g\. \fB"HH:mm:ss
|
|
505
|
+
Add timestamps to output lines\. \fBtrue\fP uses default \fBHH:mm:ss\.SSS\fP format, or pass a format string (e\.g\. \fB"HH:mm:ss"\fP)
|
|
506
506
|
T}
|
|
507
507
|
_
|
|
508
508
|
T{
|
|
@@ -949,13 +949,19 @@ T}
|
|
|
949
949
|
T{
|
|
950
950
|
\fB←\fP/\fB→\fP or \fB1\fP\-\fB9\fP
|
|
951
951
|
T}|T{
|
|
952
|
-
|
|
952
|
+
Switch tabs
|
|
953
953
|
T}
|
|
954
954
|
_
|
|
955
955
|
T{
|
|
956
|
-
\
|
|
956
|
+
\fBEnter\fP
|
|
957
957
|
T}|T{
|
|
958
|
-
|
|
958
|
+
Input mode
|
|
959
|
+
T}
|
|
960
|
+
_
|
|
961
|
+
T{
|
|
962
|
+
\fBF\fP
|
|
963
|
+
T}|T{
|
|
964
|
+
Search
|
|
959
965
|
T}
|
|
960
966
|
_
|
|
961
967
|
T{
|
|
@@ -965,15 +971,15 @@ Restart
|
|
|
965
971
|
T}
|
|
966
972
|
_
|
|
967
973
|
T{
|
|
968
|
-
\
|
|
974
|
+
\fBShift+R\fP
|
|
969
975
|
T}|T{
|
|
970
|
-
|
|
976
|
+
Restart all
|
|
971
977
|
T}
|
|
972
978
|
_
|
|
973
979
|
T{
|
|
974
|
-
\
|
|
980
|
+
\fBS\fP
|
|
975
981
|
T}|T{
|
|
976
|
-
|
|
982
|
+
Stop/start
|
|
977
983
|
T}
|
|
978
984
|
_
|
|
979
985
|
T{
|
|
@@ -995,6 +1001,30 @@ Timestamps
|
|
|
995
1001
|
T}
|
|
996
1002
|
_
|
|
997
1003
|
T{
|
|
1004
|
+
\fB↑↓\fP
|
|
1005
|
+
T}|T{
|
|
1006
|
+
Scroll line
|
|
1007
|
+
T}
|
|
1008
|
+
_
|
|
1009
|
+
T{
|
|
1010
|
+
\fBShift+↑↓\fP
|
|
1011
|
+
T}|T{
|
|
1012
|
+
Top/bottom
|
|
1013
|
+
T}
|
|
1014
|
+
_
|
|
1015
|
+
T{
|
|
1016
|
+
\fBG/Shift+G\fP
|
|
1017
|
+
T}|T{
|
|
1018
|
+
Top/bottom
|
|
1019
|
+
T}
|
|
1020
|
+
_
|
|
1021
|
+
T{
|
|
1022
|
+
\fBPgUp/PgDn\fP
|
|
1023
|
+
T}|T{
|
|
1024
|
+
Scroll page
|
|
1025
|
+
T}
|
|
1026
|
+
_
|
|
1027
|
+
T{
|
|
998
1028
|
\fBO\fP
|
|
999
1029
|
T}|T{
|
|
1000
1030
|
Open logs
|
package/dist/numux.js
CHANGED
|
@@ -244,7 +244,7 @@ export default defineConfig({
|
|
|
244
244
|
| \`--kill-others-on-fail\` | Kill all processes when any exits with non-zero code |
|
|
245
245
|
| \`--max-restarts\` \`<n>\` | Max auto-restarts for crashed processes |
|
|
246
246
|
| \`--no-watch\` | Disable file watching even if config has watch patterns |
|
|
247
|
-
| \`-t,\` \`--timestamps\` \`[<format>]\` | Add timestamps to output (default HH:mm:ss, or pass a format string) |
|
|
247
|
+
| \`-t,\` \`--timestamps\` \`[<format>]\` | Add timestamps to output (default HH:mm:ss.SSS, or pass a format string) |
|
|
248
248
|
| \`--log-dir\` \`<path>\` | Write per-process logs to directory |
|
|
249
249
|
| \`--debug\` | Enable debug logging to .numux/debug.log |
|
|
250
250
|
| \`-h,\` \`--help\` | Show this help |
|
|
@@ -283,7 +283,7 @@ numux logs api | tail -f # Follow process log output
|
|
|
283
283
|
| \`watch\` | \`string \\| string[]\` | Global watch patterns, inherited by processes without their own watch |
|
|
284
284
|
| \`sort\` | \`'config' \\| 'alphabetical' \\| 'topological'\` | Tab display order. \`'config'\` preserves definition order (package.json script order for wildcards), \`'alphabetical'\` sorts by process name, \`'topological'\` sorts by dependency tiers. |
|
|
285
285
|
| \`prefix\` | \`boolean\` | Use prefixed output mode instead of TUI (for CI/scripts) |
|
|
286
|
-
| \`timestamps\` | \`boolean \\| string\` | Add timestamps to output lines. \`true\` uses default \`HH:mm:ss\` format, or pass a format string (e.g. \`"HH:mm:ss
|
|
286
|
+
| \`timestamps\` | \`boolean \\| string\` | Add timestamps to output lines. \`true\` uses default \`HH:mm:ss.SSS\` format, or pass a format string (e.g. \`"HH:mm:ss"\`) |
|
|
287
287
|
| \`killOthers\` | \`boolean\` | Kill all processes when any one exits (regardless of exit code) |
|
|
288
288
|
| \`killOthersOnFail\` | \`boolean\` | Kill all processes when any one exits with a non-zero exit code |
|
|
289
289
|
| \`noWatch\` | \`boolean\` | Disable file watching even if processes have watch patterns |
|
|
@@ -456,14 +456,19 @@ Unmatched references are left as-is (the shell will expand \`$db\` as empty + \`
|
|
|
456
456
|
<!-- generated:keybindings -->
|
|
457
457
|
| Key | Action |
|
|
458
458
|
|-----|--------|
|
|
459
|
-
| \`\u2190\`/\`\u2192\` or \`1\`-\`9\` |
|
|
460
|
-
| \`
|
|
459
|
+
| \`\u2190\`/\`\u2192\` or \`1\`-\`9\` | Switch tabs |
|
|
460
|
+
| \`Enter\` | Input mode |
|
|
461
|
+
| \`F\` | Search |
|
|
461
462
|
| \`R\` | Restart |
|
|
463
|
+
| \`Shift+R\` | Restart all |
|
|
462
464
|
| \`S\` | Stop/start |
|
|
463
|
-
| \`F\` | Search |
|
|
464
465
|
| \`Y\` | Copy all |
|
|
465
466
|
| \`L\` | Clear |
|
|
466
467
|
| \`T\` | Timestamps |
|
|
468
|
+
| \`\u2191\u2193\` | Scroll line |
|
|
469
|
+
| \`Shift+\u2191\u2193\` | Top/bottom |
|
|
470
|
+
| \`G/Shift+G\` | Top/bottom |
|
|
471
|
+
| \`PgUp/PgDn\` | Scroll page |
|
|
467
472
|
| \`O\` | Open logs |
|
|
468
473
|
| \`Ctrl+Click\` | Open link |
|
|
469
474
|
| \`Ctrl+C\` | Quit |
|
|
@@ -541,7 +546,7 @@ var init_help = __esm(() => {
|
|
|
541
546
|
var require_package = __commonJS((exports, module) => {
|
|
542
547
|
module.exports = {
|
|
543
548
|
name: "numux",
|
|
544
|
-
version: "2.
|
|
549
|
+
version: "2.14.0",
|
|
545
550
|
description: "Terminal multiplexer with dependency orchestration",
|
|
546
551
|
type: "module",
|
|
547
552
|
license: "MIT",
|
|
@@ -857,7 +862,7 @@ var FLAGS = [
|
|
|
857
862
|
long: "--timestamps",
|
|
858
863
|
short: "-t",
|
|
859
864
|
key: "timestamps",
|
|
860
|
-
description: "Add timestamps to output (default HH:mm:ss, or pass a format string)",
|
|
865
|
+
description: "Add timestamps to output (default HH:mm:ss.SSS, or pass a format string)",
|
|
861
866
|
valueName: "<format>",
|
|
862
867
|
completionHint: "none"
|
|
863
868
|
},
|
|
@@ -2500,9 +2505,7 @@ class ProcessRunner {
|
|
|
2500
2505
|
}
|
|
2501
2506
|
}
|
|
2502
2507
|
write(data) {
|
|
2503
|
-
|
|
2504
|
-
this.proc.terminal.write(data);
|
|
2505
|
-
}
|
|
2508
|
+
this.proc?.terminal?.write(data);
|
|
2506
2509
|
}
|
|
2507
2510
|
}
|
|
2508
2511
|
|
|
@@ -2895,7 +2898,10 @@ function evaluateCondition(condition) {
|
|
|
2895
2898
|
}
|
|
2896
2899
|
|
|
2897
2900
|
// src/ui/app.ts
|
|
2898
|
-
import { BoxRenderable, createCliRenderer } from "@opentui/core";
|
|
2901
|
+
import { BoxRenderable as BoxRenderable3, createCliRenderer } from "@opentui/core";
|
|
2902
|
+
|
|
2903
|
+
// src/ui/help-overlay.ts
|
|
2904
|
+
import { BoxRenderable, TextRenderable } from "@opentui/core";
|
|
2899
2905
|
|
|
2900
2906
|
// src/ui/keybindings.ts
|
|
2901
2907
|
var SHORTCUTS = {
|
|
@@ -2910,20 +2916,105 @@ var SHORTCUTS = {
|
|
|
2910
2916
|
scrollToBottom: { key: "g", label: "Shift+G", description: "bottom", shift: true },
|
|
2911
2917
|
openLogs: { key: "o", label: "O", description: "open logs" }
|
|
2912
2918
|
};
|
|
2913
|
-
|
|
2914
|
-
[
|
|
2919
|
+
function toHintPair(hint) {
|
|
2920
|
+
return Array.isArray(hint) ? hint : [hint.label, hint.description];
|
|
2921
|
+
}
|
|
2922
|
+
var STATUS_HINTS_COMPACT = [
|
|
2923
|
+
["\u2190\u2192", "tabs"],
|
|
2924
|
+
SHORTCUTS.search,
|
|
2925
|
+
SHORTCUTS.copy,
|
|
2926
|
+
["Enter", "input"],
|
|
2927
|
+
["H", "help"]
|
|
2928
|
+
];
|
|
2929
|
+
var STATUS_HINTS_FULL = [
|
|
2930
|
+
["\u2190\u2192/1-9", "switch tabs"],
|
|
2931
|
+
["Enter", "input mode"],
|
|
2932
|
+
SHORTCUTS.search,
|
|
2933
|
+
SHORTCUTS.restart,
|
|
2934
|
+
SHORTCUTS.restartAll,
|
|
2935
|
+
SHORTCUTS.stopStart,
|
|
2936
|
+
SHORTCUTS.copy,
|
|
2937
|
+
SHORTCUTS.clear,
|
|
2938
|
+
SHORTCUTS.timestamps,
|
|
2939
|
+
["\u2191\u2193", "scroll line"],
|
|
2940
|
+
["Shift+\u2191\u2193", "top/bottom"],
|
|
2915
2941
|
["G/Shift+G", "top/bottom"],
|
|
2916
|
-
[
|
|
2917
|
-
|
|
2918
|
-
[SHORTCUTS.search.label, SHORTCUTS.search.description],
|
|
2919
|
-
[SHORTCUTS.copy.label, SHORTCUTS.copy.description],
|
|
2920
|
-
[SHORTCUTS.clear.label, SHORTCUTS.clear.description],
|
|
2921
|
-
[SHORTCUTS.timestamps.label, SHORTCUTS.timestamps.description],
|
|
2922
|
-
[SHORTCUTS.openLogs.label, SHORTCUTS.openLogs.description],
|
|
2942
|
+
["PgUp/PgDn", "scroll page"],
|
|
2943
|
+
SHORTCUTS.openLogs,
|
|
2923
2944
|
["Ctrl+Click", "open link"],
|
|
2924
2945
|
["Ctrl+C", "quit"]
|
|
2925
2946
|
];
|
|
2926
|
-
var STATUS_BAR_TEXT =
|
|
2947
|
+
var STATUS_BAR_TEXT = STATUS_HINTS_COMPACT.map((h) => {
|
|
2948
|
+
const [l, d] = toHintPair(h);
|
|
2949
|
+
return `${l}: ${d}`;
|
|
2950
|
+
}).join(" ");
|
|
2951
|
+
|
|
2952
|
+
// src/ui/help-overlay.ts
|
|
2953
|
+
class HelpOverlay {
|
|
2954
|
+
renderable;
|
|
2955
|
+
textRenderable;
|
|
2956
|
+
constructor(renderer) {
|
|
2957
|
+
this.renderable = new BoxRenderable(renderer, {
|
|
2958
|
+
id: "help-overlay",
|
|
2959
|
+
position: "absolute",
|
|
2960
|
+
width: "100%",
|
|
2961
|
+
height: "100%",
|
|
2962
|
+
zIndex: 100,
|
|
2963
|
+
visible: false,
|
|
2964
|
+
justifyContent: "center",
|
|
2965
|
+
alignItems: "center"
|
|
2966
|
+
});
|
|
2967
|
+
const backdrop = new BoxRenderable(renderer, {
|
|
2968
|
+
id: "help-backdrop",
|
|
2969
|
+
position: "absolute",
|
|
2970
|
+
width: "100%",
|
|
2971
|
+
height: "100%",
|
|
2972
|
+
backgroundColor: "#000000",
|
|
2973
|
+
opacity: 0.7
|
|
2974
|
+
});
|
|
2975
|
+
const box = new BoxRenderable(renderer, {
|
|
2976
|
+
id: "help-box",
|
|
2977
|
+
flexDirection: "column",
|
|
2978
|
+
padding: 1,
|
|
2979
|
+
paddingX: 5,
|
|
2980
|
+
backgroundColor: "#1a1a2e",
|
|
2981
|
+
border: true,
|
|
2982
|
+
borderColor: "#444",
|
|
2983
|
+
zIndex: 101
|
|
2984
|
+
});
|
|
2985
|
+
const lines = [
|
|
2986
|
+
"Keyboard Shortcuts",
|
|
2987
|
+
"",
|
|
2988
|
+
...STATUS_HINTS_FULL.map((h) => {
|
|
2989
|
+
const [label, desc] = toHintPair(h);
|
|
2990
|
+
return ` ${label.padEnd(14)} ${desc}`;
|
|
2991
|
+
}),
|
|
2992
|
+
"",
|
|
2993
|
+
"Press H or Esc to close"
|
|
2994
|
+
];
|
|
2995
|
+
this.textRenderable = new TextRenderable(renderer, {
|
|
2996
|
+
id: "help-text",
|
|
2997
|
+
content: lines.join(`
|
|
2998
|
+
`),
|
|
2999
|
+
fg: "#cccccc"
|
|
3000
|
+
});
|
|
3001
|
+
box.add(this.textRenderable);
|
|
3002
|
+
this.renderable.add(backdrop);
|
|
3003
|
+
this.renderable.add(box);
|
|
3004
|
+
}
|
|
3005
|
+
get isVisible() {
|
|
3006
|
+
return this.renderable.visible;
|
|
3007
|
+
}
|
|
3008
|
+
toggle() {
|
|
3009
|
+
this.renderable.visible = !this.renderable.visible;
|
|
3010
|
+
}
|
|
3011
|
+
hide() {
|
|
3012
|
+
this.renderable.visible = false;
|
|
3013
|
+
}
|
|
3014
|
+
show() {
|
|
3015
|
+
this.renderable.visible = true;
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
2927
3018
|
|
|
2928
3019
|
// src/ui/pane.ts
|
|
2929
3020
|
import {
|
|
@@ -3340,7 +3431,7 @@ Object.defineProperty(GhosttyTerminalRenderable.prototype, "lineInfo", {
|
|
|
3340
3431
|
});
|
|
3341
3432
|
|
|
3342
3433
|
// src/utils/timestamp.ts
|
|
3343
|
-
var DEFAULT_TIMESTAMP_FORMAT = "HH:mm:ss";
|
|
3434
|
+
var DEFAULT_TIMESTAMP_FORMAT = "HH:mm:ss.SSS";
|
|
3344
3435
|
function formatTimestamp(date, format) {
|
|
3345
3436
|
const hours24 = date.getHours();
|
|
3346
3437
|
const hours12 = hours24 % 12 || 12;
|
|
@@ -3601,18 +3692,16 @@ class Pane {
|
|
|
3601
3692
|
get timestampsEnabled() {
|
|
3602
3693
|
return this._timestampFormat !== null;
|
|
3603
3694
|
}
|
|
3695
|
+
getTimestampSigns() {
|
|
3696
|
+
return this.timestampGutter?.getLineSigns() ?? null;
|
|
3697
|
+
}
|
|
3604
3698
|
updateTimestampSigns() {
|
|
3605
3699
|
if (!(this.timestampGutter && this._timestampFormat))
|
|
3606
3700
|
return;
|
|
3607
3701
|
const fmt = this._timestampFormat;
|
|
3608
3702
|
const signs = new Map;
|
|
3609
|
-
let prevFormatted = "";
|
|
3610
3703
|
for (let i = 0;i < this.lineTimestamps.length; i++) {
|
|
3611
|
-
|
|
3612
|
-
if (formatted !== prevFormatted) {
|
|
3613
|
-
signs.set(i, { before: formatted });
|
|
3614
|
-
prevFormatted = formatted;
|
|
3615
|
-
}
|
|
3704
|
+
signs.set(i, { before: formatTimestamp(new Date(this.lineTimestamps[i]), fmt) });
|
|
3616
3705
|
}
|
|
3617
3706
|
this.timestampGutter.setLineSigns(signs);
|
|
3618
3707
|
}
|
|
@@ -3813,13 +3902,22 @@ class SearchController {
|
|
|
3813
3902
|
}
|
|
3814
3903
|
|
|
3815
3904
|
// src/ui/status-bar.ts
|
|
3816
|
-
import {
|
|
3905
|
+
import {
|
|
3906
|
+
BoxRenderable as BoxRenderable2,
|
|
3907
|
+
cyan,
|
|
3908
|
+
red,
|
|
3909
|
+
reverse,
|
|
3910
|
+
StyledText as StyledText2,
|
|
3911
|
+
TextRenderable as TextRenderable2,
|
|
3912
|
+
yellow
|
|
3913
|
+
} from "@opentui/core";
|
|
3817
3914
|
function plain(text) {
|
|
3818
3915
|
return { __isChunk: true, text };
|
|
3819
3916
|
}
|
|
3820
3917
|
|
|
3821
3918
|
class StatusBar {
|
|
3822
3919
|
renderable;
|
|
3920
|
+
text;
|
|
3823
3921
|
_searchMode = false;
|
|
3824
3922
|
_searchQuery = "";
|
|
3825
3923
|
_searchMatchCount = 0;
|
|
@@ -3827,16 +3925,23 @@ class StatusBar {
|
|
|
3827
3925
|
_crossProcessInfo;
|
|
3828
3926
|
_tempMessage = null;
|
|
3829
3927
|
_tempTimer = null;
|
|
3928
|
+
_inputMode = false;
|
|
3830
3929
|
constructor(renderer) {
|
|
3831
|
-
this.renderable = new
|
|
3930
|
+
this.renderable = new BoxRenderable2(renderer, {
|
|
3832
3931
|
id: "status-bar",
|
|
3833
3932
|
width: "100%",
|
|
3933
|
+
backgroundColor: "#1a1a1a",
|
|
3934
|
+
paddingX: 1,
|
|
3935
|
+
minHeight: 1
|
|
3936
|
+
});
|
|
3937
|
+
this.text = new TextRenderable2(renderer, {
|
|
3938
|
+
id: "status-bar-text",
|
|
3939
|
+
width: "100%",
|
|
3834
3940
|
wrapMode: "word",
|
|
3835
|
-
content: this.buildContent()
|
|
3836
|
-
bg: "#1a1a1a",
|
|
3837
|
-
paddingX: 1
|
|
3941
|
+
content: this.buildContent()
|
|
3838
3942
|
});
|
|
3839
|
-
this.
|
|
3943
|
+
this.text.selectable = false;
|
|
3944
|
+
this.renderable.add(this.text);
|
|
3840
3945
|
}
|
|
3841
3946
|
setSearchMode(active, query = "", matchCount = 0, currentIndex = -1, crossProcessInfo) {
|
|
3842
3947
|
this._searchMode = active;
|
|
@@ -3844,19 +3949,23 @@ class StatusBar {
|
|
|
3844
3949
|
this._searchMatchCount = matchCount;
|
|
3845
3950
|
this._searchCurrentIndex = currentIndex;
|
|
3846
3951
|
this._crossProcessInfo = crossProcessInfo;
|
|
3847
|
-
this.
|
|
3952
|
+
this.text.content = this.buildContent();
|
|
3848
3953
|
}
|
|
3849
3954
|
showTemporaryMessage(message, duration = 2000) {
|
|
3850
3955
|
if (this._tempTimer)
|
|
3851
3956
|
clearTimeout(this._tempTimer);
|
|
3852
3957
|
this._tempMessage = message;
|
|
3853
|
-
this.
|
|
3958
|
+
this.text.content = this.buildContent();
|
|
3854
3959
|
this._tempTimer = setTimeout(() => {
|
|
3855
3960
|
this._tempMessage = null;
|
|
3856
3961
|
this._tempTimer = null;
|
|
3857
|
-
this.
|
|
3962
|
+
this.text.content = this.buildContent();
|
|
3858
3963
|
}, duration);
|
|
3859
3964
|
}
|
|
3965
|
+
setInputMode(active) {
|
|
3966
|
+
this._inputMode = active;
|
|
3967
|
+
this.text.content = this.buildContent();
|
|
3968
|
+
}
|
|
3860
3969
|
buildContent() {
|
|
3861
3970
|
if (this._tempMessage) {
|
|
3862
3971
|
return new StyledText2([cyan(this._tempMessage)]);
|
|
@@ -3864,6 +3973,9 @@ class StatusBar {
|
|
|
3864
3973
|
if (this._searchMode) {
|
|
3865
3974
|
return this.buildSearchContent();
|
|
3866
3975
|
}
|
|
3976
|
+
if (this._inputMode) {
|
|
3977
|
+
return new StyledText2([yellow("INPUT"), plain(" Type to send input to process. "), plain("Esc: exit")]);
|
|
3978
|
+
}
|
|
3867
3979
|
return new StyledText2([plain(STATUS_BAR_TEXT)]);
|
|
3868
3980
|
}
|
|
3869
3981
|
buildSearchContent() {
|
|
@@ -4152,8 +4264,10 @@ class App {
|
|
|
4152
4264
|
panes = new Map;
|
|
4153
4265
|
tabBar;
|
|
4154
4266
|
statusBar;
|
|
4267
|
+
helpOverlay;
|
|
4155
4268
|
search;
|
|
4156
4269
|
activePane = null;
|
|
4270
|
+
inputMode = false;
|
|
4157
4271
|
destroyed = false;
|
|
4158
4272
|
names;
|
|
4159
4273
|
termCols = 80;
|
|
@@ -4182,7 +4296,7 @@ class App {
|
|
|
4182
4296
|
this.termCols = Math.max(40, width - this.sidebarWidth - 2);
|
|
4183
4297
|
this.termRows = Math.max(5, height - 2);
|
|
4184
4298
|
const { termCols, termRows } = this;
|
|
4185
|
-
const layout = new
|
|
4299
|
+
const layout = new BoxRenderable3(this.renderer, {
|
|
4186
4300
|
id: "root",
|
|
4187
4301
|
flexDirection: "column",
|
|
4188
4302
|
width: "100%",
|
|
@@ -4191,14 +4305,14 @@ class App {
|
|
|
4191
4305
|
});
|
|
4192
4306
|
const processHexColors = buildProcessHexColorMap(this.names, this.config);
|
|
4193
4307
|
this.tabBar = new TabBar(this.renderer, this.names, processHexColors);
|
|
4194
|
-
const contentRow = new
|
|
4308
|
+
const contentRow = new BoxRenderable3(this.renderer, {
|
|
4195
4309
|
id: "content-row",
|
|
4196
4310
|
flexDirection: "row",
|
|
4197
4311
|
flexGrow: 1,
|
|
4198
4312
|
width: "100%",
|
|
4199
4313
|
border: false
|
|
4200
4314
|
});
|
|
4201
|
-
const sidebar = new
|
|
4315
|
+
const sidebar = new BoxRenderable3(this.renderer, {
|
|
4202
4316
|
id: "sidebar",
|
|
4203
4317
|
width: this.sidebarWidth,
|
|
4204
4318
|
height: "100%",
|
|
@@ -4206,12 +4320,13 @@ class App {
|
|
|
4206
4320
|
borderColor: "#444"
|
|
4207
4321
|
});
|
|
4208
4322
|
sidebar.add(this.tabBar.renderable);
|
|
4209
|
-
const paneContainer = new
|
|
4323
|
+
const paneContainer = new BoxRenderable3(this.renderer, {
|
|
4210
4324
|
id: "pane-container",
|
|
4211
4325
|
flexGrow: 1,
|
|
4212
4326
|
border: false
|
|
4213
4327
|
});
|
|
4214
4328
|
this.statusBar = new StatusBar(this.renderer);
|
|
4329
|
+
this.helpOverlay = new HelpOverlay(this.renderer);
|
|
4215
4330
|
this.search = new SearchController({
|
|
4216
4331
|
logWriter: this.logWriter,
|
|
4217
4332
|
statusBar: this.statusBar,
|
|
@@ -4246,6 +4361,7 @@ class App {
|
|
|
4246
4361
|
layout.add(contentRow);
|
|
4247
4362
|
layout.add(this.statusBar.renderable);
|
|
4248
4363
|
this.renderer.root.add(layout);
|
|
4364
|
+
this.renderer.root.add(this.helpOverlay.renderable);
|
|
4249
4365
|
this.tabBar.onSelect((_index, name) => this.switchPane(name));
|
|
4250
4366
|
this.tabBar.onSelectionChanged((_index, name) => this.switchPane(name));
|
|
4251
4367
|
this.manager.on((event) => {
|
|
@@ -4282,6 +4398,14 @@ class App {
|
|
|
4282
4398
|
this.renderer.keyInput.on("keypress", (key) => {
|
|
4283
4399
|
log(key);
|
|
4284
4400
|
if (key.ctrl && key.name === "c") {
|
|
4401
|
+
if (this.helpOverlay.isVisible) {
|
|
4402
|
+
this.helpOverlay.hide();
|
|
4403
|
+
return;
|
|
4404
|
+
}
|
|
4405
|
+
if (this.inputMode) {
|
|
4406
|
+
this.exitInputMode();
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4285
4409
|
if (this.search.isActive) {
|
|
4286
4410
|
this.search.exit();
|
|
4287
4411
|
return;
|
|
@@ -4291,15 +4415,39 @@ class App {
|
|
|
4291
4415
|
});
|
|
4292
4416
|
return;
|
|
4293
4417
|
}
|
|
4418
|
+
if (this.helpOverlay.isVisible) {
|
|
4419
|
+
if (key.name === "escape" || key.sequence === "?" || key.name === "h") {
|
|
4420
|
+
this.helpOverlay.hide();
|
|
4421
|
+
}
|
|
4422
|
+
return;
|
|
4423
|
+
}
|
|
4294
4424
|
if (this.search.isActive) {
|
|
4295
4425
|
this.search.handleInput(key);
|
|
4296
4426
|
return;
|
|
4297
4427
|
}
|
|
4428
|
+
if (this.inputMode && this.activePane) {
|
|
4429
|
+
if (key.name === "escape") {
|
|
4430
|
+
this.exitInputMode();
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
if (key.sequence) {
|
|
4434
|
+
this.manager.write(this.activePane, key.sequence);
|
|
4435
|
+
}
|
|
4436
|
+
return;
|
|
4437
|
+
}
|
|
4298
4438
|
if (!this.activePane)
|
|
4299
4439
|
return;
|
|
4300
4440
|
const isInteractive = this.config.processes[this.activePane]?.interactive === true;
|
|
4301
4441
|
if (!isInteractive) {
|
|
4302
4442
|
const name = key.name.toLowerCase();
|
|
4443
|
+
if (key.sequence === "?" || name === "h") {
|
|
4444
|
+
this.helpOverlay.toggle();
|
|
4445
|
+
return;
|
|
4446
|
+
}
|
|
4447
|
+
if (name === "return") {
|
|
4448
|
+
this.enterInputMode();
|
|
4449
|
+
return;
|
|
4450
|
+
}
|
|
4303
4451
|
if (key.shift && name === SHORTCUTS.scrollToBottom.key) {
|
|
4304
4452
|
this.panes.get(this.activePane)?.scrollToBottom();
|
|
4305
4453
|
return;
|
|
@@ -4368,6 +4516,15 @@ class App {
|
|
|
4368
4516
|
this.switchPane(this.tabBar.getNameAtIndex(next));
|
|
4369
4517
|
return;
|
|
4370
4518
|
}
|
|
4519
|
+
if (name === "up" || name === "down") {
|
|
4520
|
+
const pane = this.panes.get(this.activePane);
|
|
4521
|
+
if (key.shift) {
|
|
4522
|
+
name === "up" ? pane?.scrollToTop() : pane?.scrollToBottom();
|
|
4523
|
+
} else {
|
|
4524
|
+
pane?.scrollBy(name === "up" ? -1 : 1);
|
|
4525
|
+
}
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
4371
4528
|
if (name === "pageup" || name === "pagedown") {
|
|
4372
4529
|
const pane = this.panes.get(this.activePane);
|
|
4373
4530
|
const delta = this.termRows - 2;
|
|
@@ -4394,9 +4551,33 @@ class App {
|
|
|
4394
4551
|
}
|
|
4395
4552
|
await this.manager.startAll(termCols, termRows);
|
|
4396
4553
|
}
|
|
4554
|
+
enterInputMode() {
|
|
4555
|
+
this.inputMode = true;
|
|
4556
|
+
this.statusBar.setInputMode(true);
|
|
4557
|
+
if (this.activePane) {
|
|
4558
|
+
const pane = this.panes.get(this.activePane);
|
|
4559
|
+
if (pane)
|
|
4560
|
+
pane.terminal.showCursor = true;
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
exitInputMode() {
|
|
4564
|
+
this.inputMode = false;
|
|
4565
|
+
this.statusBar.setInputMode(false);
|
|
4566
|
+
if (this.activePane) {
|
|
4567
|
+
const isInteractive = this.config.processes[this.activePane]?.interactive === true;
|
|
4568
|
+
if (!isInteractive) {
|
|
4569
|
+
const pane = this.panes.get(this.activePane);
|
|
4570
|
+
if (pane)
|
|
4571
|
+
pane.terminal.showCursor = false;
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4397
4575
|
switchPane(name) {
|
|
4398
4576
|
if (this.activePane === name)
|
|
4399
4577
|
return;
|
|
4578
|
+
if (this.inputMode) {
|
|
4579
|
+
this.exitInputMode();
|
|
4580
|
+
}
|
|
4400
4581
|
if (this.search.isActive && !this.search.isAllMode) {
|
|
4401
4582
|
this.search.exit();
|
|
4402
4583
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -102,7 +102,7 @@ export interface NumuxConfig<K extends string = string> {
|
|
|
102
102
|
* @default false
|
|
103
103
|
*/
|
|
104
104
|
prefix?: boolean;
|
|
105
|
-
/** Add timestamps to output lines. `true` uses default `HH:mm:ss` format, or pass a format string (e.g. `"HH:mm:ss
|
|
105
|
+
/** Add timestamps to output lines. `true` uses default `HH:mm:ss.SSS` format, or pass a format string (e.g. `"HH:mm:ss"`) */
|
|
106
106
|
timestamps?: boolean | string;
|
|
107
107
|
/**
|
|
108
108
|
* Kill all processes when any one exits (regardless of exit code)
|