@h-rig/cli 0.0.6-alpha.82 → 0.0.6-alpha.83
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/dist/bin/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +2798 -1440
- package/dist/src/app/board.js +62 -13
- package/dist/src/app-opentui/adapters/command.d.ts +2 -0
- package/dist/src/app-opentui/adapters/command.js +329 -0
- package/dist/src/app-opentui/adapters/common.js +2 -2
- package/dist/src/app-opentui/adapters/doctor.js +63 -14
- package/dist/src/app-opentui/adapters/fleet.js +84 -20
- package/dist/src/app-opentui/adapters/inbox.js +83 -19
- package/dist/src/app-opentui/adapters/init.js +87 -23
- package/dist/src/app-opentui/adapters/pi-attach.js +96 -34
- package/dist/src/app-opentui/adapters/run-detail.js +83 -19
- package/dist/src/app-opentui/adapters/server.js +100 -23
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
- package/dist/src/app-opentui/adapters/tasks.js +742 -94
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +13769 -12368
- package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
- package/dist/src/app-opentui/command-pty-host.js +248 -0
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/focus-manager.d.ts +14 -0
- package/dist/src/app-opentui/focus-manager.js +24 -0
- package/dist/src/app-opentui/index.js +3802 -2882
- package/dist/src/app-opentui/intent.js +154 -48
- package/dist/src/app-opentui/keymap.d.ts +20 -0
- package/dist/src/app-opentui/keymap.js +707 -0
- package/dist/src/app-opentui/launch-routing.d.ts +16 -0
- package/dist/src/app-opentui/launch-routing.js +55 -0
- package/dist/src/app-opentui/layout.js +2 -2
- package/dist/src/app-opentui/pi-host-child.js +66 -16
- package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
- package/dist/src/app-opentui/pi-pty-host.js +9 -11
- package/dist/src/app-opentui/registry.js +3231 -2403
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
- package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
- package/dist/src/app-opentui/render/graphics.d.ts +1 -1
- package/dist/src/app-opentui/render/graphics.js +131 -16
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +408 -957
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +18 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +386 -356
- package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
- package/dist/src/app-opentui/render/panel-layout.js +48 -0
- package/dist/src/app-opentui/render/panels.d.ts +2 -0
- package/dist/src/app-opentui/render/panels.js +62 -29
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +163 -0
- package/dist/src/app-opentui/render/scene.d.ts +3 -0
- package/dist/src/app-opentui/render/scene.js +12 -0
- package/dist/src/app-opentui/render/text.js +5 -1
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +51 -16
- package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
- package/dist/src/app-opentui/runtime-resources.js +62 -0
- package/dist/src/app-opentui/runtime.js +2322 -1513
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +103 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +53 -14
- package/dist/src/app-opentui/scenes/error.js +44 -14
- package/dist/src/app-opentui/scenes/fleet.js +40 -21
- package/dist/src/app-opentui/scenes/handoff.js +142 -27
- package/dist/src/app-opentui/scenes/help.js +63 -48
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +64 -17
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +62 -21
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +80 -24
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +39 -25
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +71 -20
- package/dist/src/app-opentui/scenes/tasks.js +44 -22
- package/dist/src/app-opentui/state.js +70 -30
- package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
- package/dist/src/app-opentui/terminal-capabilities.js +15 -0
- package/dist/src/app-opentui/types.d.ts +10 -2
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_operator-view.js +63 -13
- package/dist/src/commands/_pi-frontend.js +63 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/connect.js +7 -1
- package/dist/src/commands/doctor.js +62 -13
- package/dist/src/commands/github.js +144 -23
- package/dist/src/commands/inbox.js +62 -13
- package/dist/src/commands/init.js +82 -18
- package/dist/src/commands/inspect.js +62 -13
- package/dist/src/commands/run.js +66 -13
- package/dist/src/commands/server.js +69 -14
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +62 -13
- package/dist/src/commands/task-run-driver.js +62 -13
- package/dist/src/commands/task.js +65 -14
- package/dist/src/commands.js +227 -92
- package/dist/src/index.js +234 -99
- package/package.json +8 -9
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StageLayout } from "../layout";
|
|
2
|
+
import type { AppScenePanel } from "../types";
|
|
3
|
+
export type PanelCellRect = {
|
|
4
|
+
readonly left: number;
|
|
5
|
+
readonly top: number;
|
|
6
|
+
readonly width: number;
|
|
7
|
+
readonly height: number;
|
|
8
|
+
readonly right: number;
|
|
9
|
+
readonly bottom: number;
|
|
10
|
+
readonly headerHeight: number;
|
|
11
|
+
readonly dividerY: number;
|
|
12
|
+
};
|
|
13
|
+
export type PanelContentRect = {
|
|
14
|
+
readonly left: number;
|
|
15
|
+
readonly top: number;
|
|
16
|
+
readonly width: number;
|
|
17
|
+
readonly height: number;
|
|
18
|
+
readonly paddingX: number;
|
|
19
|
+
readonly paddingY: number;
|
|
20
|
+
readonly contentWidth: number;
|
|
21
|
+
};
|
|
22
|
+
export type PanelPixelSize = {
|
|
23
|
+
readonly width: number;
|
|
24
|
+
readonly height: number;
|
|
25
|
+
};
|
|
26
|
+
export type PanelPixelPlacement = {
|
|
27
|
+
readonly left: number;
|
|
28
|
+
readonly top: number;
|
|
29
|
+
readonly width: number;
|
|
30
|
+
readonly height: number;
|
|
31
|
+
readonly leftCells: number;
|
|
32
|
+
readonly topCells: number;
|
|
33
|
+
readonly widthCells: number;
|
|
34
|
+
readonly heightCells: number;
|
|
35
|
+
};
|
|
36
|
+
export declare function panelCellRect(layout: StageLayout, panel: AppScenePanel): PanelCellRect | null;
|
|
37
|
+
export declare function panelContentRect(layout: StageLayout, panel: AppScenePanel): PanelContentRect | null;
|
|
38
|
+
export declare function panelPixelPlacement(layout: StageLayout, panel: AppScenePanel, size: PanelPixelSize, minPixels?: number): PanelPixelPlacement | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/app-opentui/render/panel-layout.ts
|
|
3
|
+
function panelCellRect(layout, panel) {
|
|
4
|
+
const left = Math.max(0, Math.min(layout.width - 1, layout.centerLeft + (panel.left ?? 0)));
|
|
5
|
+
const desiredWidth = panel.width ?? layout.centerWidth;
|
|
6
|
+
const width = Math.max(1, Math.min(layout.width - left, desiredWidth));
|
|
7
|
+
const top = Math.max(0, Math.min(layout.height - 1, layout.centerTop + panel.top));
|
|
8
|
+
const height = Math.max(1, Math.min(layout.height - top, panel.height));
|
|
9
|
+
if (width <= 3 || height <= 3)
|
|
10
|
+
return null;
|
|
11
|
+
const right = left + width - 1;
|
|
12
|
+
const bottom = top + height - 1;
|
|
13
|
+
const headerHeight = panel.chrome === "ad-terminal" ? Math.max(3, Math.min(height - 2, panel.headerHeight ?? 3)) : 0;
|
|
14
|
+
const dividerY = headerHeight ? Math.min(bottom - 1, top + headerHeight) : top;
|
|
15
|
+
return { left, top, width, height, right, bottom, headerHeight, dividerY };
|
|
16
|
+
}
|
|
17
|
+
function panelContentRect(layout, panel) {
|
|
18
|
+
const rect = panelCellRect(layout, panel);
|
|
19
|
+
if (!rect)
|
|
20
|
+
return null;
|
|
21
|
+
const chromeInset = panel.chrome === "ad-terminal" ? 1 : 0;
|
|
22
|
+
const left = rect.left + chromeInset;
|
|
23
|
+
const top = rect.top + rect.headerHeight + chromeInset;
|
|
24
|
+
const width = Math.max(1, Math.min(layout.width - left, rect.width - chromeInset * 2));
|
|
25
|
+
const height = Math.max(1, Math.min(layout.height - top, rect.height - rect.headerHeight - chromeInset * 2));
|
|
26
|
+
const paddingX = panel.paddingX ?? (panel.chrome === "ad-terminal" ? 5 : 2);
|
|
27
|
+
const paddingY = panel.paddingY ?? (panel.chrome === "ad-terminal" ? 2 : 1);
|
|
28
|
+
const contentWidth = Math.max(1, width - paddingX * 2 - 1);
|
|
29
|
+
return { left, top, width, height, paddingX, paddingY, contentWidth };
|
|
30
|
+
}
|
|
31
|
+
function panelPixelPlacement(layout, panel, size, minPixels = 8) {
|
|
32
|
+
const cellW = size.width / layout.width;
|
|
33
|
+
const cellH = size.height / layout.height;
|
|
34
|
+
const leftCells = layout.centerLeft + (panel.left ?? 0);
|
|
35
|
+
const topCells = layout.centerTop + panel.top;
|
|
36
|
+
const widthCells = panel.width ?? layout.centerWidth;
|
|
37
|
+
const heightCells = panel.height;
|
|
38
|
+
const left = Math.round(leftCells * cellW);
|
|
39
|
+
const top = Math.round(topCells * cellH);
|
|
40
|
+
const width = Math.round(widthCells * cellW);
|
|
41
|
+
const height = Math.round(heightCells * cellH);
|
|
42
|
+
return width > minPixels && height > minPixels ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
panelPixelPlacement,
|
|
46
|
+
panelContentRect,
|
|
47
|
+
panelCellRect
|
|
48
|
+
};
|
|
@@ -4,6 +4,8 @@ import type { AppSceneLine, AppScenePanel } from "../types";
|
|
|
4
4
|
import { type TextLineRenderable } from "./text";
|
|
5
5
|
export type ScrollPanelRenderable = ScrollBoxRenderable & {
|
|
6
6
|
__rigTextLines: TextLineRenderable[];
|
|
7
|
+
__rigRenderer: CliRenderer;
|
|
8
|
+
__rigOnLineMouseDown?: (line: AppSceneLine, event: MouseEvent) => void;
|
|
7
9
|
__rigPanelId?: string;
|
|
8
10
|
};
|
|
9
11
|
export declare function createScrollPanelLayer(renderer: CliRenderer, id: string, onLineMouseDown?: (line: AppSceneLine, event: MouseEvent) => void): ScrollPanelRenderable;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/app-opentui/render/panels.ts
|
|
3
|
-
import { RGBA, ScrollBoxRenderable } from "@opentui/core";
|
|
3
|
+
import { RGBA as RGBA2, ScrollBoxRenderable } from "@opentui/core";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/app-opentui/theme.ts
|
|
6
6
|
import {
|
|
@@ -40,8 +40,39 @@ var styles = {
|
|
|
40
40
|
magenta: otuiFg(RIG_UI.magenta)
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
// packages/cli/src/app-opentui/render/panel-layout.ts
|
|
44
|
+
function panelCellRect(layout, panel) {
|
|
45
|
+
const left = Math.max(0, Math.min(layout.width - 1, layout.centerLeft + (panel.left ?? 0)));
|
|
46
|
+
const desiredWidth = panel.width ?? layout.centerWidth;
|
|
47
|
+
const width = Math.max(1, Math.min(layout.width - left, desiredWidth));
|
|
48
|
+
const top = Math.max(0, Math.min(layout.height - 1, layout.centerTop + panel.top));
|
|
49
|
+
const height = Math.max(1, Math.min(layout.height - top, panel.height));
|
|
50
|
+
if (width <= 3 || height <= 3)
|
|
51
|
+
return null;
|
|
52
|
+
const right = left + width - 1;
|
|
53
|
+
const bottom = top + height - 1;
|
|
54
|
+
const headerHeight = panel.chrome === "ad-terminal" ? Math.max(3, Math.min(height - 2, panel.headerHeight ?? 3)) : 0;
|
|
55
|
+
const dividerY = headerHeight ? Math.min(bottom - 1, top + headerHeight) : top;
|
|
56
|
+
return { left, top, width, height, right, bottom, headerHeight, dividerY };
|
|
57
|
+
}
|
|
58
|
+
function panelContentRect(layout, panel) {
|
|
59
|
+
const rect = panelCellRect(layout, panel);
|
|
60
|
+
if (!rect)
|
|
61
|
+
return null;
|
|
62
|
+
const chromeInset = panel.chrome === "ad-terminal" ? 1 : 0;
|
|
63
|
+
const left = rect.left + chromeInset;
|
|
64
|
+
const top = rect.top + rect.headerHeight + chromeInset;
|
|
65
|
+
const width = Math.max(1, Math.min(layout.width - left, rect.width - chromeInset * 2));
|
|
66
|
+
const height = Math.max(1, Math.min(layout.height - top, rect.height - rect.headerHeight - chromeInset * 2));
|
|
67
|
+
const paddingX = panel.paddingX ?? (panel.chrome === "ad-terminal" ? 5 : 2);
|
|
68
|
+
const paddingY = panel.paddingY ?? (panel.chrome === "ad-terminal" ? 2 : 1);
|
|
69
|
+
const contentWidth = Math.max(1, width - paddingX * 2 - 1);
|
|
70
|
+
return { left, top, width, height, paddingX, paddingY, contentWidth };
|
|
71
|
+
}
|
|
72
|
+
|
|
43
73
|
// packages/cli/src/app-opentui/render/text.ts
|
|
44
|
-
import { TextAttributes as TextAttributes2, TextRenderable } from "@opentui/core";
|
|
74
|
+
import { RGBA, TextAttributes as TextAttributes2, TextRenderable } from "@opentui/core";
|
|
75
|
+
var TRANSPARENT = RGBA.fromInts(0, 0, 0, 0);
|
|
45
76
|
function selectableMouseHandler(currentLineRef, onLineMouseDown) {
|
|
46
77
|
return (event) => {
|
|
47
78
|
const currentLine = currentLineRef();
|
|
@@ -74,25 +105,26 @@ function applyFlowTextLine(renderable, line, width) {
|
|
|
74
105
|
renderable.width = Math.max(1, width);
|
|
75
106
|
renderable.content = line?.styledText ?? line?.text ?? "";
|
|
76
107
|
renderable.fg = line?.fg ?? RIG_UI.ink2;
|
|
108
|
+
renderable.bg = line?.bg ?? TRANSPARENT;
|
|
77
109
|
renderable.attributes = line?.bold ? TextAttributes2.BOLD : line?.dim ? TextAttributes2.DIM : 0;
|
|
78
110
|
}
|
|
79
111
|
|
|
80
112
|
// packages/cli/src/app-opentui/render/panels.ts
|
|
81
|
-
var
|
|
113
|
+
var TRANSPARENT2 = RGBA2.fromInts(0, 0, 0, 0);
|
|
82
114
|
var MAX_PANEL_LINES = 420;
|
|
83
115
|
function hexToRgba(hex, alpha) {
|
|
84
116
|
const clean = hex.replace(/^#/, "");
|
|
85
117
|
const full = clean.length === 3 ? clean.split("").map((part) => `${part}${part}`).join("") : clean;
|
|
86
118
|
const value = Number.parseInt(full, 16);
|
|
87
119
|
if (!Number.isFinite(value))
|
|
88
|
-
return
|
|
89
|
-
return
|
|
120
|
+
return RGBA2.fromInts(14, 15, 17, alpha);
|
|
121
|
+
return RGBA2.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255, alpha);
|
|
90
122
|
}
|
|
91
123
|
function panelBackground(panel) {
|
|
92
124
|
return hexToRgba(panel.backgroundColor ?? RIG_UI.panel, panel.backgroundAlpha ?? 184);
|
|
93
125
|
}
|
|
94
126
|
function panelBorder(panel) {
|
|
95
|
-
return panel.borderColor ? hexToRgba(panel.borderColor, panel.borderAlpha ?? 54) :
|
|
127
|
+
return panel.borderColor ? hexToRgba(panel.borderColor, panel.borderAlpha ?? 54) : TRANSPARENT2;
|
|
96
128
|
}
|
|
97
129
|
function createScrollPanelLayer(renderer, id, onLineMouseDown) {
|
|
98
130
|
const panel = new ScrollBoxRenderable(renderer, {
|
|
@@ -102,7 +134,7 @@ function createScrollPanelLayer(renderer, id, onLineMouseDown) {
|
|
|
102
134
|
top: -2,
|
|
103
135
|
width: 1,
|
|
104
136
|
height: 1,
|
|
105
|
-
backgroundColor:
|
|
137
|
+
backgroundColor: TRANSPARENT2,
|
|
106
138
|
border: false,
|
|
107
139
|
shouldFill: true,
|
|
108
140
|
opacity: 1,
|
|
@@ -118,7 +150,7 @@ function createScrollPanelLayer(renderer, id, onLineMouseDown) {
|
|
|
118
150
|
visible: false,
|
|
119
151
|
width: 1,
|
|
120
152
|
trackOptions: {
|
|
121
|
-
backgroundColor:
|
|
153
|
+
backgroundColor: TRANSPARENT2,
|
|
122
154
|
foregroundColor: hexToRgba(RIG_UI.ink4, 84)
|
|
123
155
|
}
|
|
124
156
|
},
|
|
@@ -126,18 +158,24 @@ function createScrollPanelLayer(renderer, id, onLineMouseDown) {
|
|
|
126
158
|
height: 0,
|
|
127
159
|
visible: false,
|
|
128
160
|
trackOptions: {
|
|
129
|
-
backgroundColor:
|
|
130
|
-
foregroundColor:
|
|
161
|
+
backgroundColor: TRANSPARENT2,
|
|
162
|
+
foregroundColor: TRANSPARENT2
|
|
131
163
|
}
|
|
132
164
|
}
|
|
133
165
|
});
|
|
134
166
|
panel.__rigTextLines = [];
|
|
135
|
-
|
|
136
|
-
|
|
167
|
+
panel.__rigRenderer = renderer;
|
|
168
|
+
panel.__rigOnLineMouseDown = onLineMouseDown;
|
|
169
|
+
return panel;
|
|
170
|
+
}
|
|
171
|
+
function ensurePanelLineCount(panel, desiredCount) {
|
|
172
|
+
const target = Math.min(MAX_PANEL_LINES, Math.max(0, desiredCount));
|
|
173
|
+
while (panel.__rigTextLines.length < target) {
|
|
174
|
+
const index = panel.__rigTextLines.length;
|
|
175
|
+
const line = createFlowTextLine(panel.__rigRenderer, `${panel.id}-line-${index}`, panel.__rigOnLineMouseDown);
|
|
137
176
|
panel.__rigTextLines.push(line);
|
|
138
177
|
panel.add(line);
|
|
139
178
|
}
|
|
140
|
-
return panel;
|
|
141
179
|
}
|
|
142
180
|
function hidePanel(panel) {
|
|
143
181
|
panel.visible = false;
|
|
@@ -145,7 +183,7 @@ function hidePanel(panel) {
|
|
|
145
183
|
panel.left = 0;
|
|
146
184
|
panel.width = 1;
|
|
147
185
|
panel.height = 1;
|
|
148
|
-
panel.backgroundColor =
|
|
186
|
+
panel.backgroundColor = TRANSPARENT2;
|
|
149
187
|
panel.border = false;
|
|
150
188
|
panel.__rigPanelId = undefined;
|
|
151
189
|
panel.__rigTextLines.forEach((line) => applyFlowTextLine(line, undefined, 1));
|
|
@@ -157,19 +195,12 @@ function applyScrollPanels(panels, layout, scenePanels) {
|
|
|
157
195
|
hidePanel(renderable);
|
|
158
196
|
return;
|
|
159
197
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
const left = cardLeft + (panel.chrome === "ad-terminal" ? 1 : 0);
|
|
167
|
-
const top = cardTop + headerHeight + (panel.chrome === "ad-terminal" ? 1 : 0);
|
|
168
|
-
const width = Math.max(1, Math.min(layout.width - left, cardWidth - (panel.chrome === "ad-terminal" ? 2 : 0)));
|
|
169
|
-
const height = Math.max(1, Math.min(layout.height - top, cardHeight - headerHeight - (panel.chrome === "ad-terminal" ? 2 : 0)));
|
|
170
|
-
const paddingX = panel.paddingX ?? (panel.chrome === "ad-terminal" ? 5 : 2);
|
|
171
|
-
const paddingY = panel.paddingY ?? (panel.chrome === "ad-terminal" ? 2 : 1);
|
|
172
|
-
const contentWidth = Math.max(1, width - paddingX * 2 - 1);
|
|
198
|
+
const contentRect = panelContentRect(layout, panel);
|
|
199
|
+
if (!contentRect) {
|
|
200
|
+
hidePanel(renderable);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const { left, top, width, height, paddingX, paddingY, contentWidth } = contentRect;
|
|
173
204
|
renderable.visible = true;
|
|
174
205
|
renderable.left = left;
|
|
175
206
|
renderable.top = top;
|
|
@@ -177,7 +208,7 @@ function applyScrollPanels(panels, layout, scenePanels) {
|
|
|
177
208
|
renderable.height = height;
|
|
178
209
|
renderable.zIndex = panel.zIndex ?? 6;
|
|
179
210
|
renderable.opacity = panel.opacity ?? 1;
|
|
180
|
-
renderable.backgroundColor = panel.chrome === "ad-terminal" ?
|
|
211
|
+
renderable.backgroundColor = panel.chrome === "ad-terminal" ? TRANSPARENT2 : panelBackground(panel);
|
|
181
212
|
renderable.border = panel.chrome === "ad-terminal" ? false : panel.border ?? false;
|
|
182
213
|
renderable.borderColor = panelBorder(panel);
|
|
183
214
|
renderable.paddingX = paddingX;
|
|
@@ -190,8 +221,10 @@ function applyScrollPanels(panels, layout, scenePanels) {
|
|
|
190
221
|
renderable.__rigPanelId = panel.id;
|
|
191
222
|
renderable.scrollTo(0);
|
|
192
223
|
}
|
|
224
|
+
const lineCount = Math.min(panel.lines.length, MAX_PANEL_LINES);
|
|
225
|
+
ensurePanelLineCount(renderable, lineCount);
|
|
193
226
|
renderable.__rigTextLines.forEach((line, lineIndex) => {
|
|
194
|
-
applyFlowTextLine(line, panel.lines[lineIndex], contentWidth);
|
|
227
|
+
applyFlowTextLine(line, lineIndex < lineCount ? panel.lines[lineIndex] : undefined, contentWidth);
|
|
195
228
|
});
|
|
196
229
|
});
|
|
197
230
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StageLayout } from "../layout";
|
|
2
|
+
import type { AppSceneFrame, AppSceneId } from "../types";
|
|
3
|
+
export type PreloaderTransitionState = {
|
|
4
|
+
scene: AppSceneId | null;
|
|
5
|
+
startedAtMs: number;
|
|
6
|
+
minUntilMs: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function maybeStartPreloaderTransition(transition: PreloaderTransitionState, scene: AppSceneId, now?: number): void;
|
|
9
|
+
export declare function isPreloaderActive(transition: PreloaderTransitionState, targetReady: boolean, now?: number): boolean;
|
|
10
|
+
export declare function renderAsciiPreloaderFrame(scene: AppSceneId, layout: StageLayout, tick: number): AppSceneFrame;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/app-opentui/theme.ts
|
|
3
|
+
import {
|
|
4
|
+
bold as otuiBold,
|
|
5
|
+
dim as otuiDim,
|
|
6
|
+
fg as otuiFg,
|
|
7
|
+
t,
|
|
8
|
+
TextAttributes
|
|
9
|
+
} from "@opentui/core";
|
|
10
|
+
var RIG_UI = {
|
|
11
|
+
bg: "#070809",
|
|
12
|
+
bg2: "#0b0c0e",
|
|
13
|
+
panel: "#101115",
|
|
14
|
+
panel2: "#101115",
|
|
15
|
+
glass: "#14161b",
|
|
16
|
+
ink: "#f2f3f6",
|
|
17
|
+
ink2: "#aeb0ba",
|
|
18
|
+
ink3: "#6c6e79",
|
|
19
|
+
ink4: "#44464f",
|
|
20
|
+
lime: "#ccff4d",
|
|
21
|
+
limeDim: "#a9d63f",
|
|
22
|
+
cyan: "#56d8ff",
|
|
23
|
+
red: "#ff5d5d",
|
|
24
|
+
yellow: "#ffd24d",
|
|
25
|
+
magenta: "#ff79b0"
|
|
26
|
+
};
|
|
27
|
+
var styles = {
|
|
28
|
+
ink: otuiFg(RIG_UI.ink),
|
|
29
|
+
ink2: otuiFg(RIG_UI.ink2),
|
|
30
|
+
ink3: otuiFg(RIG_UI.ink3),
|
|
31
|
+
ink4: otuiFg(RIG_UI.ink4),
|
|
32
|
+
lime: otuiFg(RIG_UI.lime),
|
|
33
|
+
limeDim: otuiFg(RIG_UI.limeDim),
|
|
34
|
+
cyan: otuiFg(RIG_UI.cyan),
|
|
35
|
+
red: otuiFg(RIG_UI.red),
|
|
36
|
+
yellow: otuiFg(RIG_UI.yellow),
|
|
37
|
+
magenta: otuiFg(RIG_UI.magenta)
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// packages/cli/src/app-opentui/render/ascii-fleet.ts
|
|
41
|
+
var LEAD_DRONE = [
|
|
42
|
+
" .-=-. .-=-. ",
|
|
43
|
+
" ( !!! ) ( !!! ) ",
|
|
44
|
+
" '-=-'._ _.'-=-' ",
|
|
45
|
+
" '._ _.' ",
|
|
46
|
+
" '=$$$$$$$=.' ",
|
|
47
|
+
" =$$$$$$$$$$$= ",
|
|
48
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
49
|
+
" $$$@@ @@$$$ ",
|
|
50
|
+
" $$@ ? @$$$ ",
|
|
51
|
+
" $$$@ '-' @$$$ ",
|
|
52
|
+
" $$$@@ @@$$$ ",
|
|
53
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
54
|
+
" =$$$$$$$$$$$= ",
|
|
55
|
+
" '=$$$$$$$=.' ",
|
|
56
|
+
" _.' '._ ",
|
|
57
|
+
" .-=-.' '.-=-. ",
|
|
58
|
+
" ( !!! ) ( !!! ) ",
|
|
59
|
+
" '-=-' '-=-' "
|
|
60
|
+
];
|
|
61
|
+
var MINI_DRONE = [
|
|
62
|
+
"(!!!) (!!!)",
|
|
63
|
+
" \\%==%/ ",
|
|
64
|
+
" %%?%% ",
|
|
65
|
+
" /%==%\\ ",
|
|
66
|
+
"(!!!) (!!!)"
|
|
67
|
+
];
|
|
68
|
+
var ROTORS = ["---", "\\\\\\", "|||", "///"];
|
|
69
|
+
var FLEET_GRID_WIDTH = 52;
|
|
70
|
+
var FLEET_GRID_HEIGHT = 27;
|
|
71
|
+
var FLEET = [
|
|
72
|
+
{ art: MINI_DRONE, x: 0, y: 2, amp: 1, speed: 0.05, phase: 0, dx: 1, driftSpeed: 0.02 },
|
|
73
|
+
{ art: MINI_DRONE, x: 39, y: 2, amp: 1, speed: 0.044, phase: 2.1, dx: 1, driftSpeed: 0.017 },
|
|
74
|
+
{ art: MINI_DRONE, x: 1, y: 20, amp: 1, speed: 0.048, phase: 4.2, dx: 1, driftSpeed: 0.023 },
|
|
75
|
+
{ art: MINI_DRONE, x: 38, y: 20, amp: 1, speed: 0.041, phase: 1.3, dx: 1, driftSpeed: 0.015 },
|
|
76
|
+
{ art: LEAD_DRONE, x: 10, y: 5, amp: 2, speed: 0.04, phase: 0, dx: 0, driftSpeed: 0.011 }
|
|
77
|
+
];
|
|
78
|
+
function fleetRows(tick, options = {}) {
|
|
79
|
+
const animate = options.animate ?? true;
|
|
80
|
+
const t2 = animate ? tick : 0;
|
|
81
|
+
const blade = ROTORS[Math.floor(t2 / 4) % ROTORS.length];
|
|
82
|
+
const pulse = Math.sin(t2 * 0.07);
|
|
83
|
+
const eye = pulse > 0.45 ? "@" : pulse > -0.1 ? "o" : ".";
|
|
84
|
+
const grid = Array.from({ length: FLEET_GRID_HEIGHT }, () => Array.from({ length: FLEET_GRID_WIDTH }, () => " "));
|
|
85
|
+
for (const drone of FLEET) {
|
|
86
|
+
const yOffset = drone.y + Math.round(drone.amp * Math.sin(t2 * drone.speed + drone.phase));
|
|
87
|
+
const xOffset = drone.x + Math.round(drone.dx * Math.sin(t2 * drone.driftSpeed + drone.phase));
|
|
88
|
+
for (let row = 0;row < drone.art.length; row += 1) {
|
|
89
|
+
const source = drone.art[row];
|
|
90
|
+
const targetY = yOffset + row;
|
|
91
|
+
if (targetY < 0 || targetY >= FLEET_GRID_HEIGHT)
|
|
92
|
+
continue;
|
|
93
|
+
for (let col = 0;col < source.length; col += 1) {
|
|
94
|
+
let char = source[col];
|
|
95
|
+
if (char === " ")
|
|
96
|
+
continue;
|
|
97
|
+
const targetX = xOffset + col;
|
|
98
|
+
if (targetX < 0 || targetX >= FLEET_GRID_WIDTH)
|
|
99
|
+
continue;
|
|
100
|
+
if (source.slice(col, col + 3) === "!!!") {
|
|
101
|
+
for (let rotorIndex = 0;rotorIndex < 3; rotorIndex += 1) {
|
|
102
|
+
if (targetX + rotorIndex < FLEET_GRID_WIDTH)
|
|
103
|
+
grid[targetY][targetX + rotorIndex] = blade[rotorIndex];
|
|
104
|
+
}
|
|
105
|
+
col += 2;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (char === "?")
|
|
109
|
+
char = eye;
|
|
110
|
+
grid[targetY][targetX] = char;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return grid.map((row) => row.join("").replace(/\s+$/, ""));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// packages/cli/src/app-opentui/render/preloader.ts
|
|
118
|
+
var PRELOADER_MIN_DURATION_MS = 520;
|
|
119
|
+
var PRELOADER_MAX_DURATION_MS = 5000;
|
|
120
|
+
function maybeStartPreloaderTransition(transition, scene, now = Date.now()) {
|
|
121
|
+
if (transition.scene === scene)
|
|
122
|
+
return;
|
|
123
|
+
transition.scene = scene;
|
|
124
|
+
transition.startedAtMs = now;
|
|
125
|
+
transition.minUntilMs = now + PRELOADER_MIN_DURATION_MS;
|
|
126
|
+
}
|
|
127
|
+
function isPreloaderActive(transition, targetReady, now = Date.now()) {
|
|
128
|
+
if (now < transition.minUntilMs)
|
|
129
|
+
return true;
|
|
130
|
+
if (!targetReady && now - transition.startedAtMs < PRELOADER_MAX_DURATION_MS)
|
|
131
|
+
return true;
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
function preloaderLine(line, options = {}) {
|
|
135
|
+
return { text: line, bg: RIG_UI.bg, ...options };
|
|
136
|
+
}
|
|
137
|
+
function blockLine(text, leftPad, fg) {
|
|
138
|
+
return preloaderLine(`${" ".repeat(Math.max(0, leftPad))}${text.padEnd(FLEET_GRID_WIDTH)}`, { fg });
|
|
139
|
+
}
|
|
140
|
+
function renderAsciiPreloaderFrame(scene, layout, tick) {
|
|
141
|
+
const sprite = fleetRows(tick);
|
|
142
|
+
const leftPad = Math.max(0, Math.floor((layout.width - FLEET_GRID_WIDTH) / 2));
|
|
143
|
+
const blank = () => preloaderLine("");
|
|
144
|
+
const body = [
|
|
145
|
+
...sprite.map((text, index) => blockLine(text, leftPad, index >= 7 && index <= 18 ? RIG_UI.lime : RIG_UI.limeDim))
|
|
146
|
+
];
|
|
147
|
+
const topPadding = Math.max(0, Math.floor((layout.height - body.length) / 2));
|
|
148
|
+
const bottomPadding = Math.max(0, layout.height - body.length - topPadding);
|
|
149
|
+
return {
|
|
150
|
+
scene,
|
|
151
|
+
title: "Preloader",
|
|
152
|
+
lines: [...Array.from({ length: topPadding }, blank), ...body, ...Array.from({ length: bottomPadding }, blank)],
|
|
153
|
+
live: true,
|
|
154
|
+
fullScreen: true,
|
|
155
|
+
hideTypeBar: true,
|
|
156
|
+
footer: { message: "" }
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export {
|
|
160
|
+
renderAsciiPreloaderFrame,
|
|
161
|
+
maybeStartPreloaderTransition,
|
|
162
|
+
isPreloaderActive
|
|
163
|
+
};
|
|
@@ -11,6 +11,9 @@ export declare function deckRow(input: {
|
|
|
11
11
|
readonly activateOnClick?: boolean;
|
|
12
12
|
}): AppSceneLine;
|
|
13
13
|
export declare function kv(label: string, value: string, fg?: string): AppSceneLine;
|
|
14
|
+
export declare function loadingLine(message?: string): AppSceneLine;
|
|
15
|
+
export declare function emptyLine(message?: string): AppSceneLine;
|
|
16
|
+
export declare function errorLine(message: string): AppSceneLine;
|
|
14
17
|
export declare function fitSceneLine(input: AppSceneLine, layout: StageLayout): AppSceneLine;
|
|
15
18
|
export declare function fitSceneLines(lines: readonly AppSceneLine[], layout: StageLayout): AppSceneLine[];
|
|
16
19
|
export declare function makeSceneFrame(input: AppSceneFrame): AppSceneFrame;
|
|
@@ -85,6 +85,15 @@ function deckRow(input) {
|
|
|
85
85
|
function kv(label, value, fg = RIG_UI.ink2) {
|
|
86
86
|
return line(` ${label.toUpperCase().padEnd(10)} ${value}`, { fg });
|
|
87
87
|
}
|
|
88
|
+
function loadingLine(message = "loading") {
|
|
89
|
+
return line(` \u27F2 ${message}`, { fg: RIG_UI.limeDim });
|
|
90
|
+
}
|
|
91
|
+
function emptyLine(message = "nothing here yet") {
|
|
92
|
+
return line(` ${message}`, { fg: RIG_UI.ink4 });
|
|
93
|
+
}
|
|
94
|
+
function errorLine(message) {
|
|
95
|
+
return line(` ! ${message}`, { fg: RIG_UI.magenta, bold: true });
|
|
96
|
+
}
|
|
88
97
|
function fitSceneLine(input, layout) {
|
|
89
98
|
const fitted = truncateText(input.text, layout.centerWidth);
|
|
90
99
|
return {
|
|
@@ -100,10 +109,13 @@ function makeSceneFrame(input) {
|
|
|
100
109
|
}
|
|
101
110
|
export {
|
|
102
111
|
makeSceneFrame,
|
|
112
|
+
loadingLine,
|
|
103
113
|
line,
|
|
104
114
|
kv,
|
|
105
115
|
fitSceneLines,
|
|
106
116
|
fitSceneLine,
|
|
117
|
+
errorLine,
|
|
118
|
+
emptyLine,
|
|
107
119
|
deckRow,
|
|
108
120
|
center,
|
|
109
121
|
blank
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/app-opentui/render/text.ts
|
|
3
|
-
import { TextAttributes as TextAttributes2, TextRenderable } from "@opentui/core";
|
|
3
|
+
import { RGBA, TextAttributes as TextAttributes2, TextRenderable } from "@opentui/core";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/app-opentui/theme.ts
|
|
6
6
|
import {
|
|
@@ -41,6 +41,7 @@ var styles = {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
// packages/cli/src/app-opentui/render/text.ts
|
|
44
|
+
var TRANSPARENT = RGBA.fromInts(0, 0, 0, 0);
|
|
44
45
|
function selectableMouseHandler(currentLineRef, onLineMouseDown) {
|
|
45
46
|
return (event) => {
|
|
46
47
|
const currentLine = currentLineRef();
|
|
@@ -91,6 +92,7 @@ function applyTextLine(renderable, line, top, left, width) {
|
|
|
91
92
|
renderable.width = width;
|
|
92
93
|
renderable.content = line?.styledText ?? line?.text ?? "";
|
|
93
94
|
renderable.fg = line?.fg ?? RIG_UI.ink2;
|
|
95
|
+
renderable.bg = line?.bg ?? TRANSPARENT;
|
|
94
96
|
renderable.attributes = line?.bold ? TextAttributes2.BOLD : line?.dim ? TextAttributes2.DIM : 0;
|
|
95
97
|
}
|
|
96
98
|
function applyFlowTextLine(renderable, line, width) {
|
|
@@ -100,6 +102,7 @@ function applyFlowTextLine(renderable, line, width) {
|
|
|
100
102
|
renderable.width = Math.max(1, width);
|
|
101
103
|
renderable.content = line?.styledText ?? line?.text ?? "";
|
|
102
104
|
renderable.fg = line?.fg ?? RIG_UI.ink2;
|
|
105
|
+
renderable.bg = line?.bg ?? TRANSPARENT;
|
|
103
106
|
renderable.attributes = line?.bold ? TextAttributes2.BOLD : line?.dim ? TextAttributes2.DIM : 0;
|
|
104
107
|
}
|
|
105
108
|
function clearTextLines(renderables, from = 0) {
|
|
@@ -110,6 +113,7 @@ function clearTextLines(renderables, from = 0) {
|
|
|
110
113
|
renderable.top = -1;
|
|
111
114
|
renderable.left = 0;
|
|
112
115
|
renderable.width = 1;
|
|
116
|
+
renderable.bg = TRANSPARENT;
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
export {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { InputRenderable, TextRenderable, type CliRenderer } from "@opentui/core";
|
|
1
|
+
import { BoxRenderable, InputRenderable, TextRenderable, type CliRenderer } from "@opentui/core";
|
|
2
2
|
import type { AppFooterState, AppTypeBarState } from "../types";
|
|
3
3
|
export type TypeBarRenderables = {
|
|
4
|
+
readonly background: BoxRenderable;
|
|
4
5
|
readonly input: InputRenderable;
|
|
5
6
|
readonly prefix: TextRenderable;
|
|
6
7
|
readonly footer: TextRenderable;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/app-opentui/render/type-bar.ts
|
|
3
|
-
import { InputRenderable, InputRenderableEvents, TextRenderable } from "@opentui/core";
|
|
3
|
+
import { BoxRenderable, InputRenderable, InputRenderableEvents, RGBA, TextRenderable } from "@opentui/core";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/app-opentui/theme.ts
|
|
6
6
|
import {
|
|
@@ -53,48 +53,72 @@ function truncateText(text, width) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// packages/cli/src/app-opentui/render/type-bar.ts
|
|
56
|
+
var TYPEBAR_BG = RGBA.fromInts(20, 25, 14, 224);
|
|
57
|
+
var TYPEBAR_BORDER = RGBA.fromInts(204, 255, 77, 92);
|
|
56
58
|
function createTypeBar(renderer) {
|
|
57
|
-
const
|
|
58
|
-
id: "
|
|
59
|
-
content: " rig \u203A",
|
|
59
|
+
const background = new BoxRenderable(renderer, {
|
|
60
|
+
id: "typebar-card",
|
|
60
61
|
position: "absolute",
|
|
61
62
|
left: 0,
|
|
62
63
|
top: 0,
|
|
64
|
+
width: 1,
|
|
65
|
+
height: 1,
|
|
66
|
+
backgroundColor: TYPEBAR_BG,
|
|
67
|
+
border: true,
|
|
68
|
+
borderColor: TYPEBAR_BORDER,
|
|
69
|
+
zIndex: 4
|
|
70
|
+
});
|
|
71
|
+
const prefix = new TextRenderable(renderer, {
|
|
72
|
+
id: "typebar-prefix",
|
|
73
|
+
content: " \u203A",
|
|
74
|
+
position: "absolute",
|
|
75
|
+
left: 1,
|
|
76
|
+
top: 0,
|
|
63
77
|
fg: RIG_UI.lime,
|
|
78
|
+
zIndex: 7,
|
|
64
79
|
selectable: true
|
|
65
80
|
});
|
|
66
81
|
const input = new InputRenderable(renderer, {
|
|
67
|
-
id: "
|
|
82
|
+
id: "typebar-input",
|
|
68
83
|
position: "absolute",
|
|
69
|
-
left:
|
|
84
|
+
left: 4,
|
|
70
85
|
top: 0,
|
|
71
86
|
width: 40,
|
|
72
|
-
placeholder: "
|
|
87
|
+
placeholder: "runs \xB7 tasks \xB7 run next \xB7 help",
|
|
73
88
|
textColor: RIG_UI.ink,
|
|
74
89
|
cursorColor: RIG_UI.lime,
|
|
75
90
|
cursorStyle: { style: "block", blinking: false },
|
|
76
91
|
showCursor: true,
|
|
92
|
+
zIndex: 7,
|
|
77
93
|
selectable: true
|
|
78
94
|
});
|
|
79
95
|
const footer = new TextRenderable(renderer, {
|
|
80
|
-
id: "
|
|
96
|
+
id: "footer",
|
|
81
97
|
content: "",
|
|
82
98
|
position: "absolute",
|
|
83
99
|
left: 0,
|
|
84
100
|
top: 1,
|
|
85
101
|
width: "100%",
|
|
86
102
|
fg: RIG_UI.ink4,
|
|
103
|
+
zIndex: 7,
|
|
87
104
|
selectable: true
|
|
88
105
|
});
|
|
89
106
|
input.focus();
|
|
90
|
-
return { input, prefix, footer };
|
|
107
|
+
return { background, input, prefix, footer };
|
|
91
108
|
}
|
|
92
109
|
function positionTypeBar(renderables, input, footer, width, typeBarTop, footerTop) {
|
|
93
|
-
renderables.
|
|
110
|
+
renderables.background.visible = true;
|
|
111
|
+
const cardTop = Math.max(0, typeBarTop - 1);
|
|
112
|
+
renderables.background.left = 0;
|
|
113
|
+
renderables.background.top = cardTop;
|
|
114
|
+
renderables.background.width = width;
|
|
115
|
+
renderables.background.height = Math.max(3, footerTop - cardTop + 1);
|
|
116
|
+
renderables.prefix.content = " \u203A";
|
|
117
|
+
renderables.prefix.left = 2;
|
|
94
118
|
renderables.prefix.top = typeBarTop;
|
|
95
119
|
renderables.input.top = typeBarTop;
|
|
96
|
-
renderables.input.left =
|
|
97
|
-
renderables.input.width = Math.max(10, width -
|
|
120
|
+
renderables.input.left = 5;
|
|
121
|
+
renderables.input.width = Math.max(10, width - 8);
|
|
98
122
|
renderables.input.placeholder = input.placeholder;
|
|
99
123
|
renderables.input.showCursor = true;
|
|
100
124
|
renderables.footer.top = footerTop;
|
|
@@ -104,6 +128,8 @@ function positionTypeBar(renderables, input, footer, width, typeBarTop, footerTo
|
|
|
104
128
|
renderables.input.focus();
|
|
105
129
|
}
|
|
106
130
|
function hideTypeBar(renderables, height) {
|
|
131
|
+
renderables.background.visible = false;
|
|
132
|
+
renderables.background.top = height + 1;
|
|
107
133
|
renderables.prefix.content = "";
|
|
108
134
|
renderables.prefix.top = height + 1;
|
|
109
135
|
renderables.input.value = "";
|
|
@@ -119,13 +145,22 @@ function bindTypeBar(renderables, handlers) {
|
|
|
119
145
|
renderables.input.on(InputRenderableEvents.INPUT, (value) => handlers.onInput(value));
|
|
120
146
|
renderables.input.on(InputRenderableEvents.ENTER, (value) => handlers.onEnter(value));
|
|
121
147
|
}
|
|
148
|
+
function safeFooterMessage(message) {
|
|
149
|
+
const trimmed = message?.trim();
|
|
150
|
+
if (!trimmed)
|
|
151
|
+
return null;
|
|
152
|
+
if (/https?:|\b[a-z0-9-]+\.[a-z]{2,}\b|\brig\b/i.test(trimmed))
|
|
153
|
+
return null;
|
|
154
|
+
return trimmed;
|
|
155
|
+
}
|
|
122
156
|
function formatFooter(footer, width) {
|
|
157
|
+
const safeMessage = safeFooterMessage(footer.message);
|
|
123
158
|
const parts = [
|
|
124
|
-
footer.project ?
|
|
125
|
-
footer.server ?
|
|
126
|
-
footer.auth ?
|
|
159
|
+
footer.project ? "workspace" : null,
|
|
160
|
+
footer.server ? "server selected" : null,
|
|
161
|
+
footer.auth ? "auth ready" : null,
|
|
127
162
|
footer.run ? `run ${footer.run}` : null,
|
|
128
|
-
|
|
163
|
+
safeMessage
|
|
129
164
|
].filter((part) => Boolean(part));
|
|
130
165
|
return ` ${truncateText(parts.join(" \xB7 "), Math.max(8, width - 2))}`;
|
|
131
166
|
}
|