numux 2.13.1 → 2.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -3
- package/dist/man/numux.1 +38 -8
- package/dist/numux.js +251 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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.1" "numux manual"
|
|
2
2
|
.SH "NAME"
|
|
3
3
|
\fBnumux\fR
|
|
4
4
|
.P
|
|
@@ -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
|
@@ -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.1",
|
|
545
550
|
description: "Terminal multiplexer with dependency orchestration",
|
|
546
551
|
type: "module",
|
|
547
552
|
license: "MIT",
|
|
@@ -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 {
|
|
@@ -2931,6 +3022,19 @@ import {
|
|
|
2931
3022
|
ScrollBoxRenderable
|
|
2932
3023
|
} from "@opentui/core";
|
|
2933
3024
|
|
|
3025
|
+
// src/utils/timestamp.ts
|
|
3026
|
+
var DEFAULT_TIMESTAMP_FORMAT = "HH:mm:ss.SSS";
|
|
3027
|
+
function formatTimestamp(date, format) {
|
|
3028
|
+
const hours24 = date.getHours();
|
|
3029
|
+
const hours12 = hours24 % 12 || 12;
|
|
3030
|
+
return format.replace("YYYY", date.getFullYear().toString()).replace("MM", (date.getMonth() + 1).toString().padStart(2, "0")).replace("DD", date.getDate().toString().padStart(2, "0")).replace("HH", hours24.toString().padStart(2, "0")).replace("hh", hours12.toString().padStart(2, "0")).replace("mm", date.getMinutes().toString().padStart(2, "0")).replace("SSS", date.getMilliseconds().toString().padStart(3, "0")).replace("ss", date.getSeconds().toString().padStart(2, "0")).replace("A", hours24 < 12 ? "AM" : "PM");
|
|
3031
|
+
}
|
|
3032
|
+
function resolveTimestampFormat(timestamps) {
|
|
3033
|
+
if (!timestamps)
|
|
3034
|
+
return null;
|
|
3035
|
+
return typeof timestamps === "string" ? timestamps : DEFAULT_TIMESTAMP_FORMAT;
|
|
3036
|
+
}
|
|
3037
|
+
|
|
2934
3038
|
// node_modules/ghostty-opentui/src/terminal-buffer.ts
|
|
2935
3039
|
import {
|
|
2936
3040
|
TextBufferRenderable,
|
|
@@ -3339,17 +3443,24 @@ Object.defineProperty(GhosttyTerminalRenderable.prototype, "lineInfo", {
|
|
|
3339
3443
|
configurable: true
|
|
3340
3444
|
});
|
|
3341
3445
|
|
|
3342
|
-
// src/
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3446
|
+
// src/ui/tailing-terminal.ts
|
|
3447
|
+
class TailingTerminal extends GhosttyTerminalRenderable {
|
|
3448
|
+
constructor(ctx, options) {
|
|
3449
|
+
super(ctx, options);
|
|
3450
|
+
const self = this;
|
|
3451
|
+
const pt = self._persistentTerminal;
|
|
3452
|
+
if (!pt)
|
|
3453
|
+
return;
|
|
3454
|
+
const origGetJson = pt.getJson.bind(pt);
|
|
3455
|
+
pt.getJson = (opts = {}) => {
|
|
3456
|
+
if (opts.limit && opts.offset === undefined) {
|
|
3457
|
+
const peek = origGetJson({ limit: 1 });
|
|
3458
|
+
const offset = Math.max(0, peek.totalLines - opts.limit);
|
|
3459
|
+
return origGetJson({ offset, limit: opts.limit });
|
|
3460
|
+
}
|
|
3461
|
+
return origGetJson(opts);
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3353
3464
|
}
|
|
3354
3465
|
|
|
3355
3466
|
// src/ui/url-handler.ts
|
|
@@ -3396,8 +3507,9 @@ function openLink(link) {
|
|
|
3396
3507
|
}
|
|
3397
3508
|
|
|
3398
3509
|
// src/ui/pane.ts
|
|
3399
|
-
var
|
|
3400
|
-
var
|
|
3510
|
+
var RENDER_LIMIT = 5000;
|
|
3511
|
+
var MAX_SCROLLBACK_LINES = 1e6;
|
|
3512
|
+
var MAX_BUFFER_BYTES = 500 * 1024 * 1024;
|
|
3401
3513
|
|
|
3402
3514
|
class Pane {
|
|
3403
3515
|
scrollBox;
|
|
@@ -3423,13 +3535,14 @@ class Pane {
|
|
|
3423
3535
|
visible: false,
|
|
3424
3536
|
onMouseScroll: () => this._onScroll?.()
|
|
3425
3537
|
});
|
|
3426
|
-
this.terminal = new
|
|
3538
|
+
this.terminal = new TailingTerminal(renderer, {
|
|
3427
3539
|
id: `term-${name}`,
|
|
3428
3540
|
cols,
|
|
3429
3541
|
rows,
|
|
3430
3542
|
persistent: true,
|
|
3431
3543
|
showCursor: interactive,
|
|
3432
3544
|
trimEnd: true,
|
|
3545
|
+
limit: RENDER_LIMIT,
|
|
3433
3546
|
flexGrow: 1
|
|
3434
3547
|
});
|
|
3435
3548
|
const origOnSelectionChanged = this.terminal.onSelectionChanged.bind(this.terminal);
|
|
@@ -3463,7 +3576,7 @@ class Pane {
|
|
|
3463
3576
|
}
|
|
3464
3577
|
feed(data) {
|
|
3465
3578
|
this.bytesFed += data.length;
|
|
3466
|
-
if (this.
|
|
3579
|
+
if (this.lineCounter > MAX_SCROLLBACK_LINES || this.bytesFed > MAX_BUFFER_BYTES) {
|
|
3467
3580
|
this.terminal.reset();
|
|
3468
3581
|
this.bytesFed = 0;
|
|
3469
3582
|
this.lineTimestamps = [];
|
|
@@ -3811,13 +3924,22 @@ class SearchController {
|
|
|
3811
3924
|
}
|
|
3812
3925
|
|
|
3813
3926
|
// src/ui/status-bar.ts
|
|
3814
|
-
import {
|
|
3927
|
+
import {
|
|
3928
|
+
BoxRenderable as BoxRenderable2,
|
|
3929
|
+
cyan,
|
|
3930
|
+
red,
|
|
3931
|
+
reverse,
|
|
3932
|
+
StyledText as StyledText2,
|
|
3933
|
+
TextRenderable as TextRenderable2,
|
|
3934
|
+
yellow
|
|
3935
|
+
} from "@opentui/core";
|
|
3815
3936
|
function plain(text) {
|
|
3816
3937
|
return { __isChunk: true, text };
|
|
3817
3938
|
}
|
|
3818
3939
|
|
|
3819
3940
|
class StatusBar {
|
|
3820
3941
|
renderable;
|
|
3942
|
+
text;
|
|
3821
3943
|
_searchMode = false;
|
|
3822
3944
|
_searchQuery = "";
|
|
3823
3945
|
_searchMatchCount = 0;
|
|
@@ -3825,16 +3947,23 @@ class StatusBar {
|
|
|
3825
3947
|
_crossProcessInfo;
|
|
3826
3948
|
_tempMessage = null;
|
|
3827
3949
|
_tempTimer = null;
|
|
3950
|
+
_inputMode = false;
|
|
3828
3951
|
constructor(renderer) {
|
|
3829
|
-
this.renderable = new
|
|
3952
|
+
this.renderable = new BoxRenderable2(renderer, {
|
|
3830
3953
|
id: "status-bar",
|
|
3831
3954
|
width: "100%",
|
|
3955
|
+
backgroundColor: "#1a1a1a",
|
|
3956
|
+
paddingX: 1,
|
|
3957
|
+
minHeight: 1
|
|
3958
|
+
});
|
|
3959
|
+
this.text = new TextRenderable2(renderer, {
|
|
3960
|
+
id: "status-bar-text",
|
|
3961
|
+
width: "100%",
|
|
3832
3962
|
wrapMode: "word",
|
|
3833
|
-
content: this.buildContent()
|
|
3834
|
-
bg: "#1a1a1a",
|
|
3835
|
-
paddingX: 1
|
|
3963
|
+
content: this.buildContent()
|
|
3836
3964
|
});
|
|
3837
|
-
this.
|
|
3965
|
+
this.text.selectable = false;
|
|
3966
|
+
this.renderable.add(this.text);
|
|
3838
3967
|
}
|
|
3839
3968
|
setSearchMode(active, query = "", matchCount = 0, currentIndex = -1, crossProcessInfo) {
|
|
3840
3969
|
this._searchMode = active;
|
|
@@ -3842,19 +3971,23 @@ class StatusBar {
|
|
|
3842
3971
|
this._searchMatchCount = matchCount;
|
|
3843
3972
|
this._searchCurrentIndex = currentIndex;
|
|
3844
3973
|
this._crossProcessInfo = crossProcessInfo;
|
|
3845
|
-
this.
|
|
3974
|
+
this.text.content = this.buildContent();
|
|
3846
3975
|
}
|
|
3847
3976
|
showTemporaryMessage(message, duration = 2000) {
|
|
3848
3977
|
if (this._tempTimer)
|
|
3849
3978
|
clearTimeout(this._tempTimer);
|
|
3850
3979
|
this._tempMessage = message;
|
|
3851
|
-
this.
|
|
3980
|
+
this.text.content = this.buildContent();
|
|
3852
3981
|
this._tempTimer = setTimeout(() => {
|
|
3853
3982
|
this._tempMessage = null;
|
|
3854
3983
|
this._tempTimer = null;
|
|
3855
|
-
this.
|
|
3984
|
+
this.text.content = this.buildContent();
|
|
3856
3985
|
}, duration);
|
|
3857
3986
|
}
|
|
3987
|
+
setInputMode(active) {
|
|
3988
|
+
this._inputMode = active;
|
|
3989
|
+
this.text.content = this.buildContent();
|
|
3990
|
+
}
|
|
3858
3991
|
buildContent() {
|
|
3859
3992
|
if (this._tempMessage) {
|
|
3860
3993
|
return new StyledText2([cyan(this._tempMessage)]);
|
|
@@ -3862,6 +3995,9 @@ class StatusBar {
|
|
|
3862
3995
|
if (this._searchMode) {
|
|
3863
3996
|
return this.buildSearchContent();
|
|
3864
3997
|
}
|
|
3998
|
+
if (this._inputMode) {
|
|
3999
|
+
return new StyledText2([yellow("INPUT"), plain(" Type to send input to process. "), plain("Esc: exit")]);
|
|
4000
|
+
}
|
|
3865
4001
|
return new StyledText2([plain(STATUS_BAR_TEXT)]);
|
|
3866
4002
|
}
|
|
3867
4003
|
buildSearchContent() {
|
|
@@ -4150,8 +4286,10 @@ class App {
|
|
|
4150
4286
|
panes = new Map;
|
|
4151
4287
|
tabBar;
|
|
4152
4288
|
statusBar;
|
|
4289
|
+
helpOverlay;
|
|
4153
4290
|
search;
|
|
4154
4291
|
activePane = null;
|
|
4292
|
+
inputMode = false;
|
|
4155
4293
|
destroyed = false;
|
|
4156
4294
|
names;
|
|
4157
4295
|
termCols = 80;
|
|
@@ -4180,7 +4318,7 @@ class App {
|
|
|
4180
4318
|
this.termCols = Math.max(40, width - this.sidebarWidth - 2);
|
|
4181
4319
|
this.termRows = Math.max(5, height - 2);
|
|
4182
4320
|
const { termCols, termRows } = this;
|
|
4183
|
-
const layout = new
|
|
4321
|
+
const layout = new BoxRenderable3(this.renderer, {
|
|
4184
4322
|
id: "root",
|
|
4185
4323
|
flexDirection: "column",
|
|
4186
4324
|
width: "100%",
|
|
@@ -4189,14 +4327,14 @@ class App {
|
|
|
4189
4327
|
});
|
|
4190
4328
|
const processHexColors = buildProcessHexColorMap(this.names, this.config);
|
|
4191
4329
|
this.tabBar = new TabBar(this.renderer, this.names, processHexColors);
|
|
4192
|
-
const contentRow = new
|
|
4330
|
+
const contentRow = new BoxRenderable3(this.renderer, {
|
|
4193
4331
|
id: "content-row",
|
|
4194
4332
|
flexDirection: "row",
|
|
4195
4333
|
flexGrow: 1,
|
|
4196
4334
|
width: "100%",
|
|
4197
4335
|
border: false
|
|
4198
4336
|
});
|
|
4199
|
-
const sidebar = new
|
|
4337
|
+
const sidebar = new BoxRenderable3(this.renderer, {
|
|
4200
4338
|
id: "sidebar",
|
|
4201
4339
|
width: this.sidebarWidth,
|
|
4202
4340
|
height: "100%",
|
|
@@ -4204,12 +4342,13 @@ class App {
|
|
|
4204
4342
|
borderColor: "#444"
|
|
4205
4343
|
});
|
|
4206
4344
|
sidebar.add(this.tabBar.renderable);
|
|
4207
|
-
const paneContainer = new
|
|
4345
|
+
const paneContainer = new BoxRenderable3(this.renderer, {
|
|
4208
4346
|
id: "pane-container",
|
|
4209
4347
|
flexGrow: 1,
|
|
4210
4348
|
border: false
|
|
4211
4349
|
});
|
|
4212
4350
|
this.statusBar = new StatusBar(this.renderer);
|
|
4351
|
+
this.helpOverlay = new HelpOverlay(this.renderer);
|
|
4213
4352
|
this.search = new SearchController({
|
|
4214
4353
|
logWriter: this.logWriter,
|
|
4215
4354
|
statusBar: this.statusBar,
|
|
@@ -4244,6 +4383,7 @@ class App {
|
|
|
4244
4383
|
layout.add(contentRow);
|
|
4245
4384
|
layout.add(this.statusBar.renderable);
|
|
4246
4385
|
this.renderer.root.add(layout);
|
|
4386
|
+
this.renderer.root.add(this.helpOverlay.renderable);
|
|
4247
4387
|
this.tabBar.onSelect((_index, name) => this.switchPane(name));
|
|
4248
4388
|
this.tabBar.onSelectionChanged((_index, name) => this.switchPane(name));
|
|
4249
4389
|
this.manager.on((event) => {
|
|
@@ -4280,6 +4420,14 @@ class App {
|
|
|
4280
4420
|
this.renderer.keyInput.on("keypress", (key) => {
|
|
4281
4421
|
log(key);
|
|
4282
4422
|
if (key.ctrl && key.name === "c") {
|
|
4423
|
+
if (this.helpOverlay.isVisible) {
|
|
4424
|
+
this.helpOverlay.hide();
|
|
4425
|
+
return;
|
|
4426
|
+
}
|
|
4427
|
+
if (this.inputMode) {
|
|
4428
|
+
this.exitInputMode();
|
|
4429
|
+
return;
|
|
4430
|
+
}
|
|
4283
4431
|
if (this.search.isActive) {
|
|
4284
4432
|
this.search.exit();
|
|
4285
4433
|
return;
|
|
@@ -4289,15 +4437,39 @@ class App {
|
|
|
4289
4437
|
});
|
|
4290
4438
|
return;
|
|
4291
4439
|
}
|
|
4440
|
+
if (this.helpOverlay.isVisible) {
|
|
4441
|
+
if (key.name === "escape" || key.sequence === "?" || key.name === "h") {
|
|
4442
|
+
this.helpOverlay.hide();
|
|
4443
|
+
}
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4292
4446
|
if (this.search.isActive) {
|
|
4293
4447
|
this.search.handleInput(key);
|
|
4294
4448
|
return;
|
|
4295
4449
|
}
|
|
4450
|
+
if (this.inputMode && this.activePane) {
|
|
4451
|
+
if (key.name === "escape") {
|
|
4452
|
+
this.exitInputMode();
|
|
4453
|
+
return;
|
|
4454
|
+
}
|
|
4455
|
+
if (key.sequence) {
|
|
4456
|
+
this.manager.write(this.activePane, key.sequence);
|
|
4457
|
+
}
|
|
4458
|
+
return;
|
|
4459
|
+
}
|
|
4296
4460
|
if (!this.activePane)
|
|
4297
4461
|
return;
|
|
4298
4462
|
const isInteractive = this.config.processes[this.activePane]?.interactive === true;
|
|
4299
4463
|
if (!isInteractive) {
|
|
4300
4464
|
const name = key.name.toLowerCase();
|
|
4465
|
+
if (key.sequence === "?" || name === "h") {
|
|
4466
|
+
this.helpOverlay.toggle();
|
|
4467
|
+
return;
|
|
4468
|
+
}
|
|
4469
|
+
if (name === "return") {
|
|
4470
|
+
this.enterInputMode();
|
|
4471
|
+
return;
|
|
4472
|
+
}
|
|
4301
4473
|
if (key.shift && name === SHORTCUTS.scrollToBottom.key) {
|
|
4302
4474
|
this.panes.get(this.activePane)?.scrollToBottom();
|
|
4303
4475
|
return;
|
|
@@ -4366,6 +4538,15 @@ class App {
|
|
|
4366
4538
|
this.switchPane(this.tabBar.getNameAtIndex(next));
|
|
4367
4539
|
return;
|
|
4368
4540
|
}
|
|
4541
|
+
if (name === "up" || name === "down") {
|
|
4542
|
+
const pane = this.panes.get(this.activePane);
|
|
4543
|
+
if (key.shift) {
|
|
4544
|
+
name === "up" ? pane?.scrollToTop() : pane?.scrollToBottom();
|
|
4545
|
+
} else {
|
|
4546
|
+
pane?.scrollBy(name === "up" ? -1 : 1);
|
|
4547
|
+
}
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
4369
4550
|
if (name === "pageup" || name === "pagedown") {
|
|
4370
4551
|
const pane = this.panes.get(this.activePane);
|
|
4371
4552
|
const delta = this.termRows - 2;
|
|
@@ -4392,9 +4573,33 @@ class App {
|
|
|
4392
4573
|
}
|
|
4393
4574
|
await this.manager.startAll(termCols, termRows);
|
|
4394
4575
|
}
|
|
4576
|
+
enterInputMode() {
|
|
4577
|
+
this.inputMode = true;
|
|
4578
|
+
this.statusBar.setInputMode(true);
|
|
4579
|
+
if (this.activePane) {
|
|
4580
|
+
const pane = this.panes.get(this.activePane);
|
|
4581
|
+
if (pane)
|
|
4582
|
+
pane.terminal.showCursor = true;
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
exitInputMode() {
|
|
4586
|
+
this.inputMode = false;
|
|
4587
|
+
this.statusBar.setInputMode(false);
|
|
4588
|
+
if (this.activePane) {
|
|
4589
|
+
const isInteractive = this.config.processes[this.activePane]?.interactive === true;
|
|
4590
|
+
if (!isInteractive) {
|
|
4591
|
+
const pane = this.panes.get(this.activePane);
|
|
4592
|
+
if (pane)
|
|
4593
|
+
pane.terminal.showCursor = false;
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
}
|
|
4395
4597
|
switchPane(name) {
|
|
4396
4598
|
if (this.activePane === name)
|
|
4397
4599
|
return;
|
|
4600
|
+
if (this.inputMode) {
|
|
4601
|
+
this.exitInputMode();
|
|
4602
|
+
}
|
|
4398
4603
|
if (this.search.isActive && !this.search.isAllMode) {
|
|
4399
4604
|
this.search.exit();
|
|
4400
4605
|
}
|