omnius 1.0.862 → 1.0.863
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/index.js +2 -2
- package/dist/tui/native/worker.js +1327 -237
- package/docs/DISCOVERY.json +797 -6
- package/docs/DISCOVERY.md +25 -2
- package/docs/work-orders/evidence/instrument-100x30.png +0 -0
- package/docs/work-orders/evidence/instrument-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-160x48.png +0 -0
- package/docs/work-orders/evidence/instrument-60x20.png +0 -0
- package/docs/work-orders/evidence/instrument-80x24.png +0 -0
- package/docs/work-orders/evidence/instrument-collapse-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-help-scroll-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-package-artifact-20261016.json +18 -0
- package/docs/work-orders/evidence/instrument-package-install-20261016.json +69 -0
- package/docs/work-orders/evidence/instrument-popup-api-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-api-input-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-config-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-dark-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-dark-60x20.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-light-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-model-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-popup-network-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-production-terminal-live.json +6228 -0
- package/docs/work-orders/evidence/instrument-semantic-components-20261016.json +76 -0
- package/docs/work-orders/evidence/instrument-targeted-verification-20261016.json +408 -0
- package/docs/work-orders/evidence/instrument-terminal-light-120x36.png +0 -0
- package/docs/work-orders/instrument-command-surface-audit.md +237 -0
- package/docs/work-orders/instrument-tui.md +108 -0
- package/docs/work-orders/native-tui-renderer.md +8 -6
- package/npm-shrinkwrap.json +6 -6
- package/package.json +1 -1
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
import { createWriteStream } from "node:fs";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
4
|
import { PassThrough } from "node:stream";
|
|
5
|
-
import { CliRenderEvents as
|
|
5
|
+
import { CliRenderEvents as CliRenderEvents4, NativeSession, RGBA as RGBA3, createCliRenderer } from "@opentui/core";
|
|
6
6
|
|
|
7
7
|
// packages/cli/src/tui/native/components/frontend.ts
|
|
8
8
|
import {
|
|
9
|
-
BoxRenderable as
|
|
10
|
-
CliRenderEvents,
|
|
9
|
+
BoxRenderable as BoxRenderable4,
|
|
10
|
+
CliRenderEvents as CliRenderEvents2,
|
|
11
11
|
EmbeddedTerminalRenderable as EmbeddedTerminalRenderable2,
|
|
12
|
-
ScrollBoxRenderable as
|
|
12
|
+
ScrollBoxRenderable as ScrollBoxRenderable4,
|
|
13
13
|
SyntaxStyle as SyntaxStyle2,
|
|
14
14
|
TabSelectRenderable,
|
|
15
15
|
TextareaRenderable as TextareaRenderable2,
|
|
16
|
-
TextRenderable as
|
|
16
|
+
TextRenderable as TextRenderable4,
|
|
17
|
+
TextAttributes as TextAttributes6,
|
|
17
18
|
createClipboard,
|
|
18
19
|
createHostClipboard,
|
|
19
20
|
createRendererClipboardAdapter
|
|
@@ -26,22 +27,23 @@ import {
|
|
|
26
27
|
EmbeddedTerminalRenderable,
|
|
27
28
|
ImageRenderable,
|
|
28
29
|
MarkdownRenderable,
|
|
30
|
+
RenderableEvents,
|
|
29
31
|
ScrollBoxRenderable,
|
|
30
32
|
TextRenderable
|
|
31
33
|
} from "@opentui/core";
|
|
32
34
|
|
|
33
35
|
// packages/cli/src/tui/native/components/text.ts
|
|
34
|
-
import { RGBA, StyledText } from "@opentui/core";
|
|
36
|
+
import { CliRenderEvents, RGBA, StyledText, TextAttributes } from "@opentui/core";
|
|
35
37
|
|
|
36
38
|
// packages/cli/src/tui/native/text.ts
|
|
37
39
|
function stripTextControls(value) {
|
|
38
|
-
return value.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "").replace(/\x1b[P_^X][\s\S]*?\x1b\\/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[\s\S]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "");
|
|
40
|
+
return value.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "").replace(/\x1b[P_^X][\s\S]*?\x1b\\/g, "").replace(/\x1b\[[-0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[\s\S]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "");
|
|
39
41
|
}
|
|
40
42
|
function nativeTextFromAnsi(value) {
|
|
41
43
|
const result = [];
|
|
42
44
|
let style = { attributes: 0 };
|
|
43
45
|
let previous = 0;
|
|
44
|
-
const pattern = /\x1b(?:\[[0-?]*[ -/]*[@-~]|\][\s\S]*?(?:\x07|\x1b\\)|[P_^X][\s\S]*?\x1b\\|[\s\S])/g;
|
|
46
|
+
const pattern = /\x1b(?:\[[-0-?]*[ -/]*[@-~]|\][\s\S]*?(?:\x07|\x1b\\)|[P_^X][\s\S]*?\x1b\\|[\s\S])/g;
|
|
45
47
|
const append = (text) => {
|
|
46
48
|
const clean = stripTextControls(text);
|
|
47
49
|
if (!clean) return;
|
|
@@ -60,7 +62,7 @@ function nativeTextFromAnsi(value) {
|
|
|
60
62
|
else delete style.link;
|
|
61
63
|
continue;
|
|
62
64
|
}
|
|
63
|
-
if (!/^\x1b\[[
|
|
65
|
+
if (!/^\x1b\[[-\d;:]*m$/.test(seq)) continue;
|
|
64
66
|
const codes = seq.slice(2, -1).replace(/:/g, ";").split(";").map(Number);
|
|
65
67
|
for (let i = 0; i < codes.length; i++) {
|
|
66
68
|
const code = codes[i];
|
|
@@ -89,7 +91,10 @@ function nativeTextFromAnsi(value) {
|
|
|
89
91
|
else if (code >= 40 && code <= 47) style.bg = `index:${code - 40}`;
|
|
90
92
|
else if (code >= 100 && code <= 107) style.bg = `index:${code - 100 + 8}`;
|
|
91
93
|
else if ((code === 38 || code === 48) && codes[i + 1] === 5) {
|
|
92
|
-
|
|
94
|
+
const role = code === 38 ? "fg" : "bg";
|
|
95
|
+
const index = codes[i + 2] ?? 0;
|
|
96
|
+
if (index < 0) delete style[role];
|
|
97
|
+
else style[role] = `index:${Math.min(255, index)}`;
|
|
93
98
|
i += 2;
|
|
94
99
|
} else if ((code === 38 || code === 48) && codes[i + 1] === 2) {
|
|
95
100
|
const rgb = codes.slice(i + 2, i + 5).map(
|
|
@@ -106,20 +111,6 @@ function nativeTextFromAnsi(value) {
|
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
// packages/cli/src/tui/native/components/text.ts
|
|
109
|
-
var colorCache = /* @__PURE__ */ new Map();
|
|
110
|
-
function nativeColor(value, background = false) {
|
|
111
|
-
if (value === void 0 || value === -1 || value === "default")
|
|
112
|
-
return background ? RGBA.defaultBackground() : RGBA.defaultForeground();
|
|
113
|
-
const key = String(value);
|
|
114
|
-
if (key === "index:-1") return background ? RGBA.defaultBackground() : RGBA.defaultForeground();
|
|
115
|
-
let color = colorCache.get(key);
|
|
116
|
-
if (!color) {
|
|
117
|
-
color = typeof value === "number" || /^\d+$/.test(key) ? RGBA.fromIndex(Number(value)) : key.startsWith("index:") ? RGBA.fromIndex(Number(key.slice(6))) : RGBA.fromHex(key);
|
|
118
|
-
if (colorCache.size >= 1024) colorCache.delete(colorCache.keys().next().value);
|
|
119
|
-
colorCache.set(key, color);
|
|
120
|
-
}
|
|
121
|
-
return color;
|
|
122
|
-
}
|
|
123
114
|
function plainText(value) {
|
|
124
115
|
return typeof value === "string" ? stripTextControls(value) : (value ?? []).map((span) => stripTextControls(span.text)).join("");
|
|
125
116
|
}
|
|
@@ -130,27 +121,71 @@ function styledText(value) {
|
|
|
130
121
|
return new StyledText(textSpans(value).map((span) => ({
|
|
131
122
|
__isChunk: true,
|
|
132
123
|
text: span.text,
|
|
133
|
-
...span.fg === void 0 ? {} : { fg: nativeColor(span.fg) },
|
|
134
|
-
...span.bg === void 0 ? {} : { bg: nativeColor(span.bg, true) },
|
|
135
124
|
...span.attributes === void 0 ? {} : { attributes: span.attributes },
|
|
136
125
|
...span.link ? { link: { url: span.link } } : {}
|
|
137
126
|
})));
|
|
138
127
|
}
|
|
139
|
-
function palette(
|
|
128
|
+
function palette(_theme) {
|
|
129
|
+
const background2 = RGBA.defaultBackground();
|
|
130
|
+
const foreground2 = RGBA.defaultForeground();
|
|
140
131
|
return {
|
|
141
|
-
background:
|
|
142
|
-
foreground:
|
|
143
|
-
accent:
|
|
144
|
-
dim:
|
|
145
|
-
border:
|
|
146
|
-
warning:
|
|
147
|
-
error:
|
|
148
|
-
success:
|
|
149
|
-
selection:
|
|
132
|
+
background: background2,
|
|
133
|
+
foreground: foreground2,
|
|
134
|
+
accent: foreground2,
|
|
135
|
+
dim: foreground2,
|
|
136
|
+
border: foreground2,
|
|
137
|
+
warning: foreground2,
|
|
138
|
+
error: foreground2,
|
|
139
|
+
success: foreground2,
|
|
140
|
+
selection: background2
|
|
150
141
|
};
|
|
151
142
|
}
|
|
143
|
+
function installDefaultSelectionContrast(renderer2) {
|
|
144
|
+
const foreground2 = RGBA.defaultForeground();
|
|
145
|
+
const background2 = RGBA.defaultBackground();
|
|
146
|
+
const paintSelection = (buffer) => {
|
|
147
|
+
const lines = buffer.getSpanLines();
|
|
148
|
+
buffer.withBuffers((cells) => {
|
|
149
|
+
for (let y = 0; y < lines.length; y++) {
|
|
150
|
+
let x = 0;
|
|
151
|
+
for (const span of lines[y].spans) {
|
|
152
|
+
if (span.fg.intent === "default" && span.bg.intent === "default" && !(span.attributes & TextAttributes.INVERSE) && span.fg.equals(background2) && span.bg.equals(foreground2)) {
|
|
153
|
+
for (let column = x; column < x + span.width; column++)
|
|
154
|
+
cells.attributes[y * cells.width + column] |= TextAttributes.INVERSE;
|
|
155
|
+
}
|
|
156
|
+
x += span.width;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
renderer2.addPostProcessFn(paintSelection);
|
|
162
|
+
let disposed = false;
|
|
163
|
+
const dispose = () => {
|
|
164
|
+
if (disposed) return;
|
|
165
|
+
disposed = true;
|
|
166
|
+
renderer2.removePostProcessFn(paintSelection);
|
|
167
|
+
renderer2.off(CliRenderEvents.DESTROY, dispose);
|
|
168
|
+
};
|
|
169
|
+
renderer2.once(CliRenderEvents.DESTROY, dispose);
|
|
170
|
+
renderer2.requestRender();
|
|
171
|
+
return dispose;
|
|
172
|
+
}
|
|
152
173
|
|
|
153
174
|
// packages/cli/src/tui/native/components/blocks.ts
|
|
175
|
+
function nativeProgressText(value, working = false) {
|
|
176
|
+
if (value === void 0 || !Number.isFinite(value)) return `[············] ${working ? "working" : "pending"}`;
|
|
177
|
+
const ratio = Math.max(0, Math.min(1, value));
|
|
178
|
+
const filled = Math.round(ratio * 12);
|
|
179
|
+
return `[${"•".repeat(filled)}${"·".repeat(12 - filled)}] ${Math.round(ratio * 100)}%`;
|
|
180
|
+
}
|
|
181
|
+
function nativeControlFocus(control, baseAttributes = 0) {
|
|
182
|
+
const update = () => {
|
|
183
|
+
if (!control.isDestroyed) control.attributes = baseAttributes | (control.focused ? 32 : 0);
|
|
184
|
+
};
|
|
185
|
+
control.on(RenderableEvents.FOCUSED, update);
|
|
186
|
+
control.on(RenderableEvents.BLURRED, update);
|
|
187
|
+
update();
|
|
188
|
+
}
|
|
154
189
|
var NativeBlockView = class {
|
|
155
190
|
constructor(context, spec, inline = false) {
|
|
156
191
|
this.context = context;
|
|
@@ -178,13 +213,13 @@ var NativeBlockView = class {
|
|
|
178
213
|
body;
|
|
179
214
|
children;
|
|
180
215
|
actions;
|
|
181
|
-
|
|
182
|
-
progressValue;
|
|
216
|
+
progress;
|
|
183
217
|
image;
|
|
184
218
|
terminal;
|
|
185
219
|
collapsed = false;
|
|
186
220
|
previousModelCollapsed;
|
|
187
221
|
signature = "";
|
|
222
|
+
actionsSignature = "";
|
|
188
223
|
pixel;
|
|
189
224
|
get block() {
|
|
190
225
|
return this.spec;
|
|
@@ -192,16 +227,18 @@ var NativeBlockView = class {
|
|
|
192
227
|
build() {
|
|
193
228
|
const { renderer: renderer2, colors, syntax } = this.context;
|
|
194
229
|
const spec = this.spec;
|
|
195
|
-
if (spec.kind === "card") this.root.borderStyle = "
|
|
196
|
-
this.root.border = spec.kind === "card";
|
|
197
|
-
this.root.
|
|
198
|
-
this.root.
|
|
230
|
+
if (spec.kind === "card") this.root.borderStyle = "single";
|
|
231
|
+
this.root.border = spec.kind === "card" ? ["left", "bottom"] : false;
|
|
232
|
+
this.root.paddingLeft = spec.kind === "card" ? 1 : 0;
|
|
233
|
+
this.root.paddingRight = 0;
|
|
234
|
+
this.root.marginBottom = 0;
|
|
199
235
|
this.root.flexDirection = spec.kind === "row" ? "row" : "column";
|
|
200
236
|
if (spec.title) {
|
|
201
237
|
this.title = new TextRenderable(renderer2, {
|
|
202
238
|
id: `block-title:${spec.id}`,
|
|
203
239
|
content: "",
|
|
204
|
-
fg: colors.
|
|
240
|
+
fg: colors.foreground,
|
|
241
|
+
attributes: 1,
|
|
205
242
|
flexShrink: 0,
|
|
206
243
|
width: "100%",
|
|
207
244
|
wrapMode: "word",
|
|
@@ -210,8 +247,15 @@ var NativeBlockView = class {
|
|
|
210
247
|
if (event.button !== 0 || !this.spec.collapsible) return;
|
|
211
248
|
this.toggle();
|
|
212
249
|
event.stopPropagation();
|
|
250
|
+
},
|
|
251
|
+
onKeyDown: (key) => {
|
|
252
|
+
if (key.eventType === "release" || !this.spec.collapsible || key.name !== "return" && key.sequence !== " ") return;
|
|
253
|
+
this.toggle();
|
|
254
|
+
key.preventDefault();
|
|
255
|
+
key.stopPropagation();
|
|
213
256
|
}
|
|
214
257
|
});
|
|
258
|
+
nativeControlFocus(this.title, 1);
|
|
215
259
|
this.root.add(this.title);
|
|
216
260
|
}
|
|
217
261
|
this.body = new BoxRenderable(renderer2, {
|
|
@@ -303,26 +347,24 @@ var NativeBlockView = class {
|
|
|
303
347
|
this.body.add(this.text);
|
|
304
348
|
}
|
|
305
349
|
if (spec.kind === "progress") {
|
|
306
|
-
this.
|
|
350
|
+
this.progress = new TextRenderable(renderer2, {
|
|
307
351
|
id: `progress-track:${spec.id}`,
|
|
308
352
|
width: "100%",
|
|
309
353
|
height: 1,
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
backgroundColor: colors.accent
|
|
354
|
+
content: "",
|
|
355
|
+
fg: colors.foreground,
|
|
356
|
+
bg: colors.background,
|
|
357
|
+
attributes: 2,
|
|
358
|
+
wrapMode: "none",
|
|
359
|
+
selectable: false
|
|
317
360
|
});
|
|
318
|
-
this.
|
|
319
|
-
this.body.add(this.progressBar);
|
|
361
|
+
this.body.add(this.progress);
|
|
320
362
|
}
|
|
321
363
|
this.children = new NativeBlockList(this.context, this.body);
|
|
322
364
|
this.actions = new BoxRenderable(renderer2, {
|
|
323
365
|
id: `block-actions:${spec.id}`,
|
|
324
366
|
flexDirection: "row",
|
|
325
|
-
gap:
|
|
367
|
+
gap: 1,
|
|
326
368
|
flexShrink: 0
|
|
327
369
|
});
|
|
328
370
|
this.root.add(this.actions);
|
|
@@ -331,8 +373,9 @@ var NativeBlockView = class {
|
|
|
331
373
|
this.children?.destroy();
|
|
332
374
|
if (this.terminal) this.context.terminals.delete(this.spec.id);
|
|
333
375
|
for (const child of [...this.root.getChildren()]) child.destroyRecursively();
|
|
334
|
-
this.title = this.text = this.body = this.actions = this.
|
|
376
|
+
this.title = this.text = this.body = this.actions = this.progress = this.image = this.terminal = void 0;
|
|
335
377
|
this.children = void 0;
|
|
378
|
+
this.actionsSignature = "";
|
|
336
379
|
this.build();
|
|
337
380
|
}
|
|
338
381
|
update(spec) {
|
|
@@ -346,7 +389,10 @@ var NativeBlockView = class {
|
|
|
346
389
|
this.collapsed = spec.collapsed ?? false;
|
|
347
390
|
this.previousModelCollapsed = spec.collapsed;
|
|
348
391
|
}
|
|
349
|
-
if (this.title)
|
|
392
|
+
if (this.title) {
|
|
393
|
+
this.title.content = `${spec.collapsible ? this.collapsed ? "▸ " : "· " : ""}${plainText(spec.title)}`;
|
|
394
|
+
this.title.focusable = Boolean(spec.collapsible);
|
|
395
|
+
}
|
|
350
396
|
if (this.body) this.body.visible = !this.collapsed;
|
|
351
397
|
if (this.text instanceof TextRenderable) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
|
|
352
398
|
else if (this.text instanceof MarkdownRenderable || this.text instanceof CodeRenderable) {
|
|
@@ -357,16 +403,19 @@ var NativeBlockView = class {
|
|
|
357
403
|
if (this.image && spec.imagePath && this.image.source !== spec.imagePath) this.image.source = spec.imagePath;
|
|
358
404
|
if (this.terminal) this.terminal.height = spec.height ?? 14;
|
|
359
405
|
if (this.image) this.image.height = spec.height ?? 12;
|
|
360
|
-
if (this.
|
|
406
|
+
if (this.progress) this.progress.content = nativeProgressText(spec.progress, spec.streaming);
|
|
361
407
|
this.children?.replace(spec.children ?? []);
|
|
362
|
-
|
|
408
|
+
const actionsSignature = JSON.stringify(spec.actions ?? []);
|
|
409
|
+
if (this.actions && actionsSignature !== this.actionsSignature) {
|
|
410
|
+
this.actionsSignature = actionsSignature;
|
|
363
411
|
for (const child of [...this.actions.getChildren()]) child.destroy();
|
|
364
412
|
for (const action of spec.actions ?? []) {
|
|
365
413
|
const button = new TextRenderable(this.context.renderer, {
|
|
366
414
|
id: `block-action:${spec.id}:${action.id}`,
|
|
367
|
-
content:
|
|
368
|
-
fg:
|
|
369
|
-
|
|
415
|
+
content: plainText(action.label),
|
|
416
|
+
fg: this.context.colors.foreground,
|
|
417
|
+
bg: this.context.colors.background,
|
|
418
|
+
attributes: action.disabled ? 2 : action.active ? 1 : 0,
|
|
370
419
|
flexShrink: 0,
|
|
371
420
|
selectable: false,
|
|
372
421
|
onMouseDown: (event) => {
|
|
@@ -375,12 +424,13 @@ var NativeBlockView = class {
|
|
|
375
424
|
event.stopPropagation();
|
|
376
425
|
},
|
|
377
426
|
onKeyDown: (key) => {
|
|
378
|
-
if (key.name !== "return" || action.disabled) return;
|
|
427
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
379
428
|
this.context.emit({ type: "action", action: action.command ?? action.id, id: this.spec.id });
|
|
380
429
|
key.preventDefault();
|
|
381
430
|
}
|
|
382
431
|
});
|
|
383
432
|
button.focusable = !action.disabled;
|
|
433
|
+
nativeControlFocus(button, action.disabled ? 2 : action.active ? 1 : 0);
|
|
384
434
|
if (action.id.startsWith("pixel:") && !action.disabled) {
|
|
385
435
|
button.onMouseOver = () => this.context.emit({ type: "action", action: `hover:${action.id}`, id: this.spec.id });
|
|
386
436
|
button.onMouseOut = () => this.context.emit({ type: "action", action: `hover-end:${action.id}`, id: this.spec.id });
|
|
@@ -394,25 +444,31 @@ var NativeBlockView = class {
|
|
|
394
444
|
toggle() {
|
|
395
445
|
this.collapsed = !this.collapsed;
|
|
396
446
|
if (this.body) this.body.visible = !this.collapsed;
|
|
397
|
-
if (this.title) this.title.content =
|
|
447
|
+
if (this.title) this.title.content = `${this.collapsed ? "▸ " : "· "}${plainText(this.spec.title)}`;
|
|
398
448
|
this.context.emit({ type: "action", action: "toggle-block", id: this.spec.id });
|
|
399
449
|
}
|
|
400
450
|
applyTheme() {
|
|
401
451
|
const colors = this.context.colors;
|
|
402
452
|
this.root.backgroundColor = colors.background;
|
|
403
|
-
if (this.spec.kind === "card") this.root.borderColor =
|
|
453
|
+
if (this.spec.kind === "card") this.root.borderColor = colors.border;
|
|
404
454
|
else this.root.border = false;
|
|
405
455
|
if (this.title) {
|
|
406
|
-
this.title.fg =
|
|
456
|
+
this.title.fg = colors.foreground;
|
|
407
457
|
this.title.bg = colors.background;
|
|
408
458
|
}
|
|
409
459
|
if (this.body) this.body.backgroundColor = colors.background;
|
|
410
460
|
if (this.text) {
|
|
411
|
-
this.text.fg =
|
|
461
|
+
this.text.fg = colors.foreground;
|
|
412
462
|
this.text.bg = colors.background;
|
|
413
463
|
}
|
|
414
|
-
if (this.
|
|
415
|
-
|
|
464
|
+
if (this.progress) {
|
|
465
|
+
this.progress.fg = colors.foreground;
|
|
466
|
+
this.progress.bg = colors.background;
|
|
467
|
+
}
|
|
468
|
+
for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable) {
|
|
469
|
+
action.fg = colors.foreground;
|
|
470
|
+
action.bg = colors.background;
|
|
471
|
+
}
|
|
416
472
|
this.children?.applyTheme();
|
|
417
473
|
}
|
|
418
474
|
destroy() {
|
|
@@ -480,15 +536,100 @@ import {
|
|
|
480
536
|
InputRenderable,
|
|
481
537
|
ScrollBoxRenderable as ScrollBoxRenderable2,
|
|
482
538
|
TextareaRenderable,
|
|
539
|
+
TextAttributes as TextAttributes4,
|
|
483
540
|
TextRenderable as TextRenderable2
|
|
484
541
|
} from "@opentui/core";
|
|
485
542
|
|
|
486
543
|
// packages/cli/src/tui/native/components/select.ts
|
|
487
544
|
import { SelectRenderable } from "@opentui/core";
|
|
545
|
+
|
|
546
|
+
// packages/cli/src/tui/native/components/scrollbar.ts
|
|
547
|
+
import { TextAttributes as TextAttributes2 } from "@opentui/core";
|
|
548
|
+
function drawDotRail(buffer, x, y, length, horizontal, progress, colors) {
|
|
549
|
+
length = Math.max(0, Math.trunc(length));
|
|
550
|
+
if (!length) return;
|
|
551
|
+
buffer.fillRect(x, y, horizontal ? length : 1, horizontal ? 1 : length, colors.background);
|
|
552
|
+
if (horizontal) buffer.drawText("─".repeat(length), x, y, colors.foreground, colors.background, TextAttributes2.DIM);
|
|
553
|
+
else for (let row = 0; row < length; row++) buffer.drawText("│", x, y + row, colors.foreground, colors.background, TextAttributes2.DIM);
|
|
554
|
+
const position = Math.round(Math.max(0, Math.min(1, progress)) * (length - 1));
|
|
555
|
+
buffer.drawText("•", x + (horizontal ? position : 0), y + (horizontal ? 0 : position), colors.foreground, colors.background, TextAttributes2.BOLD);
|
|
556
|
+
}
|
|
557
|
+
function styleNativeScrollbars(scroll, colors) {
|
|
558
|
+
for (const bar of [scroll.horizontalScrollBar, scroll.verticalScrollBar]) {
|
|
559
|
+
bar.showArrows = false;
|
|
560
|
+
const slider = bar.slider;
|
|
561
|
+
slider.foregroundColor = colors.foreground;
|
|
562
|
+
slider.backgroundColor = colors.background;
|
|
563
|
+
slider.renderAfter = (buffer) => {
|
|
564
|
+
const horizontal = slider.orientation === "horizontal";
|
|
565
|
+
drawDotRail(
|
|
566
|
+
buffer,
|
|
567
|
+
slider.x,
|
|
568
|
+
slider.y,
|
|
569
|
+
horizontal ? slider.width : slider.height,
|
|
570
|
+
horizontal,
|
|
571
|
+
slider.max > slider.min ? (slider.value - slider.min) / (slider.max - slider.min) : 0,
|
|
572
|
+
colors
|
|
573
|
+
);
|
|
574
|
+
};
|
|
575
|
+
let dragging = false;
|
|
576
|
+
const seek = (event) => {
|
|
577
|
+
const horizontal = slider.orientation === "horizontal";
|
|
578
|
+
const length = horizontal ? slider.width : slider.height;
|
|
579
|
+
const offset = horizontal ? event.x - slider.x : event.y - slider.y;
|
|
580
|
+
const progress = length > 1 ? Math.max(0, Math.min(1, offset / (length - 1))) : 0;
|
|
581
|
+
slider.value = slider.min + progress * (slider.max - slider.min);
|
|
582
|
+
event.preventDefault();
|
|
583
|
+
event.stopPropagation();
|
|
584
|
+
};
|
|
585
|
+
slider.onMouseDown = (event) => {
|
|
586
|
+
if (event.button === 0) {
|
|
587
|
+
dragging = true;
|
|
588
|
+
seek(event);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
slider.onMouseDrag = (event) => {
|
|
592
|
+
if (dragging) seek(event);
|
|
593
|
+
};
|
|
594
|
+
slider.onMouseUp = (event) => {
|
|
595
|
+
if (dragging) seek(event);
|
|
596
|
+
dragging = false;
|
|
597
|
+
};
|
|
598
|
+
slider.onMouseDragEnd = () => {
|
|
599
|
+
dragging = false;
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// packages/cli/src/tui/native/components/select.ts
|
|
488
605
|
var NativeSelectRenderable = class extends SelectRenderable {
|
|
489
606
|
onActivate;
|
|
607
|
+
draggingRail = false;
|
|
490
608
|
constructor(renderer2, options) {
|
|
491
609
|
super(renderer2, options);
|
|
610
|
+
const colors = palette({ mode: "system", bg: -1, accent: -1, textPrimary: -1, textDim: -1, boxColor: -1 });
|
|
611
|
+
this.renderAfter = (buffer) => {
|
|
612
|
+
const count = this.visibleCount();
|
|
613
|
+
if (!this.showScrollIndicator || this.options.length <= count) return;
|
|
614
|
+
const offset = Math.max(0, Math.min(this.getSelectedIndex() - Math.floor(count / 2), this.options.length - count));
|
|
615
|
+
drawDotRail(buffer, this.width - 1, 0, this.height, false, offset / (this.options.length - count), colors);
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
visibleCount() {
|
|
619
|
+
return Math.max(1, Math.floor(this.height / (this.showDescription ? 2 : 1)));
|
|
620
|
+
}
|
|
621
|
+
seekRail(event) {
|
|
622
|
+
const progress = this.height > 1 ? Math.max(0, Math.min(1, (event.y - this.y) / (this.height - 1))) : 0;
|
|
623
|
+
const target = Math.round(progress * Math.max(0, this.options.length - 1));
|
|
624
|
+
for (let distance = 0; distance < this.options.length; distance++) {
|
|
625
|
+
const index = [target + distance, target - distance].find((candidate) => candidate >= 0 && candidate < this.options.length && !this.disabled(candidate));
|
|
626
|
+
if (index !== void 0) {
|
|
627
|
+
this.setSelectedIndex(index);
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
event.preventDefault();
|
|
632
|
+
event.stopPropagation();
|
|
492
633
|
}
|
|
493
634
|
disabled(index) {
|
|
494
635
|
const option = this.options[index];
|
|
@@ -517,6 +658,11 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
517
658
|
if (!this.disabled(this.getSelectedIndex())) super.selectCurrent();
|
|
518
659
|
}
|
|
519
660
|
onMouseEvent(event) {
|
|
661
|
+
if ((event.type === "drag" || event.type === "up") && this.draggingRail) {
|
|
662
|
+
this.seekRail(event);
|
|
663
|
+
if (event.type === "up") this.draggingRail = false;
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
520
666
|
if (event.type === "scroll") {
|
|
521
667
|
if (event.scroll?.direction === "up") this.moveUp(1);
|
|
522
668
|
else if (event.scroll?.direction === "down") this.moveDown(1);
|
|
@@ -524,6 +670,11 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
524
670
|
return;
|
|
525
671
|
}
|
|
526
672
|
if (event.type !== "down" || event.button !== 0) return;
|
|
673
|
+
if (this.showScrollIndicator && this.options.length > this.visibleCount() && event.x === this.x + this.width - 1) {
|
|
674
|
+
this.draggingRail = true;
|
|
675
|
+
this.seekRail(event);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
527
678
|
const lines = this.showDescription ? 2 : 1;
|
|
528
679
|
const count = Math.max(1, Math.floor(this.height / lines));
|
|
529
680
|
const start = Math.max(0, Math.min(this.getSelectedIndex() - Math.floor(count / 2), this.options.length - count));
|
|
@@ -536,6 +687,89 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
536
687
|
}
|
|
537
688
|
};
|
|
538
689
|
|
|
690
|
+
// packages/cli/src/tui/native/components/corner-frame.ts
|
|
691
|
+
import { RenderableEvents as RenderableEvents2, TextAttributes as TextAttributes3 } from "@opentui/core";
|
|
692
|
+
function drawCornerFrame(buffer, panel, colors, progress = 1) {
|
|
693
|
+
const { x, y, width, height } = panel;
|
|
694
|
+
if (width <= 0 || height <= 0) return;
|
|
695
|
+
const draw = (text, column, row) => buffer.drawText(text, column, row, colors.foreground, colors.background, TextAttributes3.DIM);
|
|
696
|
+
draw(" ".repeat(width), x, y);
|
|
697
|
+
if (height > 1) draw(" ".repeat(width), x, y + height - 1);
|
|
698
|
+
for (let row = 1; row < height - 1; row++) {
|
|
699
|
+
draw(" ", x, y + row);
|
|
700
|
+
if (width > 1) draw(" ", x + width - 1, y + row);
|
|
701
|
+
}
|
|
702
|
+
if (height === 1) {
|
|
703
|
+
draw("─".repeat(width), x, y);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (width === 1) {
|
|
707
|
+
for (let row = 0; row < height; row++) draw("│", x, y + row);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const fraction = Math.max(0, Math.min(1, progress));
|
|
711
|
+
const horizontal = Math.round(Math.min(4, Math.max(0, Math.floor((width - 3) / 2))) * fraction);
|
|
712
|
+
const vertical = Math.round(Math.min(2, Math.max(0, Math.floor((height - 3) / 2))) * fraction);
|
|
713
|
+
draw(`┌${"─".repeat(horizontal)}`, x, y);
|
|
714
|
+
draw(`${"─".repeat(horizontal)}┐`, x + width - horizontal - 1, y);
|
|
715
|
+
draw(`└${"─".repeat(horizontal)}`, x, y + height - 1);
|
|
716
|
+
draw(`${"─".repeat(horizontal)}┘`, x + width - horizontal - 1, y + height - 1);
|
|
717
|
+
for (let arm = 1; arm <= vertical; arm++) {
|
|
718
|
+
for (const column of [x, x + width - 1]) {
|
|
719
|
+
draw("│", column, y + arm);
|
|
720
|
+
draw("│", column, y + height - arm - 1);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
var NativeCornerFrame = class {
|
|
725
|
+
constructor(panel, colors, renderer2) {
|
|
726
|
+
this.panel = panel;
|
|
727
|
+
this.colors = colors;
|
|
728
|
+
this.renderer = renderer2;
|
|
729
|
+
this.previousRender = panel.renderAfter;
|
|
730
|
+
this.render = (buffer, deltaTime) => {
|
|
731
|
+
this.previousRender?.call(panel, buffer, deltaTime);
|
|
732
|
+
drawCornerFrame(buffer, panel, this.colors, this.progress);
|
|
733
|
+
};
|
|
734
|
+
panel.renderAfter = this.render;
|
|
735
|
+
panel.once(RenderableEvents2.DESTROYED, this.destroy);
|
|
736
|
+
if (process.env["OMNIUS_TUI_REDUCE_MOTION"] === "1" || process.env["REDUCE_MOTION"] === "1") this.progress = 1;
|
|
737
|
+
else {
|
|
738
|
+
let frame = 0;
|
|
739
|
+
const advance = () => {
|
|
740
|
+
if (this.disposed) return;
|
|
741
|
+
this.progress = ++frame / 4;
|
|
742
|
+
renderer2.requestRender();
|
|
743
|
+
if (frame < 4) this.timer = setTimeout(advance, 32);
|
|
744
|
+
else this.timer = void 0;
|
|
745
|
+
};
|
|
746
|
+
this.timer = setTimeout(advance, 32);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
panel;
|
|
750
|
+
colors;
|
|
751
|
+
renderer;
|
|
752
|
+
progress = 0;
|
|
753
|
+
timer;
|
|
754
|
+
disposed = false;
|
|
755
|
+
previousRender;
|
|
756
|
+
render;
|
|
757
|
+
get animating() {
|
|
758
|
+
return this.timer !== void 0;
|
|
759
|
+
}
|
|
760
|
+
setColors(colors) {
|
|
761
|
+
this.colors = colors;
|
|
762
|
+
}
|
|
763
|
+
destroy = () => {
|
|
764
|
+
if (this.disposed) return;
|
|
765
|
+
this.disposed = true;
|
|
766
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
767
|
+
this.timer = void 0;
|
|
768
|
+
this.panel.off(RenderableEvents2.DESTROYED, this.destroy);
|
|
769
|
+
if (this.panel.renderAfter === this.render) this.panel.renderAfter = this.previousRender;
|
|
770
|
+
};
|
|
771
|
+
};
|
|
772
|
+
|
|
539
773
|
// packages/cli/src/tui/native/components/modals.ts
|
|
540
774
|
var NativeModalView = class {
|
|
541
775
|
constructor(context, spec, close2) {
|
|
@@ -554,7 +788,8 @@ var NativeModalView = class {
|
|
|
554
788
|
justifyContent: "center",
|
|
555
789
|
alignItems: "center",
|
|
556
790
|
zIndex: 100,
|
|
557
|
-
backgroundColor:
|
|
791
|
+
backgroundColor: "transparent",
|
|
792
|
+
shouldFill: false
|
|
558
793
|
});
|
|
559
794
|
this.panel = new BoxRenderable2(renderer2, {
|
|
560
795
|
id: `modal-panel:${spec.id}`,
|
|
@@ -562,13 +797,15 @@ var NativeModalView = class {
|
|
|
562
797
|
width: "94%",
|
|
563
798
|
maxHeight: "94%",
|
|
564
799
|
border: true,
|
|
565
|
-
borderStyle: "
|
|
800
|
+
borderStyle: "single",
|
|
566
801
|
borderColor: colors.border,
|
|
567
802
|
backgroundColor: colors.background,
|
|
568
803
|
paddingX: 1,
|
|
569
|
-
paddingY:
|
|
804
|
+
paddingY: 0,
|
|
805
|
+
focusable: true,
|
|
570
806
|
onMouseDown: () => this.focus()
|
|
571
807
|
});
|
|
808
|
+
this.frame = new NativeCornerFrame(this.panel, colors, renderer2);
|
|
572
809
|
this.root.add(this.panel);
|
|
573
810
|
this.breadcrumb = this.addText(`modal-breadcrumb:${spec.id}`);
|
|
574
811
|
this.heading = this.addText(`modal-title:${spec.id}`);
|
|
@@ -585,20 +822,29 @@ var NativeModalView = class {
|
|
|
585
822
|
viewportCulling: true,
|
|
586
823
|
contentOptions: { flexDirection: "column" }
|
|
587
824
|
});
|
|
825
|
+
styleNativeScrollbars(this.document, colors);
|
|
588
826
|
this.blocks = new NativeBlockList(this.modalContext(), this.document);
|
|
589
827
|
this.panel.add(this.document);
|
|
590
828
|
} else {
|
|
591
|
-
this.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
829
|
+
this.progress = new TextRenderable2(renderer2, {
|
|
830
|
+
id: `modal-progress:${spec.id}`,
|
|
831
|
+
width: "100%",
|
|
832
|
+
height: 1,
|
|
833
|
+
content: "",
|
|
834
|
+
fg: colors.foreground,
|
|
835
|
+
bg: colors.background,
|
|
836
|
+
attributes: 2,
|
|
837
|
+
wrapMode: "none",
|
|
838
|
+
selectable: false
|
|
839
|
+
});
|
|
840
|
+
this.panel.add(this.progress);
|
|
595
841
|
const progressBlocks = new BoxRenderable2(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
|
|
596
842
|
this.panel.add(progressBlocks);
|
|
597
843
|
this.blocks = new NativeBlockList(this.modalContext(), progressBlocks);
|
|
598
844
|
}
|
|
599
845
|
this.error = this.addText(`modal-error:${spec.id}`);
|
|
600
846
|
this.hints = this.addText(`modal-hints:${spec.id}`);
|
|
601
|
-
this.actions = new BoxRenderable2(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", gap:
|
|
847
|
+
this.actions = new BoxRenderable2(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", gap: 1, width: "100%", marginTop: 0 });
|
|
602
848
|
this.panel.add(this.actions);
|
|
603
849
|
this.update(spec);
|
|
604
850
|
}
|
|
@@ -606,6 +852,7 @@ var NativeModalView = class {
|
|
|
606
852
|
close;
|
|
607
853
|
root;
|
|
608
854
|
panel;
|
|
855
|
+
frame;
|
|
609
856
|
spec;
|
|
610
857
|
heading;
|
|
611
858
|
description;
|
|
@@ -619,11 +866,11 @@ var NativeModalView = class {
|
|
|
619
866
|
select;
|
|
620
867
|
document;
|
|
621
868
|
blocks;
|
|
622
|
-
|
|
623
|
-
progressValue;
|
|
869
|
+
progress;
|
|
624
870
|
filtered = [];
|
|
625
871
|
filter = "";
|
|
626
872
|
changing = false;
|
|
873
|
+
actionsSignature = "";
|
|
627
874
|
addText(id) {
|
|
628
875
|
const text = new TextRenderable2(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
|
|
629
876
|
this.panel.add(text);
|
|
@@ -636,6 +883,7 @@ var NativeModalView = class {
|
|
|
636
883
|
width: "100%",
|
|
637
884
|
value: this.spec.filter ?? "",
|
|
638
885
|
placeholder: "Filter…",
|
|
886
|
+
placeholderColor: colors.foreground,
|
|
639
887
|
textColor: colors.foreground,
|
|
640
888
|
backgroundColor: colors.background,
|
|
641
889
|
focusedTextColor: colors.foreground,
|
|
@@ -682,8 +930,8 @@ var NativeModalView = class {
|
|
|
682
930
|
focusedTextColor: colors.foreground,
|
|
683
931
|
focusedBackgroundColor: colors.background,
|
|
684
932
|
placeholder: this.spec.placeholder ?? "",
|
|
685
|
-
|
|
686
|
-
cursorColor: colors.
|
|
933
|
+
placeholderColor: colors.foreground,
|
|
934
|
+
cursorColor: colors.foreground,
|
|
687
935
|
onContentChange: () => {
|
|
688
936
|
if (!this.editor || this.changing) return;
|
|
689
937
|
this.refreshMask();
|
|
@@ -713,14 +961,14 @@ var NativeModalView = class {
|
|
|
713
961
|
const spec = this.spec;
|
|
714
962
|
this.changing = true;
|
|
715
963
|
try {
|
|
716
|
-
this.heading.content =
|
|
964
|
+
this.heading.content = plainText(spec.title);
|
|
717
965
|
this.description.content = styledText(spec.description);
|
|
718
966
|
this.description.visible = plainText(spec.description).length > 0;
|
|
719
967
|
this.breadcrumb.content = (spec.breadcrumbs ?? []).join(" › ");
|
|
720
968
|
this.breadcrumb.visible = (spec.breadcrumbs?.length ?? 0) > 0;
|
|
721
969
|
this.error.content = spec.validationError ?? "";
|
|
722
970
|
this.error.visible = Boolean(spec.validationError);
|
|
723
|
-
this.hints.content = spec.hints ??
|
|
971
|
+
this.hints.content = spec.hints ?? `${spec.deferSelection || spec.captureKeys ? "" : "Tab controls · "}${spec.kind === "select" ? "↑ ↓ select · Enter confirm · Esc return" : spec.multiline ? "Ctrl+Enter confirm · Esc return" : "Enter confirm · Esc return"}`;
|
|
724
972
|
if (this.filterInput && patch.filter !== void 0 && patch.filter !== old.filter) {
|
|
725
973
|
this.filter = patch.filter;
|
|
726
974
|
this.filterInput.setText(patch.filter);
|
|
@@ -729,29 +977,36 @@ var NativeModalView = class {
|
|
|
729
977
|
this.refreshMask();
|
|
730
978
|
this.refreshOptions(patch.selectedIndex);
|
|
731
979
|
this.blocks?.replace(spec.blocks ?? []);
|
|
732
|
-
if (this.
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
this.
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
980
|
+
if (this.progress) this.progress.content = nativeProgressText(spec.progress, true);
|
|
981
|
+
const actionsSignature = JSON.stringify(spec.actions ?? []);
|
|
982
|
+
if (actionsSignature !== this.actionsSignature) {
|
|
983
|
+
this.actionsSignature = actionsSignature;
|
|
984
|
+
for (const child of [...this.actions.getChildren()]) child.destroy();
|
|
985
|
+
for (const action of spec.actions ?? []) {
|
|
986
|
+
const button = new TextRenderable2(this.context.renderer, {
|
|
987
|
+
id: `modal-action:${spec.id}:${action.id}`,
|
|
988
|
+
content: plainText(action.label),
|
|
989
|
+
fg: this.context.colors.foreground,
|
|
990
|
+
bg: this.context.colors.background,
|
|
991
|
+
attributes: action.disabled ? 2 : action.active ? 1 : 0,
|
|
992
|
+
selectable: false,
|
|
993
|
+
onMouseDown: (event) => {
|
|
994
|
+
if (event.button !== 0 || action.disabled) return;
|
|
995
|
+
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
996
|
+
event.stopPropagation();
|
|
997
|
+
},
|
|
998
|
+
onKeyDown: (key) => {
|
|
999
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
1000
|
+
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1001
|
+
key.preventDefault();
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
button.focusable = !action.disabled;
|
|
1005
|
+
nativeControlFocus(button, action.disabled ? 2 : action.active ? 1 : 0);
|
|
1006
|
+
this.actions.add(button);
|
|
1007
|
+
}
|
|
1008
|
+
this.actions.visible = (spec.actions?.length ?? 0) > 0;
|
|
753
1009
|
}
|
|
754
|
-
this.actions.visible = (spec.actions?.length ?? 0) > 0;
|
|
755
1010
|
} finally {
|
|
756
1011
|
this.changing = false;
|
|
757
1012
|
}
|
|
@@ -816,20 +1071,42 @@ var NativeModalView = class {
|
|
|
816
1071
|
else if (this.document) this.document.focus();
|
|
817
1072
|
else this.panel.focus();
|
|
818
1073
|
}
|
|
1074
|
+
nextControl(direction) {
|
|
1075
|
+
const controls = [];
|
|
1076
|
+
const visit = (node) => {
|
|
1077
|
+
if (!node.visible || node.isDestroyed) return;
|
|
1078
|
+
if (node !== this.panel && node.focusable) controls.push(node);
|
|
1079
|
+
for (const child of node.getChildren()) visit(child);
|
|
1080
|
+
};
|
|
1081
|
+
visit(this.panel);
|
|
1082
|
+
if (!controls.length) {
|
|
1083
|
+
this.panel.focus();
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
const focused = this.context.renderer.currentFocusedRenderable;
|
|
1087
|
+
const current = focused ? controls.indexOf(focused) : -1;
|
|
1088
|
+
const next = controls[current < 0 ? direction > 0 ? 0 : controls.length - 1 : (current + direction + controls.length) % controls.length];
|
|
1089
|
+
next.focus();
|
|
1090
|
+
this.document?.scrollChildIntoView(next.id);
|
|
1091
|
+
}
|
|
819
1092
|
handleKey(key) {
|
|
820
1093
|
const consume = () => {
|
|
821
1094
|
key.preventDefault();
|
|
822
1095
|
key.stopPropagation();
|
|
823
1096
|
return true;
|
|
824
1097
|
};
|
|
1098
|
+
if (key.eventType === "release") return false;
|
|
825
1099
|
if (key.name === "escape" || key.ctrl && key.name === "c") {
|
|
826
1100
|
this.cancel();
|
|
827
1101
|
return consume();
|
|
828
1102
|
}
|
|
829
1103
|
if (key.name === "tab") {
|
|
830
|
-
this.emitAction(key.shift ? "shift-tab" : "tab");
|
|
1104
|
+
if (this.spec.deferSelection || this.spec.captureKeys) this.emitAction(key.shift ? "shift-tab" : "tab");
|
|
1105
|
+
else this.nextControl(key.shift ? -1 : 1);
|
|
831
1106
|
return consume();
|
|
832
1107
|
}
|
|
1108
|
+
const focusedId = this.context.renderer.currentFocusedRenderable?.id ?? "";
|
|
1109
|
+
if ((key.name === "return" || key.sequence === " ") && /^(?:block-title:|block-action:|modal-action:)/.test(focusedId)) return false;
|
|
833
1110
|
if (this.spec.kind === "select") {
|
|
834
1111
|
if (key.name === "up" || key.name === "down" || key.name === "pageup" || key.name === "pagedown") {
|
|
835
1112
|
const amount = key.name.startsWith("page") ? Math.max(1, Math.floor((this.select?.height ?? 5) / (this.select?.showDescription ? 2 : 1))) : 1;
|
|
@@ -910,14 +1187,23 @@ var NativeModalView = class {
|
|
|
910
1187
|
}
|
|
911
1188
|
applyTheme() {
|
|
912
1189
|
const colors = this.context.colors;
|
|
913
|
-
this.root.backgroundColor = colors.background;
|
|
914
1190
|
this.panel.backgroundColor = colors.background;
|
|
915
1191
|
this.panel.borderColor = colors.border;
|
|
916
|
-
this.
|
|
1192
|
+
this.frame.setColors(colors);
|
|
1193
|
+
this.heading.fg = colors.foreground;
|
|
1194
|
+
this.heading.attributes = 1;
|
|
917
1195
|
this.description.fg = colors.foreground;
|
|
918
1196
|
this.breadcrumb.fg = colors.dim;
|
|
919
|
-
this.
|
|
1197
|
+
this.breadcrumb.attributes = 2;
|
|
1198
|
+
this.error.fg = colors.foreground;
|
|
1199
|
+
this.error.attributes = 1;
|
|
920
1200
|
this.hints.fg = colors.dim;
|
|
1201
|
+
this.hints.attributes = 2;
|
|
1202
|
+
if (this.progress) {
|
|
1203
|
+
this.progress.fg = colors.foreground;
|
|
1204
|
+
this.progress.bg = colors.background;
|
|
1205
|
+
}
|
|
1206
|
+
if (this.document) styleNativeScrollbars(this.document, colors);
|
|
921
1207
|
if (this.select) {
|
|
922
1208
|
this.select.backgroundColor = this.select.focusedBackgroundColor = colors.background;
|
|
923
1209
|
this.select.textColor = this.select.focusedTextColor = colors.foreground;
|
|
@@ -929,13 +1215,20 @@ var NativeModalView = class {
|
|
|
929
1215
|
for (const editor of [this.editor, this.filterInput]) if (editor) {
|
|
930
1216
|
editor.backgroundColor = editor.focusedBackgroundColor = colors.background;
|
|
931
1217
|
editor.textColor = editor.focusedTextColor = colors.foreground;
|
|
932
|
-
editor.selectionBg =
|
|
933
|
-
editor.
|
|
1218
|
+
editor.selectionBg = void 0;
|
|
1219
|
+
editor.selectionFg = void 0;
|
|
1220
|
+
editor.cursorColor = colors.foreground;
|
|
1221
|
+
editor.placeholderColor = colors.foreground;
|
|
934
1222
|
}
|
|
935
1223
|
if (this.secretMask) this.secretMask.fg = colors.foreground;
|
|
1224
|
+
for (const action of this.actions.getChildren()) if (action instanceof TextRenderable2) {
|
|
1225
|
+
action.fg = colors.foreground;
|
|
1226
|
+
action.bg = colors.background;
|
|
1227
|
+
}
|
|
936
1228
|
this.blocks?.applyTheme();
|
|
937
1229
|
}
|
|
938
1230
|
destroy() {
|
|
1231
|
+
this.frame.destroy();
|
|
939
1232
|
this.blocks?.destroy();
|
|
940
1233
|
if (this.spec.secret) this.editor?.destroy();
|
|
941
1234
|
this.root.destroyRecursively();
|
|
@@ -945,11 +1238,23 @@ var NativeModalStack = class {
|
|
|
945
1238
|
constructor(context, restoreFocus) {
|
|
946
1239
|
this.context = context;
|
|
947
1240
|
this.restoreFocus = restoreFocus;
|
|
1241
|
+
context.renderer.addPostProcessFn(this.dimUnderlay);
|
|
948
1242
|
}
|
|
949
1243
|
context;
|
|
950
1244
|
restoreFocus;
|
|
951
1245
|
views = [];
|
|
952
1246
|
byId = /* @__PURE__ */ new Map();
|
|
1247
|
+
dimUnderlay = (buffer) => {
|
|
1248
|
+
const panel = this.active?.panel;
|
|
1249
|
+
if (!panel || panel.isDestroyed || !panel.visible) return;
|
|
1250
|
+
const { x, y, width, height } = panel;
|
|
1251
|
+
buffer.withBuffers((cells) => {
|
|
1252
|
+
for (let row = 0; row < cells.height; row++) for (let column = 0; column < cells.width; column++) {
|
|
1253
|
+
if (column < x || column >= x + width || row < y || row >= y + height)
|
|
1254
|
+
cells.attributes[row * cells.width + column] |= TextAttributes4.DIM;
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
};
|
|
953
1258
|
get active() {
|
|
954
1259
|
return this.views.at(-1);
|
|
955
1260
|
}
|
|
@@ -989,23 +1294,583 @@ var NativeModalStack = class {
|
|
|
989
1294
|
for (const view of this.views) view.applyTheme();
|
|
990
1295
|
}
|
|
991
1296
|
destroy() {
|
|
1297
|
+
this.context.renderer.removePostProcessFn(this.dimUnderlay);
|
|
992
1298
|
for (const view of this.views) view.destroy();
|
|
993
1299
|
this.views = [];
|
|
994
1300
|
this.byId.clear();
|
|
995
1301
|
}
|
|
996
1302
|
};
|
|
997
1303
|
|
|
1304
|
+
// packages/cli/src/tui/native/components/instruments.ts
|
|
1305
|
+
import {
|
|
1306
|
+
BoxRenderable as BoxRenderable3,
|
|
1307
|
+
RGBA as RGBA2,
|
|
1308
|
+
RenderableEvents as RenderableEvents3,
|
|
1309
|
+
ScrollBoxRenderable as ScrollBoxRenderable3,
|
|
1310
|
+
TextAttributes as TextAttributes5,
|
|
1311
|
+
TextRenderable as TextRenderable3
|
|
1312
|
+
} from "@opentui/core";
|
|
1313
|
+
|
|
1314
|
+
// packages/cli/src/tui/terminal-cells.ts
|
|
1315
|
+
var graphemes = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1316
|
+
var extendedPictographic = /\p{Extended_Pictographic}/u;
|
|
1317
|
+
var emojiPresentation = /\p{Emoji_Presentation}/u;
|
|
1318
|
+
var zeroWidthGrapheme = /^[\p{Mark}\p{Cf}\u200c\u200d\ufe0e\ufe0f]+$/u;
|
|
1319
|
+
function escapeEnd(value, start) {
|
|
1320
|
+
if (value[start] !== "\x1B") return start + 1;
|
|
1321
|
+
const kind = value[start + 1];
|
|
1322
|
+
if (kind === "[") {
|
|
1323
|
+
for (let i = start + 2; i < value.length; i++) {
|
|
1324
|
+
const code = value.charCodeAt(i);
|
|
1325
|
+
if (code >= 64 && code <= 126) return i + 1;
|
|
1326
|
+
}
|
|
1327
|
+
return value.length;
|
|
1328
|
+
}
|
|
1329
|
+
if (kind === "]") {
|
|
1330
|
+
for (let i = start + 2; i < value.length; i++) {
|
|
1331
|
+
if (value[i] === "\x07") return i + 1;
|
|
1332
|
+
if (value[i] === "\x1B" && value[i + 1] === "\\") return i + 2;
|
|
1333
|
+
}
|
|
1334
|
+
return value.length;
|
|
1335
|
+
}
|
|
1336
|
+
if (kind === "P" || kind === "X" || kind === "^" || kind === "_") {
|
|
1337
|
+
for (let i = start + 2; i < value.length; i++) {
|
|
1338
|
+
if (value[i] === "\x1B" && value[i + 1] === "\\") return i + 2;
|
|
1339
|
+
}
|
|
1340
|
+
return value.length;
|
|
1341
|
+
}
|
|
1342
|
+
return Math.min(value.length, start + 2);
|
|
1343
|
+
}
|
|
1344
|
+
function stripTerminalSequences(value) {
|
|
1345
|
+
let out = "";
|
|
1346
|
+
for (let i = 0; i < value.length; ) {
|
|
1347
|
+
if (value[i] === "\x1B") {
|
|
1348
|
+
i = escapeEnd(value, i);
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
const code = value.charCodeAt(i);
|
|
1352
|
+
if (code >= 0 && code < 32 || code === 127) {
|
|
1353
|
+
i++;
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
const cp = value.codePointAt(i);
|
|
1357
|
+
out += String.fromCodePoint(cp);
|
|
1358
|
+
i += cp > 65535 ? 2 : 1;
|
|
1359
|
+
}
|
|
1360
|
+
return out;
|
|
1361
|
+
}
|
|
1362
|
+
function normalizeTerminalLine(value) {
|
|
1363
|
+
return value.replace(/\r\n|\r|\n/g, " ").replace(/\t/g, " ");
|
|
1364
|
+
}
|
|
1365
|
+
function isWideCodePoint(cp) {
|
|
1366
|
+
return cp >= 4352 && cp <= 4447 || cp === 9001 || cp === 9002 || cp >= 11904 && cp <= 12350 || cp >= 12352 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65049 || cp >= 65072 && cp <= 65135 || cp >= 65280 && cp <= 65376 || cp >= 65504 && cp <= 65510 || cp >= 110592 && cp <= 111359 || cp >= 131072 && cp <= 262141;
|
|
1367
|
+
}
|
|
1368
|
+
function graphemeCellWidth(value) {
|
|
1369
|
+
if (!value || zeroWidthGrapheme.test(value)) return 0;
|
|
1370
|
+
const cp = value.codePointAt(0);
|
|
1371
|
+
if (cp < 32 || cp >= 127 && cp < 160) return 0;
|
|
1372
|
+
if (emojiPresentation.test(value) || value.includes("️") || value.includes("") && extendedPictographic.test(value) || cp >= 127462 && cp <= 127487) {
|
|
1373
|
+
return 2;
|
|
1374
|
+
}
|
|
1375
|
+
return isWideCodePoint(cp) ? 2 : 1;
|
|
1376
|
+
}
|
|
1377
|
+
function plainGraphemes(value) {
|
|
1378
|
+
return Array.from(
|
|
1379
|
+
graphemes.segment(stripTerminalSequences(value)),
|
|
1380
|
+
(part) => part.segment
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
function terminalCellWidth(value) {
|
|
1384
|
+
let width = 0;
|
|
1385
|
+
for (const grapheme of plainGraphemes(normalizeTerminalLine(value))) {
|
|
1386
|
+
width += graphemeCellWidth(grapheme);
|
|
1387
|
+
}
|
|
1388
|
+
return width;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// packages/cli/src/tui/native/components/instrument-format.ts
|
|
1392
|
+
var graphemes2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1393
|
+
function fitInstrumentText(value, cells, marker = "~") {
|
|
1394
|
+
const text = stripTerminalSequences(normalizeTerminalLine(value));
|
|
1395
|
+
const width = Math.max(0, Math.floor(cells));
|
|
1396
|
+
if (terminalCellWidth(text) <= width) return text;
|
|
1397
|
+
const suffix = width > 0 ? marker : "";
|
|
1398
|
+
const budget = Math.max(0, width - terminalCellWidth(suffix));
|
|
1399
|
+
let used = 0, result = "";
|
|
1400
|
+
for (const { segment } of graphemes2.segment(text)) {
|
|
1401
|
+
const size = graphemeCellWidth(segment);
|
|
1402
|
+
if (used + size > budget) break;
|
|
1403
|
+
used += size;
|
|
1404
|
+
result += segment;
|
|
1405
|
+
}
|
|
1406
|
+
return result + suffix;
|
|
1407
|
+
}
|
|
1408
|
+
function formatTelemetryRow(label, value, width, reserved = 12) {
|
|
1409
|
+
const cells = Math.max(0, Math.floor(width));
|
|
1410
|
+
if (!cells) return "";
|
|
1411
|
+
const reading = stripTerminalSequences(normalizeTerminalLine(value));
|
|
1412
|
+
const readingCells = terminalCellWidth(reading);
|
|
1413
|
+
if (readingCells >= cells) return fitInstrumentText(reading, cells);
|
|
1414
|
+
const field = Math.min(cells, Math.max(readingCells, Math.floor(reserved)));
|
|
1415
|
+
const labelCells = Math.max(0, cells - field - 1);
|
|
1416
|
+
const left = fitInstrumentText(label.toUpperCase(), labelCells);
|
|
1417
|
+
const dots = ".".repeat(Math.max(0, cells - field - terminalCellWidth(left)));
|
|
1418
|
+
return left + dots + " ".repeat(Math.max(0, field - readingCells)) + reading;
|
|
1419
|
+
}
|
|
1420
|
+
function instrumentNumber(value, precision = 0, unit = "") {
|
|
1421
|
+
return value === void 0 || !Number.isFinite(value) || value < 0 ? "--" : `${value.toFixed(Math.max(0, Math.min(6, precision)))}${unit}`;
|
|
1422
|
+
}
|
|
1423
|
+
function formatSignalMeter(value, maximum, width = 8) {
|
|
1424
|
+
const cells = Math.max(1, Math.floor(width));
|
|
1425
|
+
if (value === void 0 || maximum === void 0 || !Number.isFinite(value) || !Number.isFinite(maximum) || value < 0 || maximum <= 0)
|
|
1426
|
+
return `[${".".repeat(cells)}] --`;
|
|
1427
|
+
const ratio = Math.max(0, Math.min(1, value / maximum));
|
|
1428
|
+
const filled = Math.round(ratio * cells);
|
|
1429
|
+
return `[${"●".repeat(filled)}${"○".repeat(cells - filled)}] ${Math.round(ratio * 100)}%`;
|
|
1430
|
+
}
|
|
1431
|
+
function formatSectionBanner(title, width, expanded = true) {
|
|
1432
|
+
const cells = Math.max(0, Math.floor(width));
|
|
1433
|
+
const label = fitInstrumentText(
|
|
1434
|
+
`${expanded ? "-" : "+"} ${title.toUpperCase()} `,
|
|
1435
|
+
cells
|
|
1436
|
+
);
|
|
1437
|
+
return label + "─".repeat(Math.max(0, cells - terminalCellWidth(label)));
|
|
1438
|
+
}
|
|
1439
|
+
function instrumentStatus(value) {
|
|
1440
|
+
const status = (value || "unknown").toLowerCase();
|
|
1441
|
+
if (status === "running") return "ACTIVE";
|
|
1442
|
+
if (status === "completed" || status === "succeeded") return "DONE";
|
|
1443
|
+
if (status === "failed" || status === "error") return "ERR";
|
|
1444
|
+
if (status === "waiting") return "STDBY";
|
|
1445
|
+
if (status === "paused") return "PAUSED";
|
|
1446
|
+
if (status === "stopped") return "STOPPED";
|
|
1447
|
+
return status.toUpperCase();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// packages/cli/src/tui/native/components/instruments.ts
|
|
1451
|
+
var foreground = RGBA2.defaultForeground();
|
|
1452
|
+
var background = RGBA2.defaultBackground();
|
|
1453
|
+
var SectionBanner = class extends TextRenderable3 {
|
|
1454
|
+
constructor(renderer2, title, id, activate) {
|
|
1455
|
+
super(renderer2, {
|
|
1456
|
+
id,
|
|
1457
|
+
content: `- ${title.toUpperCase()}`,
|
|
1458
|
+
width: "100%",
|
|
1459
|
+
height: 1,
|
|
1460
|
+
flexShrink: 0,
|
|
1461
|
+
fg: foreground,
|
|
1462
|
+
bg: background,
|
|
1463
|
+
attributes: TextAttributes5.BOLD,
|
|
1464
|
+
selectable: false,
|
|
1465
|
+
wrapMode: "none",
|
|
1466
|
+
truncate: true,
|
|
1467
|
+
onMouseDown: (event) => {
|
|
1468
|
+
if (event.button === 0) {
|
|
1469
|
+
this.focus();
|
|
1470
|
+
this.activate();
|
|
1471
|
+
event.stopPropagation();
|
|
1472
|
+
}
|
|
1473
|
+
},
|
|
1474
|
+
onKeyDown: (key) => {
|
|
1475
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
1476
|
+
this.activate();
|
|
1477
|
+
key.preventDefault();
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
this.activate = activate;
|
|
1482
|
+
this.heading = title;
|
|
1483
|
+
this.focusable = true;
|
|
1484
|
+
this.on(RenderableEvents3.FOCUSED, () => {
|
|
1485
|
+
this.attributes = TextAttributes5.BOLD | TextAttributes5.INVERSE;
|
|
1486
|
+
});
|
|
1487
|
+
this.on(RenderableEvents3.BLURRED, () => {
|
|
1488
|
+
this.attributes = TextAttributes5.BOLD;
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
activate;
|
|
1492
|
+
expanded = true;
|
|
1493
|
+
heading;
|
|
1494
|
+
onResize(width, height) {
|
|
1495
|
+
super.onResize(width, height);
|
|
1496
|
+
this.refresh();
|
|
1497
|
+
}
|
|
1498
|
+
update(title, expanded = this.expanded) {
|
|
1499
|
+
this.heading = title;
|
|
1500
|
+
this.expanded = expanded;
|
|
1501
|
+
this.refresh();
|
|
1502
|
+
}
|
|
1503
|
+
refresh() {
|
|
1504
|
+
const next = formatSectionBanner(this.heading, this.width, this.expanded);
|
|
1505
|
+
if (this.plainText !== next) this.content = next;
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
var TelemetryRow = class extends TextRenderable3 {
|
|
1509
|
+
constructor(renderer2, label, id, reserved = 12) {
|
|
1510
|
+
super(renderer2, {
|
|
1511
|
+
id,
|
|
1512
|
+
content: "",
|
|
1513
|
+
width: "100%",
|
|
1514
|
+
height: 1,
|
|
1515
|
+
flexShrink: 0,
|
|
1516
|
+
fg: foreground,
|
|
1517
|
+
bg: background,
|
|
1518
|
+
wrapMode: "none",
|
|
1519
|
+
truncate: true
|
|
1520
|
+
});
|
|
1521
|
+
this.reserved = reserved;
|
|
1522
|
+
this.label = label;
|
|
1523
|
+
}
|
|
1524
|
+
reserved;
|
|
1525
|
+
label;
|
|
1526
|
+
value = "--";
|
|
1527
|
+
onResize(width, height) {
|
|
1528
|
+
super.onResize(width, height);
|
|
1529
|
+
this.refresh();
|
|
1530
|
+
}
|
|
1531
|
+
update(value, label = this.label) {
|
|
1532
|
+
this.label = label;
|
|
1533
|
+
this.value = value;
|
|
1534
|
+
this.refresh();
|
|
1535
|
+
}
|
|
1536
|
+
refresh() {
|
|
1537
|
+
const next = formatTelemetryRow(
|
|
1538
|
+
this.label,
|
|
1539
|
+
this.value,
|
|
1540
|
+
this.width,
|
|
1541
|
+
this.reserved
|
|
1542
|
+
);
|
|
1543
|
+
if (this.plainText !== next) this.content = next;
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
var SignalMeter = class extends TelemetryRow {
|
|
1547
|
+
constructor(renderer2, label, id) {
|
|
1548
|
+
super(renderer2, label, id, 18);
|
|
1549
|
+
}
|
|
1550
|
+
updateReading(value, maximum) {
|
|
1551
|
+
this.update(formatSignalMeter(value, maximum));
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
var ChannelMap = class extends BoxRenderable3 {
|
|
1555
|
+
constructor(renderer2, id, inspect, reveal) {
|
|
1556
|
+
super(renderer2, {
|
|
1557
|
+
id,
|
|
1558
|
+
width: "100%",
|
|
1559
|
+
flexDirection: "column",
|
|
1560
|
+
flexShrink: 0
|
|
1561
|
+
});
|
|
1562
|
+
this.inspect = inspect;
|
|
1563
|
+
this.reveal = reveal;
|
|
1564
|
+
}
|
|
1565
|
+
inspect;
|
|
1566
|
+
reveal;
|
|
1567
|
+
rows = /* @__PURE__ */ new Map();
|
|
1568
|
+
channels = /* @__PURE__ */ new Map();
|
|
1569
|
+
agents = [];
|
|
1570
|
+
onResize(width, height) {
|
|
1571
|
+
super.onResize(width, height);
|
|
1572
|
+
this.refresh();
|
|
1573
|
+
}
|
|
1574
|
+
update(agents) {
|
|
1575
|
+
this.agents = agents;
|
|
1576
|
+
const keep = new Set(agents.map((agent) => agent.id));
|
|
1577
|
+
for (const [id, row] of this.rows)
|
|
1578
|
+
if (!keep.has(id)) {
|
|
1579
|
+
row.destroyRecursively();
|
|
1580
|
+
this.rows.delete(id);
|
|
1581
|
+
}
|
|
1582
|
+
for (const agent of agents) {
|
|
1583
|
+
if (!this.channels.has(agent.id))
|
|
1584
|
+
this.channels.set(agent.id, this.channels.size + 1);
|
|
1585
|
+
if (!this.rows.has(agent.id)) {
|
|
1586
|
+
const row = new TextRenderable3(this._ctx, {
|
|
1587
|
+
id: `${this.id}:channel:${agent.id}`,
|
|
1588
|
+
content: "",
|
|
1589
|
+
width: "100%",
|
|
1590
|
+
height: 1,
|
|
1591
|
+
flexShrink: 0,
|
|
1592
|
+
fg: foreground,
|
|
1593
|
+
bg: background,
|
|
1594
|
+
wrapMode: "none",
|
|
1595
|
+
truncate: true,
|
|
1596
|
+
selectable: false,
|
|
1597
|
+
onMouseDown: (event) => {
|
|
1598
|
+
if (event.button === 0) {
|
|
1599
|
+
row.focus();
|
|
1600
|
+
this.inspect?.(agent.id);
|
|
1601
|
+
event.stopPropagation();
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
onKeyDown: (key) => {
|
|
1605
|
+
if (key.eventType === "release") return;
|
|
1606
|
+
if (key.name === "return" || key.sequence === " ") {
|
|
1607
|
+
this.inspect?.(agent.id);
|
|
1608
|
+
key.preventDefault();
|
|
1609
|
+
} else if (key.name === "down" || key.name === "up") {
|
|
1610
|
+
const targets = this.focusTargets();
|
|
1611
|
+
const index = targets.indexOf(row), delta = key.name === "down" ? 1 : -1;
|
|
1612
|
+
targets[(index + delta + targets.length) % targets.length]?.focus();
|
|
1613
|
+
key.preventDefault();
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
row.focusable = !!this.inspect;
|
|
1618
|
+
row.on(RenderableEvents3.FOCUSED, () => {
|
|
1619
|
+
row.attributes = TextAttributes5.INVERSE;
|
|
1620
|
+
this.reveal?.(row);
|
|
1621
|
+
});
|
|
1622
|
+
row.on(RenderableEvents3.BLURRED, () => {
|
|
1623
|
+
row.attributes = 0;
|
|
1624
|
+
});
|
|
1625
|
+
this.rows.set(agent.id, row);
|
|
1626
|
+
this.add(row);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
this.refresh();
|
|
1630
|
+
}
|
|
1631
|
+
focusTargets() {
|
|
1632
|
+
return this.agents.flatMap((agent) => {
|
|
1633
|
+
const row = this.rows.get(agent.id);
|
|
1634
|
+
return row?.focusable ? [row] : [];
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
refresh() {
|
|
1638
|
+
for (const agent of this.agents) {
|
|
1639
|
+
const row = this.rows.get(agent.id);
|
|
1640
|
+
if (!row) continue;
|
|
1641
|
+
const channel = String(this.channels.get(agent.id)).padStart(2, "0");
|
|
1642
|
+
const next = formatTelemetryRow(
|
|
1643
|
+
`${agent.selected ? ">" : " "}${channel} ${agent.label}`,
|
|
1644
|
+
instrumentStatus(agent.status),
|
|
1645
|
+
this.width,
|
|
1646
|
+
7
|
|
1647
|
+
);
|
|
1648
|
+
if (row.plainText !== next) row.content = next;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
var InstrumentRail = class extends BoxRenderable3 {
|
|
1653
|
+
scroll;
|
|
1654
|
+
inferenceHeader;
|
|
1655
|
+
sessionHeader;
|
|
1656
|
+
channels;
|
|
1657
|
+
inference;
|
|
1658
|
+
session;
|
|
1659
|
+
model;
|
|
1660
|
+
rows = /* @__PURE__ */ new Map();
|
|
1661
|
+
contextMeter;
|
|
1662
|
+
constructor(renderer2, options = {}) {
|
|
1663
|
+
const id = options.id ?? "omnius-instruments";
|
|
1664
|
+
super(renderer2, {
|
|
1665
|
+
id,
|
|
1666
|
+
width: "100%",
|
|
1667
|
+
height: "100%",
|
|
1668
|
+
minWidth: 0,
|
|
1669
|
+
minHeight: 1,
|
|
1670
|
+
flexDirection: "column",
|
|
1671
|
+
flexGrow: 1,
|
|
1672
|
+
flexShrink: 1
|
|
1673
|
+
});
|
|
1674
|
+
this.scroll = new ScrollBoxRenderable3(renderer2, {
|
|
1675
|
+
id: `${id}:scroll`,
|
|
1676
|
+
width: "100%",
|
|
1677
|
+
flexGrow: 1,
|
|
1678
|
+
minHeight: 1,
|
|
1679
|
+
minWidth: 0,
|
|
1680
|
+
scrollY: true,
|
|
1681
|
+
scrollX: false,
|
|
1682
|
+
viewportCulling: true,
|
|
1683
|
+
verticalScrollbarOptions: { showArrows: false },
|
|
1684
|
+
horizontalScrollbarOptions: { visible: false },
|
|
1685
|
+
contentOptions: { flexDirection: "column", paddingRight: 1 }
|
|
1686
|
+
});
|
|
1687
|
+
this.add(this.scroll);
|
|
1688
|
+
this.inferenceHeader = new SectionBanner(
|
|
1689
|
+
renderer2,
|
|
1690
|
+
"INFERENCE",
|
|
1691
|
+
`${id}:inference`,
|
|
1692
|
+
() => this.toggleSection("inference")
|
|
1693
|
+
);
|
|
1694
|
+
this.inferenceHeader.on(
|
|
1695
|
+
RenderableEvents3.FOCUSED,
|
|
1696
|
+
() => this.reveal(this.inferenceHeader)
|
|
1697
|
+
);
|
|
1698
|
+
this.scroll.add(this.inferenceHeader);
|
|
1699
|
+
this.inference = new BoxRenderable3(renderer2, {
|
|
1700
|
+
id: `${id}:inference-body`,
|
|
1701
|
+
width: "100%",
|
|
1702
|
+
flexDirection: "column",
|
|
1703
|
+
flexShrink: 0
|
|
1704
|
+
});
|
|
1705
|
+
this.scroll.add(this.inference);
|
|
1706
|
+
const addRow = (parent, key, label, reserved = 12) => {
|
|
1707
|
+
const row = new TelemetryRow(renderer2, label, `${id}:${key}`, reserved);
|
|
1708
|
+
this.rows.set(key, row);
|
|
1709
|
+
parent.add(row);
|
|
1710
|
+
return row;
|
|
1711
|
+
};
|
|
1712
|
+
addRow(this.inference, "route", "ROUTE");
|
|
1713
|
+
addRow(this.inference, "backend", "BACKEND");
|
|
1714
|
+
this.model = new TextRenderable3(renderer2, {
|
|
1715
|
+
id: `${id}:model`,
|
|
1716
|
+
content: "MODEL --",
|
|
1717
|
+
width: "100%",
|
|
1718
|
+
height: 1,
|
|
1719
|
+
fg: foreground,
|
|
1720
|
+
bg: background,
|
|
1721
|
+
wrapMode: "none",
|
|
1722
|
+
truncate: true,
|
|
1723
|
+
flexShrink: 0
|
|
1724
|
+
});
|
|
1725
|
+
this.inference.add(this.model);
|
|
1726
|
+
addRow(this.inference, "input", "IN TOK");
|
|
1727
|
+
addRow(this.inference, "output", "OUT TOK");
|
|
1728
|
+
addRow(this.inference, "speed", "RATE");
|
|
1729
|
+
addRow(this.inference, "context", "CTX TOK", 20);
|
|
1730
|
+
this.contextMeter = new SignalMeter(
|
|
1731
|
+
renderer2,
|
|
1732
|
+
"CTX LOAD",
|
|
1733
|
+
`${id}:context-load`
|
|
1734
|
+
);
|
|
1735
|
+
this.inference.add(this.contextMeter);
|
|
1736
|
+
addRow(this.inference, "state", "STATE");
|
|
1737
|
+
this.sessionHeader = new SectionBanner(
|
|
1738
|
+
renderer2,
|
|
1739
|
+
"SESSION / AGENTS",
|
|
1740
|
+
`${id}:session`,
|
|
1741
|
+
() => this.toggleSection("session")
|
|
1742
|
+
);
|
|
1743
|
+
this.sessionHeader.on(
|
|
1744
|
+
RenderableEvents3.FOCUSED,
|
|
1745
|
+
() => this.reveal(this.sessionHeader)
|
|
1746
|
+
);
|
|
1747
|
+
this.scroll.add(this.sessionHeader);
|
|
1748
|
+
this.session = new BoxRenderable3(renderer2, {
|
|
1749
|
+
id: `${id}:session-body`,
|
|
1750
|
+
width: "100%",
|
|
1751
|
+
flexDirection: "column",
|
|
1752
|
+
flexShrink: 0
|
|
1753
|
+
});
|
|
1754
|
+
this.scroll.add(this.session);
|
|
1755
|
+
addRow(this.session, "selected", "VIEW");
|
|
1756
|
+
addRow(this.session, "elapsed", "ELAPSED");
|
|
1757
|
+
addRow(this.session, "turns", "TURNS");
|
|
1758
|
+
addRow(this.session, "tools", "TOOLS");
|
|
1759
|
+
addRow(this.session, "toolResults", "TOOL OK/ERR");
|
|
1760
|
+
addRow(this.session, "queued", "QUEUED");
|
|
1761
|
+
this.channels = new ChannelMap(
|
|
1762
|
+
renderer2,
|
|
1763
|
+
`${id}:channels`,
|
|
1764
|
+
options.onInspectAgent,
|
|
1765
|
+
(row) => this.reveal(row)
|
|
1766
|
+
);
|
|
1767
|
+
this.session.add(this.channels);
|
|
1768
|
+
this.session.visible = false;
|
|
1769
|
+
this.sessionHeader.update("SESSION / AGENTS", false);
|
|
1770
|
+
if (options.colors) this.applyTheme(options.colors);
|
|
1771
|
+
}
|
|
1772
|
+
update(state) {
|
|
1773
|
+
const data = state.instruments;
|
|
1774
|
+
const inference = data?.inference;
|
|
1775
|
+
const session = data?.session;
|
|
1776
|
+
const set = (id, value) => this.rows.get(id).update(value);
|
|
1777
|
+
set("route", (inference?.route || "UNKNOWN").toUpperCase());
|
|
1778
|
+
set("backend", (inference?.backend || "--").toUpperCase());
|
|
1779
|
+
const model = `MODEL ${inference?.model || "--"}`;
|
|
1780
|
+
if (this.model.plainText !== model) this.model.content = model;
|
|
1781
|
+
set("input", instrumentNumber(inference?.inputTokens));
|
|
1782
|
+
set(
|
|
1783
|
+
"output",
|
|
1784
|
+
`${inference?.outputEstimated ? "~" : ""}${instrumentNumber(inference?.outputTokens)}`
|
|
1785
|
+
);
|
|
1786
|
+
set("speed", instrumentNumber(inference?.tokensPerSecond, 1, " TOK/S"));
|
|
1787
|
+
const used = instrumentNumber(inference?.contextUsedTokens), limit = instrumentNumber(inference?.contextLimitTokens);
|
|
1788
|
+
set("context", `${inference?.contextEstimated ? "~" : ""}${used}/${limit}`);
|
|
1789
|
+
this.contextMeter.updateReading(
|
|
1790
|
+
inference?.contextUsedTokens,
|
|
1791
|
+
inference?.contextLimitTokens
|
|
1792
|
+
);
|
|
1793
|
+
set(
|
|
1794
|
+
"state",
|
|
1795
|
+
inference?.streaming ? "STREAMING" : instrumentStatus(session?.status)
|
|
1796
|
+
);
|
|
1797
|
+
set(
|
|
1798
|
+
"selected",
|
|
1799
|
+
session?.selectedLabel || state.tabs.find((tab) => tab.id === state.activeViewId)?.label || state.activeViewId
|
|
1800
|
+
);
|
|
1801
|
+
set("elapsed", session?.elapsed ?? "--");
|
|
1802
|
+
set("turns", instrumentNumber(session?.turns));
|
|
1803
|
+
set("tools", instrumentNumber(session?.tools));
|
|
1804
|
+
set(
|
|
1805
|
+
"toolResults",
|
|
1806
|
+
`${instrumentNumber(session?.successfulTools)}/${instrumentNumber(session?.failedTools)}`
|
|
1807
|
+
);
|
|
1808
|
+
set("queued", instrumentNumber(session?.queued ?? state.queued.length));
|
|
1809
|
+
this.channels.update(
|
|
1810
|
+
session?.agents ?? state.tabs.map((tab) => ({
|
|
1811
|
+
id: tab.id,
|
|
1812
|
+
label: tab.label,
|
|
1813
|
+
status: "unknown",
|
|
1814
|
+
selected: tab.id === state.activeViewId
|
|
1815
|
+
}))
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1818
|
+
applyTheme(colors) {
|
|
1819
|
+
this.backgroundColor = colors.background;
|
|
1820
|
+
const visit = (node) => {
|
|
1821
|
+
if (node instanceof TextRenderable3) {
|
|
1822
|
+
node.fg = colors.foreground;
|
|
1823
|
+
node.bg = colors.background;
|
|
1824
|
+
}
|
|
1825
|
+
for (const child of node.getChildren()) visit(child);
|
|
1826
|
+
};
|
|
1827
|
+
visit(this);
|
|
1828
|
+
styleNativeScrollbars(this.scroll, colors);
|
|
1829
|
+
}
|
|
1830
|
+
toggleSection(id) {
|
|
1831
|
+
const header = id === "inference" ? this.inferenceHeader : this.sessionHeader;
|
|
1832
|
+
const body = id === "inference" ? this.inference : this.session;
|
|
1833
|
+
body.visible = !body.visible;
|
|
1834
|
+
header.update(
|
|
1835
|
+
id === "inference" ? "INFERENCE" : "SESSION / AGENTS",
|
|
1836
|
+
body.visible
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
focusTargets() {
|
|
1840
|
+
if (!this.visible) return [];
|
|
1841
|
+
return [
|
|
1842
|
+
this.inferenceHeader,
|
|
1843
|
+
this.sessionHeader,
|
|
1844
|
+
...this.session.visible ? this.channels.focusTargets() : []
|
|
1845
|
+
];
|
|
1846
|
+
}
|
|
1847
|
+
focusSection(index = 0) {
|
|
1848
|
+
const target = [this.inferenceHeader, this.sessionHeader][index];
|
|
1849
|
+
if (!target || !this.visible) return false;
|
|
1850
|
+
target.focus();
|
|
1851
|
+
return true;
|
|
1852
|
+
}
|
|
1853
|
+
reveal(target) {
|
|
1854
|
+
const viewport = this.scroll.viewport;
|
|
1855
|
+
if (target.y < viewport.y) this.scroll.scrollBy(target.y - viewport.y);
|
|
1856
|
+
else if (target.y + target.height > viewport.y + viewport.height)
|
|
1857
|
+
this.scroll.scrollBy(
|
|
1858
|
+
target.y + target.height - viewport.y - viewport.height
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
|
|
998
1863
|
// packages/cli/src/tui/native/components/frontend.ts
|
|
999
|
-
var initialTheme = { mode: "
|
|
1000
|
-
var spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
1864
|
+
var initialTheme = { mode: "system", bg: -1, accent: -1, textPrimary: -1, textDim: -1, boxColor: -1 };
|
|
1001
1865
|
var NativeFrontend = class {
|
|
1002
1866
|
constructor(renderer2, emit) {
|
|
1003
1867
|
this.renderer = renderer2;
|
|
1004
1868
|
this.emit = emit;
|
|
1005
1869
|
const colors = palette(initialTheme);
|
|
1006
1870
|
this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map() };
|
|
1871
|
+
this.disposeSelectionContrast = installDefaultSelectionContrast(renderer2);
|
|
1007
1872
|
this.clipboard = createClipboard({ host: createHostClipboard({ timeoutMs: 1500 }), terminal: createRendererClipboardAdapter(renderer2) });
|
|
1008
|
-
this.root = new
|
|
1873
|
+
this.root = new BoxRenderable4(renderer2, {
|
|
1009
1874
|
id: "omnius-native",
|
|
1010
1875
|
width: "100%",
|
|
1011
1876
|
height: "100%",
|
|
@@ -1013,56 +1878,101 @@ var NativeFrontend = class {
|
|
|
1013
1878
|
backgroundColor: colors.background
|
|
1014
1879
|
});
|
|
1015
1880
|
renderer2.root.add(this.root);
|
|
1016
|
-
this.header = new
|
|
1881
|
+
this.header = new BoxRenderable4(renderer2, {
|
|
1017
1882
|
id: "omnius-header",
|
|
1018
1883
|
width: "100%",
|
|
1884
|
+
minWidth: 0,
|
|
1019
1885
|
flexDirection: "column",
|
|
1020
1886
|
flexShrink: 0,
|
|
1021
|
-
border:
|
|
1022
|
-
borderStyle: "
|
|
1023
|
-
borderColor: colors.border
|
|
1024
|
-
paddingX: 1
|
|
1887
|
+
border: ["bottom"],
|
|
1888
|
+
borderStyle: "single",
|
|
1889
|
+
borderColor: colors.border
|
|
1025
1890
|
});
|
|
1026
1891
|
this.root.add(this.header);
|
|
1027
|
-
const headerRow = new
|
|
1892
|
+
const headerRow = new BoxRenderable4(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
|
|
1028
1893
|
this.header.add(headerRow);
|
|
1029
|
-
this.identity = new
|
|
1894
|
+
this.identity = new TextRenderable4(renderer2, {
|
|
1030
1895
|
id: "omnius-identity",
|
|
1031
1896
|
content: "Omnius",
|
|
1032
1897
|
fg: colors.foreground,
|
|
1033
|
-
flexShrink:
|
|
1034
|
-
flexGrow:
|
|
1898
|
+
flexShrink: 0,
|
|
1899
|
+
flexGrow: 0,
|
|
1035
1900
|
wrapMode: "none",
|
|
1036
1901
|
truncate: true,
|
|
1037
|
-
minWidth:
|
|
1038
|
-
selectable: false
|
|
1902
|
+
minWidth: 18,
|
|
1903
|
+
selectable: false,
|
|
1904
|
+
attributes: TextAttributes6.BOLD,
|
|
1905
|
+
onMouseDown: (event) => {
|
|
1906
|
+
if (event.button === 0) {
|
|
1907
|
+
this.toggleHeaderPanel();
|
|
1908
|
+
event.stopPropagation();
|
|
1909
|
+
}
|
|
1910
|
+
},
|
|
1911
|
+
onKeyDown: (key) => {
|
|
1912
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
1913
|
+
this.toggleHeaderPanel();
|
|
1914
|
+
key.preventDefault();
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1039
1917
|
});
|
|
1040
1918
|
headerRow.add(this.identity);
|
|
1041
|
-
this.headerActions = new
|
|
1919
|
+
this.headerActions = new ScrollBoxRenderable4(renderer2, {
|
|
1042
1920
|
id: "omnius-header-actions",
|
|
1043
|
-
width: "50%",
|
|
1044
1921
|
flexGrow: 0,
|
|
1045
|
-
flexShrink:
|
|
1922
|
+
flexShrink: 1,
|
|
1046
1923
|
minWidth: 0,
|
|
1047
|
-
|
|
1924
|
+
minHeight: 1,
|
|
1048
1925
|
scrollX: true,
|
|
1049
1926
|
scrollY: false,
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1927
|
+
viewportOptions: { height: 1, minHeight: 1, maxHeight: 1, flexGrow: 0, flexShrink: 0 },
|
|
1928
|
+
contentOptions: { flexDirection: "row", gap: 1, justifyContent: "flex-end" },
|
|
1929
|
+
verticalScrollbarOptions: { visible: false }
|
|
1053
1930
|
});
|
|
1054
1931
|
headerRow.add(this.headerActions);
|
|
1055
|
-
this.headerPanel = new
|
|
1932
|
+
this.headerPanel = new TextRenderable4(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
|
|
1056
1933
|
this.header.add(this.headerPanel);
|
|
1057
|
-
this.
|
|
1934
|
+
this.detailButton = new TextRenderable4(renderer2, {
|
|
1935
|
+
id: "omnius-details-toggle",
|
|
1936
|
+
content: "+ DETAILS",
|
|
1937
|
+
height: 1,
|
|
1938
|
+
width: 9,
|
|
1939
|
+
flexShrink: 0,
|
|
1940
|
+
fg: colors.foreground,
|
|
1941
|
+
attributes: TextAttributes6.DIM,
|
|
1942
|
+
selectable: false,
|
|
1943
|
+
onMouseDown: (event) => {
|
|
1944
|
+
if (event.button === 0) {
|
|
1945
|
+
this.toggleDetails();
|
|
1946
|
+
event.stopPropagation();
|
|
1947
|
+
}
|
|
1948
|
+
},
|
|
1949
|
+
onKeyDown: (key) => {
|
|
1950
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
1951
|
+
this.toggleDetails();
|
|
1952
|
+
key.preventDefault();
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
this.detailButton.focusable = true;
|
|
1957
|
+
headerRow.add(this.detailButton, 1);
|
|
1958
|
+
for (const control of [this.identity, this.detailButton]) {
|
|
1959
|
+
const attributes = control.attributes;
|
|
1960
|
+
control.on("focused", () => {
|
|
1961
|
+
control.attributes = attributes | TextAttributes6.INVERSE;
|
|
1962
|
+
});
|
|
1963
|
+
control.on("blurred", () => {
|
|
1964
|
+
control.attributes = attributes;
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
this.routeWarning = new TextRenderable4(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes6.BOLD | TextAttributes6.UNDERLINE, visible: false, flexShrink: 0 });
|
|
1058
1968
|
this.root.add(this.routeWarning);
|
|
1059
1969
|
this.tabs = new TabSelectRenderable(renderer2, {
|
|
1060
1970
|
id: "omnius-agent-tabs",
|
|
1061
1971
|
width: "100%",
|
|
1062
|
-
height:
|
|
1972
|
+
height: 2,
|
|
1063
1973
|
showDescription: false,
|
|
1064
|
-
showUnderline:
|
|
1065
|
-
showScrollArrows:
|
|
1974
|
+
showUnderline: true,
|
|
1975
|
+
showScrollArrows: false,
|
|
1066
1976
|
tabWidth: 18,
|
|
1067
1977
|
wrapSelection: true,
|
|
1068
1978
|
visible: false,
|
|
@@ -1082,16 +1992,26 @@ var NativeFrontend = class {
|
|
|
1082
1992
|
this.tabs.on("selectionChanged", () => {
|
|
1083
1993
|
if (this.changing) return;
|
|
1084
1994
|
const selected = this.tabs.getSelectedOption();
|
|
1085
|
-
if (selected) this.
|
|
1995
|
+
if (selected) this.emitAction("view", String(selected.value));
|
|
1086
1996
|
this.editor.focus();
|
|
1087
1997
|
});
|
|
1998
|
+
this.tabs.renderAfter = (buffer) => {
|
|
1999
|
+
if (this.tabs.height < 2) return;
|
|
2000
|
+
const colors2 = this.context.colors;
|
|
2001
|
+
const tabWidth = Math.min(this.tabs.getTabWidth(), this.tabs.width);
|
|
2002
|
+
const count = Math.max(1, Math.floor(this.tabs.width / Math.max(1, tabWidth)));
|
|
2003
|
+
const selected = this.tabs.getSelectedIndex();
|
|
2004
|
+
const start = Math.max(0, Math.min(selected - Math.floor(count / 2), this.tabs.options.length - count));
|
|
2005
|
+
buffer.fillRect(0, 1, this.tabs.width, 1, colors2.background);
|
|
2006
|
+
buffer.drawText("─".repeat(Math.max(0, tabWidth)), (selected - start) * tabWidth, 1, colors2.foreground, colors2.background, TextAttributes6.DIM);
|
|
2007
|
+
};
|
|
1088
2008
|
this.root.add(this.tabs);
|
|
1089
|
-
this.taskPane = new
|
|
2009
|
+
this.taskPane = new BoxRenderable4(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
|
|
1090
2010
|
this.tasks = new NativeBlockList(this.context, this.taskPane);
|
|
1091
2011
|
this.root.add(this.taskPane);
|
|
1092
|
-
this.body = new
|
|
2012
|
+
this.body = new BoxRenderable4(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
|
|
1093
2013
|
this.root.add(this.body);
|
|
1094
|
-
this.scroll = new
|
|
2014
|
+
this.scroll = new ScrollBoxRenderable4(renderer2, {
|
|
1095
2015
|
id: "omnius-conversation",
|
|
1096
2016
|
flexGrow: 1,
|
|
1097
2017
|
flexShrink: 1,
|
|
@@ -1127,18 +2047,20 @@ var NativeFrontend = class {
|
|
|
1127
2047
|
});
|
|
1128
2048
|
this.body.add(this.scroll);
|
|
1129
2049
|
this.content = new NativeBlockList(this.context, this.scroll);
|
|
1130
|
-
this.
|
|
2050
|
+
this.instruments = new InstrumentRail(renderer2, { colors, onInspectAgent: (id) => this.emitAction("view", id) });
|
|
2051
|
+
this.body.add(this.instruments);
|
|
2052
|
+
this.embeddedPane = new BoxRenderable4(renderer2, {
|
|
1131
2053
|
id: "omnius-editor-pane",
|
|
1132
2054
|
width: "50%",
|
|
1133
2055
|
flexShrink: 0,
|
|
1134
2056
|
flexDirection: "column",
|
|
1135
2057
|
border: true,
|
|
1136
|
-
borderStyle: "
|
|
2058
|
+
borderStyle: "single",
|
|
1137
2059
|
borderColor: colors.border,
|
|
1138
2060
|
visible: false
|
|
1139
2061
|
});
|
|
1140
2062
|
this.body.add(this.embeddedPane);
|
|
1141
|
-
this.liveButton = new
|
|
2063
|
+
this.liveButton = new TextRenderable4(renderer2, {
|
|
1142
2064
|
id: "omnius-live-button",
|
|
1143
2065
|
content: "↓ Live",
|
|
1144
2066
|
width: "100%",
|
|
@@ -1153,7 +2075,7 @@ var NativeFrontend = class {
|
|
|
1153
2075
|
}
|
|
1154
2076
|
},
|
|
1155
2077
|
onKeyDown: (key) => {
|
|
1156
|
-
if (key.name === "return") {
|
|
2078
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
1157
2079
|
this.scrollToLive();
|
|
1158
2080
|
key.preventDefault();
|
|
1159
2081
|
}
|
|
@@ -1161,7 +2083,7 @@ var NativeFrontend = class {
|
|
|
1161
2083
|
});
|
|
1162
2084
|
this.liveButton.focusable = true;
|
|
1163
2085
|
this.root.add(this.liveButton);
|
|
1164
|
-
this.queue = new
|
|
2086
|
+
this.queue = new TextRenderable4(renderer2, { id: "omnius-steering-queue", content: "", width: "100%", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, visible: false, flexShrink: 0 });
|
|
1165
2087
|
this.root.add(this.queue);
|
|
1166
2088
|
this.suggestions = new NativeSelectRenderable(renderer2, {
|
|
1167
2089
|
id: "omnius-suggestions",
|
|
@@ -1180,20 +2102,21 @@ var NativeFrontend = class {
|
|
|
1180
2102
|
this.suggestions.onActivate = () => this.acceptSuggestion();
|
|
1181
2103
|
this.suggestions.on("itemSelected", () => this.acceptSuggestion());
|
|
1182
2104
|
this.root.add(this.suggestions);
|
|
1183
|
-
this.
|
|
2105
|
+
this.status = new BoxRenderable4(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
|
|
2106
|
+
this.root.add(this.status);
|
|
2107
|
+
this.footer = new BoxRenderable4(renderer2, {
|
|
1184
2108
|
id: "omnius-input-box",
|
|
1185
2109
|
width: "100%",
|
|
1186
2110
|
flexShrink: 0,
|
|
1187
|
-
border:
|
|
1188
|
-
borderStyle: "
|
|
2111
|
+
border: ["top"],
|
|
2112
|
+
borderStyle: "single",
|
|
1189
2113
|
borderColor: colors.border,
|
|
1190
|
-
flexDirection: "column"
|
|
1191
|
-
paddingX: 1
|
|
2114
|
+
flexDirection: "column"
|
|
1192
2115
|
});
|
|
1193
2116
|
this.root.add(this.footer);
|
|
1194
|
-
const inputRow = new
|
|
2117
|
+
const inputRow = new BoxRenderable4(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
|
|
1195
2118
|
this.footer.add(inputRow);
|
|
1196
|
-
this.prompt = new
|
|
2119
|
+
this.prompt = new TextRenderable4(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
|
|
1197
2120
|
inputRow.add(this.prompt);
|
|
1198
2121
|
this.editor = new TextareaRenderable2(renderer2, {
|
|
1199
2122
|
id: "omnius-input",
|
|
@@ -1206,8 +2129,7 @@ var NativeFrontend = class {
|
|
|
1206
2129
|
backgroundColor: colors.background,
|
|
1207
2130
|
focusedTextColor: colors.foreground,
|
|
1208
2131
|
focusedBackgroundColor: colors.background,
|
|
1209
|
-
|
|
1210
|
-
cursorColor: colors.accent,
|
|
2132
|
+
placeholderColor: colors.foreground,
|
|
1211
2133
|
cursorStyle: { style: "block", blinking: true },
|
|
1212
2134
|
keyBindings: [{ name: "return", action: "submit" }, { name: "return", shift: true, action: "newline" }],
|
|
1213
2135
|
onContentChange: () => this.editorChanged(),
|
|
@@ -1215,31 +2137,31 @@ var NativeFrontend = class {
|
|
|
1215
2137
|
onSubmit: () => this.submit()
|
|
1216
2138
|
});
|
|
1217
2139
|
inputRow.add(this.editor);
|
|
1218
|
-
this.enhance = new
|
|
2140
|
+
this.enhance = new BoxRenderable4(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
|
|
1219
2141
|
inputRow.add(this.enhance);
|
|
1220
|
-
this.activity = new
|
|
1221
|
-
this.
|
|
1222
|
-
this.media = new
|
|
1223
|
-
this.mediaText = new
|
|
2142
|
+
this.activity = new TextRenderable4(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
|
|
2143
|
+
this.status.add(this.activity);
|
|
2144
|
+
this.media = new BoxRenderable4(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
|
|
2145
|
+
this.mediaText = new TextRenderable4(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
|
|
1224
2146
|
this.media.add(this.mediaText);
|
|
1225
|
-
this.micTrack = new
|
|
1226
|
-
this.micLevel = new BoxRenderable3(renderer2, { id: "omnius-mic-level", width: "0%", height: 1, backgroundColor: colors.accent });
|
|
1227
|
-
this.micTrack.add(this.micLevel);
|
|
2147
|
+
this.micTrack = new TextRenderable4(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
|
|
1228
2148
|
this.media.add(this.micTrack);
|
|
1229
|
-
this.
|
|
1230
|
-
this.metricsRow = new
|
|
1231
|
-
this.
|
|
2149
|
+
this.status.add(this.media);
|
|
2150
|
+
this.metricsRow = new BoxRenderable4(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
|
|
2151
|
+
this.keyHints = new TextRenderable4(renderer2, { id: "omnius-key-hints", content: "F1 HELP TAB FOCUS", height: 1, flexShrink: 0, attributes: TextAttributes6.DIM, fg: colors.foreground, selectable: false });
|
|
2152
|
+
this.metricsRow.add(this.keyHints);
|
|
2153
|
+
this.metricsStartActions = new BoxRenderable4(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
1232
2154
|
this.metricsRow.add(this.metricsStartActions);
|
|
1233
|
-
this.metrics = new
|
|
2155
|
+
this.metrics = new TextRenderable4(renderer2, { id: "omnius-metrics", content: "", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, flexShrink: 1, flexGrow: 1, minWidth: 0 });
|
|
1234
2156
|
this.metricsRow.add(this.metrics);
|
|
1235
|
-
this.metricsEndActions = new
|
|
2157
|
+
this.metricsEndActions = new BoxRenderable4(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
1236
2158
|
this.metricsRow.add(this.metricsEndActions);
|
|
1237
2159
|
this.root.add(this.metricsRow);
|
|
1238
2160
|
this.modals = new NativeModalStack(this.context, () => this.restoreFocus());
|
|
1239
2161
|
renderer2.keyInput.on("keypress", this.onKey);
|
|
1240
2162
|
renderer2.keyInput.on("paste", this.onPaste);
|
|
1241
|
-
renderer2.on(
|
|
1242
|
-
renderer2.on(
|
|
2163
|
+
renderer2.on(CliRenderEvents2.FRAME, this.onFrame);
|
|
2164
|
+
renderer2.on(CliRenderEvents2.RESIZE, this.onResize);
|
|
1243
2165
|
this.applyTheme(initialTheme);
|
|
1244
2166
|
this.editor.focus();
|
|
1245
2167
|
}
|
|
@@ -1260,6 +2182,8 @@ var NativeFrontend = class {
|
|
|
1260
2182
|
taskPane;
|
|
1261
2183
|
tasks;
|
|
1262
2184
|
body;
|
|
2185
|
+
instruments;
|
|
2186
|
+
detailButton;
|
|
1263
2187
|
embeddedPane;
|
|
1264
2188
|
liveButton;
|
|
1265
2189
|
queue;
|
|
@@ -1268,14 +2192,15 @@ var NativeFrontend = class {
|
|
|
1268
2192
|
prompt;
|
|
1269
2193
|
enhance;
|
|
1270
2194
|
metrics;
|
|
2195
|
+
keyHints;
|
|
1271
2196
|
metricsRow;
|
|
1272
2197
|
metricsStartActions;
|
|
1273
2198
|
metricsEndActions;
|
|
1274
2199
|
activity;
|
|
2200
|
+
status;
|
|
1275
2201
|
media;
|
|
1276
2202
|
mediaText;
|
|
1277
2203
|
micTrack;
|
|
1278
|
-
micLevel;
|
|
1279
2204
|
clipboard;
|
|
1280
2205
|
state;
|
|
1281
2206
|
themeSignature = "";
|
|
@@ -1291,8 +2216,10 @@ var NativeFrontend = class {
|
|
|
1291
2216
|
history = [];
|
|
1292
2217
|
historyIndex = -1;
|
|
1293
2218
|
savedInput = "";
|
|
1294
|
-
|
|
1295
|
-
|
|
2219
|
+
disposeSelectionContrast;
|
|
2220
|
+
actionSignatures = /* @__PURE__ */ new WeakMap();
|
|
2221
|
+
detailsOpen;
|
|
2222
|
+
headerPanelCollapsed = false;
|
|
1296
2223
|
inputEventPending = false;
|
|
1297
2224
|
viewportSignature = "";
|
|
1298
2225
|
newEntries = 0;
|
|
@@ -1319,20 +2246,21 @@ var NativeFrontend = class {
|
|
|
1319
2246
|
if (headerSignature !== this.headerSignature) {
|
|
1320
2247
|
this.headerSignature = headerSignature;
|
|
1321
2248
|
this.identity.content = styledText([
|
|
1322
|
-
{ text: state.header.
|
|
2249
|
+
{ text: `${plainText(state.header.panel) ? this.headerPanelCollapsed ? "▸" : "▾" : "·"} ${state.header.title}` },
|
|
1323
2250
|
...state.header.detail ? [{ text: " " }, ...textSpans(state.header.detail)] : []
|
|
1324
2251
|
]);
|
|
1325
2252
|
this.headerPanel.content = styledText(state.header.panel);
|
|
1326
|
-
this.headerPanel.visible = plainText(state.header.panel).length > 0;
|
|
2253
|
+
this.headerPanel.visible = plainText(state.header.panel).length > 0 && !this.headerPanelCollapsed;
|
|
2254
|
+
this.identity.focusable = plainText(state.header.panel).length > 0;
|
|
1327
2255
|
this.replaceActions(this.headerActions, state.header.actions, "header");
|
|
1328
2256
|
this.headerActions.visible = state.header.actions.length > 0;
|
|
1329
2257
|
}
|
|
1330
|
-
this.routeWarning.content = state.routeWarning
|
|
2258
|
+
this.routeWarning.content = state.routeWarning ? `! ${state.routeWarning}` : "";
|
|
1331
2259
|
this.routeWarning.visible = Boolean(state.routeWarning);
|
|
1332
2260
|
const tabsSignature = JSON.stringify(state.tabs);
|
|
1333
2261
|
if (tabsSignature !== this.tabsSignature) {
|
|
1334
2262
|
this.tabsSignature = tabsSignature;
|
|
1335
|
-
this.tabs.setOptions(state.tabs.map((tab) => ({ name: plainText(tab.label), description: "", value: tab.id })));
|
|
2263
|
+
this.tabs.setOptions(state.tabs.map((tab) => ({ name: fitInstrumentText(plainText(tab.label), this.tabs.getTabWidth() - 2), description: "", value: tab.id })));
|
|
1336
2264
|
}
|
|
1337
2265
|
this.tabs.visible = state.tabs.length > 1;
|
|
1338
2266
|
const activeTab = state.tabs.findIndex((tab) => tab.id === state.activeViewId);
|
|
@@ -1364,7 +2292,9 @@ var NativeFrontend = class {
|
|
|
1364
2292
|
this.editor.focusable = state.input.enabled && !this.paused;
|
|
1365
2293
|
if (state.input.enabled && !this.paused && !this.modals.active && (inputWasEnabled === false || !this.renderer.currentFocusedRenderable)) this.editor.focus();
|
|
1366
2294
|
this.metrics.content = styledText(state.metrics);
|
|
1367
|
-
this.
|
|
2295
|
+
this.metrics.visible = state.metricsVisible;
|
|
2296
|
+
this.instruments.update(state);
|
|
2297
|
+
this.layoutInstruments();
|
|
1368
2298
|
const metricsActions = JSON.stringify(state.metricsActions ?? []);
|
|
1369
2299
|
if (metricsActions !== this.metricsActionsSignature) {
|
|
1370
2300
|
this.metricsActionsSignature = metricsActions;
|
|
@@ -1374,7 +2304,6 @@ var NativeFrontend = class {
|
|
|
1374
2304
|
this.renderEnhance();
|
|
1375
2305
|
this.renderActivity();
|
|
1376
2306
|
this.renderMedia();
|
|
1377
|
-
this.updateAnimation();
|
|
1378
2307
|
} finally {
|
|
1379
2308
|
this.changing = false;
|
|
1380
2309
|
}
|
|
@@ -1382,43 +2311,46 @@ var NativeFrontend = class {
|
|
|
1382
2311
|
this.renderer.requestRender();
|
|
1383
2312
|
}
|
|
1384
2313
|
replaceActions(parent, actions, prefix) {
|
|
2314
|
+
const signature = JSON.stringify(actions);
|
|
2315
|
+
if (this.actionSignatures.get(parent) === signature) return;
|
|
2316
|
+
this.actionSignatures.set(parent, signature);
|
|
1385
2317
|
for (const child of [...parent.getChildren()]) child.destroy();
|
|
1386
2318
|
for (const action of actions) {
|
|
1387
2319
|
const colors = this.context.colors;
|
|
1388
|
-
const
|
|
1389
|
-
const
|
|
1390
|
-
const backing = prefix === "enhance" ? colors.warning : colors.accent;
|
|
1391
|
-
const [r, g, b] = backing.toInts();
|
|
1392
|
-
const foreground = filled && !inverse ? nativeColor(r * 0.299 + g * 0.587 + b * 0.114 > 140 ? 16 : 231) : colors.accent;
|
|
1393
|
-
const button = new TextRenderable3(this.renderer, {
|
|
2320
|
+
const attributes = action.disabled ? TextAttributes6.DIM : action.active ? TextAttributes6.BOLD | TextAttributes6.UNDERLINE : 0;
|
|
2321
|
+
const button = new TextRenderable4(this.renderer, {
|
|
1394
2322
|
id: `${prefix}:${action.id}`,
|
|
1395
|
-
content: styledText(
|
|
2323
|
+
content: styledText(action.label),
|
|
1396
2324
|
flexShrink: 0,
|
|
1397
2325
|
wrapMode: "none",
|
|
1398
2326
|
selectable: false,
|
|
1399
|
-
fg:
|
|
1400
|
-
bg:
|
|
1401
|
-
attributes
|
|
2327
|
+
fg: colors.foreground,
|
|
2328
|
+
bg: colors.background,
|
|
2329
|
+
attributes,
|
|
1402
2330
|
onMouseDown: (event) => {
|
|
1403
2331
|
if (event.button !== 0 || action.disabled) return;
|
|
1404
|
-
this.
|
|
2332
|
+
this.emitAction(action.command ?? action.id, action.id);
|
|
1405
2333
|
event.stopPropagation();
|
|
1406
2334
|
},
|
|
1407
2335
|
onKeyDown: (key) => {
|
|
1408
|
-
if (key.name !== "return" || action.disabled) return;
|
|
1409
|
-
this.
|
|
2336
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
2337
|
+
this.emitAction(action.command ?? action.id, action.id);
|
|
1410
2338
|
key.preventDefault();
|
|
1411
2339
|
}
|
|
1412
2340
|
});
|
|
1413
2341
|
button.focusable = !action.disabled;
|
|
1414
2342
|
if (!action.disabled) {
|
|
2343
|
+
button.on("focused", () => {
|
|
2344
|
+
button.attributes = attributes | TextAttributes6.INVERSE;
|
|
2345
|
+
});
|
|
2346
|
+
button.on("blurred", () => {
|
|
2347
|
+
button.attributes = attributes;
|
|
2348
|
+
});
|
|
1415
2349
|
button.onMouseOver = () => {
|
|
1416
|
-
button.
|
|
1417
|
-
button.fg = colors.foreground;
|
|
2350
|
+
button.attributes = attributes | TextAttributes6.UNDERLINE | (button.focused ? TextAttributes6.INVERSE : 0);
|
|
1418
2351
|
};
|
|
1419
2352
|
button.onMouseOut = () => {
|
|
1420
|
-
button.
|
|
1421
|
-
button.fg = foreground;
|
|
2353
|
+
button.attributes = attributes | (button.focused ? TextAttributes6.INVERSE : 0);
|
|
1422
2354
|
};
|
|
1423
2355
|
}
|
|
1424
2356
|
parent.add(button);
|
|
@@ -1426,23 +2358,25 @@ var NativeFrontend = class {
|
|
|
1426
2358
|
}
|
|
1427
2359
|
renderEnhance() {
|
|
1428
2360
|
const state = this.state?.enhance;
|
|
1429
|
-
this.enhance.visible = Boolean(state);
|
|
2361
|
+
this.enhance.visible = Boolean(state && (state.state !== "idle" || this.editor.plainText.trim()));
|
|
1430
2362
|
if (!state) return;
|
|
1431
|
-
const actions = state.state === "confirm" ? [{ id: "enhance-accept", label: "✓" }, { id: "enhance-reject", label: "×" }] : [{ id: "enhance", label: state.state === "busy" ?
|
|
2363
|
+
const actions = state.state === "confirm" ? [{ id: "enhance-accept", label: "✓" }, { id: "enhance-reject", label: "×" }] : [{ id: "enhance", label: state.state === "busy" ? "WORK · ENHANCE" : state.label ?? "ENHANCE", disabled: state.state === "busy" }];
|
|
1432
2364
|
this.replaceActions(this.enhance, actions, "enhance");
|
|
1433
2365
|
}
|
|
1434
2366
|
renderActivity() {
|
|
1435
2367
|
const activity = this.state?.activity;
|
|
1436
2368
|
const stage = activity?.stage === "idle" ? void 0 : activity?.stage;
|
|
1437
2369
|
this.activity.visible = Boolean(activity?.processing || activity?.label || stage);
|
|
1438
|
-
this.activity.content = `${activity?.processing ?
|
|
2370
|
+
this.activity.content = `${activity?.processing ? "RUN · " : ""}${activity?.label || stage || ""}`;
|
|
2371
|
+
this.status.visible = this.activity.visible || this.media.visible;
|
|
1439
2372
|
}
|
|
1440
2373
|
renderMedia() {
|
|
1441
2374
|
const media = this.state?.media;
|
|
1442
2375
|
this.media.visible = Boolean(media && (media.audio || media.video || media.voiceEnabled || media.recording || media.countdown > 0 || media.label));
|
|
2376
|
+
this.status.visible = this.activity.visible || this.media.visible;
|
|
1443
2377
|
if (!media) return;
|
|
1444
2378
|
this.mediaText.content = [
|
|
1445
|
-
media.recording ?
|
|
2379
|
+
media.recording ? "● REC" : void 0,
|
|
1446
2380
|
media.speech ? "speech" : void 0,
|
|
1447
2381
|
media.audio ? "audio" : void 0,
|
|
1448
2382
|
media.video ? "video" : void 0,
|
|
@@ -1453,23 +2387,8 @@ var NativeFrontend = class {
|
|
|
1453
2387
|
].filter(Boolean).join(" · ");
|
|
1454
2388
|
this.mediaText.fg = media.recording ? this.context.colors.warning : this.context.colors.accent;
|
|
1455
2389
|
this.micTrack.visible = media.recording && media.levelDb !== void 0;
|
|
1456
|
-
|
|
1457
|
-
this.
|
|
1458
|
-
}
|
|
1459
|
-
updateAnimation() {
|
|
1460
|
-
const active = this.state?.activity?.processing || this.state?.enhance?.state === "busy" || this.state?.media?.recording;
|
|
1461
|
-
if (active && !this.animation) {
|
|
1462
|
-
this.animation = setInterval(() => {
|
|
1463
|
-
this.animationIndex++;
|
|
1464
|
-
this.renderActivity();
|
|
1465
|
-
this.renderMedia();
|
|
1466
|
-
if (this.state?.enhance?.state === "busy") this.renderEnhance();
|
|
1467
|
-
}, 100);
|
|
1468
|
-
this.animation.unref();
|
|
1469
|
-
} else if (!active && this.animation) {
|
|
1470
|
-
clearInterval(this.animation);
|
|
1471
|
-
this.animation = void 0;
|
|
1472
|
-
}
|
|
2390
|
+
const level = Math.round(Math.max(0, Math.min(1, ((media.levelDb ?? -60) + 60) / 60)) * 8);
|
|
2391
|
+
this.micTrack.content = `[${"●".repeat(level)}${"·".repeat(8 - level)}]`;
|
|
1473
2392
|
}
|
|
1474
2393
|
applyTheme(theme) {
|
|
1475
2394
|
const signature = JSON.stringify(theme);
|
|
@@ -1480,6 +2399,7 @@ var NativeFrontend = class {
|
|
|
1480
2399
|
const colors = this.context.colors = palette(theme);
|
|
1481
2400
|
for (const [name, style] of Object.entries({
|
|
1482
2401
|
default: { fg: colors.foreground },
|
|
2402
|
+
conceal: { fg: colors.foreground },
|
|
1483
2403
|
"markup.heading": { fg: colors.accent, bold: true },
|
|
1484
2404
|
"markup.strong": { fg: colors.foreground, bold: true },
|
|
1485
2405
|
"markup.italic": { fg: colors.foreground, italic: true },
|
|
@@ -1503,13 +2423,11 @@ var NativeFrontend = class {
|
|
|
1503
2423
|
this.headerPanel.fg = colors.foreground;
|
|
1504
2424
|
this.routeWarning.fg = colors.warning;
|
|
1505
2425
|
this.prompt.fg = this.activity.fg = this.liveButton.fg = colors.accent;
|
|
1506
|
-
this.micTrack.
|
|
1507
|
-
this.micLevel.backgroundColor = colors.accent;
|
|
2426
|
+
this.micTrack.fg = colors.foreground;
|
|
1508
2427
|
this.queue.fg = this.metrics.fg = colors.dim;
|
|
1509
2428
|
this.editor.backgroundColor = this.editor.focusedBackgroundColor = colors.background;
|
|
1510
2429
|
this.editor.textColor = this.editor.focusedTextColor = colors.foreground;
|
|
1511
|
-
this.editor.
|
|
1512
|
-
this.editor.selectionBg = colors.selection;
|
|
2430
|
+
this.editor.editorView.setSelectionColors(void 0, void 0);
|
|
1513
2431
|
this.tabs.backgroundColor = this.tabs.focusedBackgroundColor = colors.background;
|
|
1514
2432
|
this.tabs.textColor = colors.dim;
|
|
1515
2433
|
this.tabs.selectedTextColor = colors.foreground;
|
|
@@ -1518,10 +2436,61 @@ var NativeFrontend = class {
|
|
|
1518
2436
|
this.suggestions.textColor = colors.dim;
|
|
1519
2437
|
this.suggestions.selectedTextColor = colors.foreground;
|
|
1520
2438
|
this.suggestions.selectedBackgroundColor = colors.selection;
|
|
2439
|
+
styleNativeScrollbars(this.headerActions, colors);
|
|
2440
|
+
styleNativeScrollbars(this.scroll, colors);
|
|
2441
|
+
this.instruments.applyTheme(colors);
|
|
1521
2442
|
this.content.applyTheme();
|
|
1522
2443
|
this.tasks.applyTheme();
|
|
1523
2444
|
this.modals?.applyTheme();
|
|
1524
2445
|
}
|
|
2446
|
+
toggleHeaderPanel() {
|
|
2447
|
+
if (!this.state || !plainText(this.state.header.panel)) return;
|
|
2448
|
+
this.headerPanelCollapsed = !this.headerPanelCollapsed;
|
|
2449
|
+
this.headerSignature = "";
|
|
2450
|
+
this.update(this.state);
|
|
2451
|
+
}
|
|
2452
|
+
toggleDetails() {
|
|
2453
|
+
this.detailsOpen = !(this.detailsOpen ?? this.renderer.terminalWidth >= 110);
|
|
2454
|
+
this.layoutInstruments();
|
|
2455
|
+
this.renderer.requestRender();
|
|
2456
|
+
}
|
|
2457
|
+
layoutInstruments() {
|
|
2458
|
+
const width = this.renderer.terminalWidth;
|
|
2459
|
+
const wide = width >= 110;
|
|
2460
|
+
const shown = !this.embeddedPane.visible && (this.detailsOpen ?? wide);
|
|
2461
|
+
this.body.flexDirection = wide || this.embeddedPane.visible && width >= 80 ? "row" : "column";
|
|
2462
|
+
this.instruments.visible = shown;
|
|
2463
|
+
const railWidth = Math.floor(width * 0.38);
|
|
2464
|
+
this.instruments.width = wide ? railWidth : "100%";
|
|
2465
|
+
this.scroll.width = shown && wide ? width - railWidth : this.embeddedPane.visible && width >= 80 ? "50%" : "100%";
|
|
2466
|
+
this.instruments.maxHeight = wide ? "100%" : Math.max(4, Math.min(9, Math.floor(this.renderer.terminalHeight / 3)));
|
|
2467
|
+
this.instruments.flexGrow = 0;
|
|
2468
|
+
this.instruments.flexShrink = wide ? 0 : 1;
|
|
2469
|
+
this.instruments.border = wide ? ["left"] : ["top"];
|
|
2470
|
+
this.instruments.borderStyle = "single";
|
|
2471
|
+
this.instruments.borderColor = this.context.colors.border;
|
|
2472
|
+
this.instruments.paddingLeft = wide ? 1 : 0;
|
|
2473
|
+
this.embeddedPane.width = width >= 80 ? "50%" : "100%";
|
|
2474
|
+
this.embeddedPane.maxHeight = width >= 80 ? "100%" : "50%";
|
|
2475
|
+
this.headerActions.visible = width >= 60 && Boolean(this.state?.header.actions.length);
|
|
2476
|
+
const actions = this.state?.header.actions ?? [];
|
|
2477
|
+
const naturalWidth = actions.reduce((size, action) => size + terminalCellWidth(plainText(action.label)), 0) + Math.max(0, actions.length - 1);
|
|
2478
|
+
const detailWidth = width < 40 ? 1 : 9;
|
|
2479
|
+
const gapCount = this.headerActions.visible ? 2 : 1;
|
|
2480
|
+
const controlWidth = Math.min(naturalWidth, Math.max(1, width - 18 - detailWidth - gapCount));
|
|
2481
|
+
this.headerActions.width = controlWidth;
|
|
2482
|
+
this.identity.minWidth = Math.max(0, Math.min(18, width - detailWidth - gapCount));
|
|
2483
|
+
this.identity.width = Math.max(0, width - (this.headerActions.visible ? controlWidth : 0) - detailWidth - gapCount);
|
|
2484
|
+
this.detailButton.width = detailWidth;
|
|
2485
|
+
this.keyHints.content = width < 60 ? "F1 HELP" : "F1 HELP TAB FOCUS";
|
|
2486
|
+
this.detailButton.content = `${shown ? "-" : "+"}${detailWidth > 1 ? " DETAILS" : ""}`;
|
|
2487
|
+
for (let focused = this.renderer.currentFocusedRenderable; focused; focused = focused.parent) {
|
|
2488
|
+
if (!shown && focused === this.instruments || !this.headerActions.visible && focused === this.headerActions) {
|
|
2489
|
+
this.restoreFocus();
|
|
2490
|
+
break;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
1525
2494
|
get offset() {
|
|
1526
2495
|
return Math.max(0, this.scroll.scrollHeight - this.scroll.viewport.height - this.scroll.scrollTop);
|
|
1527
2496
|
}
|
|
@@ -1559,6 +2528,7 @@ var NativeFrontend = class {
|
|
|
1559
2528
|
}
|
|
1560
2529
|
};
|
|
1561
2530
|
onResize = () => {
|
|
2531
|
+
this.layoutInstruments();
|
|
1562
2532
|
this.emit({ type: "resize", columns: this.renderer.terminalWidth, rows: this.renderer.terminalHeight });
|
|
1563
2533
|
};
|
|
1564
2534
|
editorChanged() {
|
|
@@ -1578,6 +2548,7 @@ var NativeFrontend = class {
|
|
|
1578
2548
|
if (text === this.lastInput && cursor === this.lastCursor) return;
|
|
1579
2549
|
this.lastInput = text;
|
|
1580
2550
|
this.lastCursor = cursor;
|
|
2551
|
+
this.renderEnhance();
|
|
1581
2552
|
this.emit({ type: "input", text, cursor });
|
|
1582
2553
|
}
|
|
1583
2554
|
submit() {
|
|
@@ -1608,7 +2579,7 @@ var NativeFrontend = class {
|
|
|
1608
2579
|
const text = String(selected.value);
|
|
1609
2580
|
this.setInput(text + (text.endsWith(" ") ? "" : " "));
|
|
1610
2581
|
this.suggestions.visible = false;
|
|
1611
|
-
this.
|
|
2582
|
+
this.emitAction("completion", text);
|
|
1612
2583
|
return true;
|
|
1613
2584
|
}
|
|
1614
2585
|
setInput(text, cursor = text.length) {
|
|
@@ -1623,6 +2594,10 @@ var NativeFrontend = class {
|
|
|
1623
2594
|
key.preventDefault();
|
|
1624
2595
|
key.stopPropagation();
|
|
1625
2596
|
}
|
|
2597
|
+
emitAction(action, id) {
|
|
2598
|
+
this.flushLatestInput();
|
|
2599
|
+
this.emit({ type: "action", action, ...id === void 0 ? {} : { id } });
|
|
2600
|
+
}
|
|
1626
2601
|
onKey = (key) => {
|
|
1627
2602
|
if (this.modals.active) {
|
|
1628
2603
|
this.modals.active.handleKey(key);
|
|
@@ -1634,10 +2609,22 @@ var NativeFrontend = class {
|
|
|
1634
2609
|
return;
|
|
1635
2610
|
}
|
|
1636
2611
|
if (this.renderer.currentFocusedRenderable instanceof EmbeddedTerminalRenderable2) return;
|
|
2612
|
+
if (key.name === "f1") {
|
|
2613
|
+
this.emitAction("/help");
|
|
2614
|
+
key.preventDefault();
|
|
2615
|
+
key.stopPropagation();
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
if (key.ctrl && key.shift && key.name.toLowerCase() === "i") {
|
|
2619
|
+
this.toggleDetails();
|
|
2620
|
+
key.preventDefault();
|
|
2621
|
+
key.stopPropagation();
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
1637
2624
|
const headerButtons = (this.state?.header.actions ?? []).filter((action) => !action.disabled).map((action) => this.getRenderable(`header:${action.id}`)).filter((button) => Boolean(button));
|
|
1638
2625
|
const headerIndex = headerButtons.indexOf(this.renderer.currentFocusedRenderable);
|
|
1639
2626
|
if (headerIndex >= 0) {
|
|
1640
|
-
const direction = key.name === "left"
|
|
2627
|
+
const direction = key.name === "left" ? -1 : key.name === "right" ? 1 : 0;
|
|
1641
2628
|
if (direction || key.name === "home" || key.name === "end") {
|
|
1642
2629
|
const index = key.name === "home" ? 0 : key.name === "end" ? headerButtons.length - 1 : (headerIndex + direction + headerButtons.length) % headerButtons.length;
|
|
1643
2630
|
const button = headerButtons[index];
|
|
@@ -1689,18 +2676,28 @@ var NativeFrontend = class {
|
|
|
1689
2676
|
return;
|
|
1690
2677
|
}
|
|
1691
2678
|
if (key.name === "escape") {
|
|
2679
|
+
if (this.renderer.currentFocusedRenderable !== this.editor) {
|
|
2680
|
+
this.restoreFocus();
|
|
2681
|
+
key.preventDefault();
|
|
2682
|
+
key.stopPropagation();
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
1692
2685
|
if (this.suggestions.visible) this.suggestions.visible = false;
|
|
1693
2686
|
this.shortcut("escape", key);
|
|
1694
2687
|
return;
|
|
1695
2688
|
}
|
|
1696
2689
|
if (key.name === "tab") {
|
|
1697
2690
|
if (key.ctrl) this.shortcut("ctrl-tab", key);
|
|
1698
|
-
else if (this.suggestions.visible) {
|
|
2691
|
+
else if (!key.shift && this.renderer.currentFocusedRenderable === this.editor && this.suggestions.visible) {
|
|
1699
2692
|
this.acceptSuggestion();
|
|
1700
2693
|
key.preventDefault();
|
|
1701
|
-
} else this.shortcut("tab", key);
|
|
2694
|
+
} else if (!key.shift && this.renderer.currentFocusedRenderable === this.editor && this.editor.plainText) this.shortcut("tab", key);
|
|
2695
|
+
else this.focusNext(key.shift ? -1 : 1);
|
|
2696
|
+
key.preventDefault();
|
|
2697
|
+
key.stopPropagation();
|
|
1702
2698
|
return;
|
|
1703
2699
|
}
|
|
2700
|
+
if (this.renderer.currentFocusedRenderable !== this.editor && this.renderer.currentFocusedRenderable !== this.scroll && !key.ctrl) return;
|
|
1704
2701
|
if (key.ctrl && (key.name === "left" || key.name === "right")) {
|
|
1705
2702
|
this.shortcut(`ctrl-${key.name}`, key);
|
|
1706
2703
|
return;
|
|
@@ -1749,6 +2746,28 @@ var NativeFrontend = class {
|
|
|
1749
2746
|
return;
|
|
1750
2747
|
}
|
|
1751
2748
|
};
|
|
2749
|
+
focusNext(direction) {
|
|
2750
|
+
const targets = [];
|
|
2751
|
+
const visit = (node) => {
|
|
2752
|
+
if (!node.visible) return;
|
|
2753
|
+
if (node.focusable && node !== this.scroll && !(node instanceof ScrollBoxRenderable4) && node.height > 0 && node.width > 0) targets.push(node);
|
|
2754
|
+
for (const child of node.getChildren()) visit(child);
|
|
2755
|
+
};
|
|
2756
|
+
visit(this.root);
|
|
2757
|
+
if (this.instruments.visible) {
|
|
2758
|
+
for (const target2 of this.instruments.focusTargets()) if (!targets.includes(target2)) targets.push(target2);
|
|
2759
|
+
}
|
|
2760
|
+
if (!targets.length) return;
|
|
2761
|
+
const current = targets.indexOf(this.renderer.currentFocusedRenderable);
|
|
2762
|
+
const target = targets[(current + direction + targets.length) % targets.length];
|
|
2763
|
+
target.focus();
|
|
2764
|
+
if (target.id.startsWith("header:")) this.headerActions.scrollChildIntoView(target.id);
|
|
2765
|
+
else if (target.id.startsWith("block-title:") || target.id.startsWith("block-action:")) {
|
|
2766
|
+
let block = target;
|
|
2767
|
+
while (block && !block.id.startsWith("block:")) block = block.parent;
|
|
2768
|
+
if (block) this.scroll.scrollChildIntoView(block.id);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
1752
2771
|
onPaste = (event) => {
|
|
1753
2772
|
if (this.modals.active) {
|
|
1754
2773
|
this.modals.active.handlePaste(event);
|
|
@@ -1888,11 +2907,11 @@ var NativeFrontend = class {
|
|
|
1888
2907
|
async dispose() {
|
|
1889
2908
|
if (this.destroyed) return;
|
|
1890
2909
|
this.destroyed = true;
|
|
1891
|
-
|
|
2910
|
+
this.disposeSelectionContrast();
|
|
1892
2911
|
this.renderer.keyInput.off("keypress", this.onKey);
|
|
1893
2912
|
this.renderer.keyInput.off("paste", this.onPaste);
|
|
1894
|
-
this.renderer.off(
|
|
1895
|
-
this.renderer.off(
|
|
2913
|
+
this.renderer.off(CliRenderEvents2.FRAME, this.onFrame);
|
|
2914
|
+
this.renderer.off(CliRenderEvents2.RESIZE, this.onResize);
|
|
1896
2915
|
this.modals.destroy();
|
|
1897
2916
|
this.content.destroy();
|
|
1898
2917
|
this.tasks.destroy();
|
|
@@ -1902,21 +2921,86 @@ var NativeFrontend = class {
|
|
|
1902
2921
|
}
|
|
1903
2922
|
};
|
|
1904
2923
|
|
|
2924
|
+
// packages/cli/src/tui/native/components/cursor.ts
|
|
2925
|
+
import { CliRenderEvents as CliRenderEvents3 } from "@opentui/core";
|
|
2926
|
+
function installTerminalCursorDefaults(renderer2, session) {
|
|
2927
|
+
const reset = new TextEncoder().encode("\x1B]112\x07");
|
|
2928
|
+
let disposed = false;
|
|
2929
|
+
let requested = 0;
|
|
2930
|
+
let restored = 0;
|
|
2931
|
+
let pending;
|
|
2932
|
+
let observedFrame;
|
|
2933
|
+
const restore = () => {
|
|
2934
|
+
if (disposed || session.disposed) return Promise.resolve();
|
|
2935
|
+
requested++;
|
|
2936
|
+
if (pending) return pending;
|
|
2937
|
+
let resolve;
|
|
2938
|
+
let reject;
|
|
2939
|
+
const completion = new Promise((done, fail2) => {
|
|
2940
|
+
resolve = done;
|
|
2941
|
+
reject = fail2;
|
|
2942
|
+
});
|
|
2943
|
+
pending = completion;
|
|
2944
|
+
void (async () => {
|
|
2945
|
+
try {
|
|
2946
|
+
while (!disposed && !session.disposed && restored < requested) {
|
|
2947
|
+
const generation = requested;
|
|
2948
|
+
while (!session.write(reset)) await session.idle();
|
|
2949
|
+
await session.idle();
|
|
2950
|
+
restored = generation;
|
|
2951
|
+
}
|
|
2952
|
+
pending = void 0;
|
|
2953
|
+
resolve();
|
|
2954
|
+
} catch (error) {
|
|
2955
|
+
pending = void 0;
|
|
2956
|
+
reject(error);
|
|
2957
|
+
}
|
|
2958
|
+
})();
|
|
2959
|
+
return completion;
|
|
2960
|
+
};
|
|
2961
|
+
const afterFrame = () => {
|
|
2962
|
+
const current = restore();
|
|
2963
|
+
if (observedFrame === current) return;
|
|
2964
|
+
observedFrame = current;
|
|
2965
|
+
void current.then(() => {
|
|
2966
|
+
if (observedFrame === current) observedFrame = void 0;
|
|
2967
|
+
}, (error) => {
|
|
2968
|
+
if (observedFrame === current) observedFrame = void 0;
|
|
2969
|
+
if (!disposed && !renderer2.isDestroyed) renderer2.emit("error", error);
|
|
2970
|
+
});
|
|
2971
|
+
};
|
|
2972
|
+
const dispose = () => {
|
|
2973
|
+
if (disposed) return;
|
|
2974
|
+
disposed = true;
|
|
2975
|
+
renderer2.off(CliRenderEvents3.FRAME, afterFrame);
|
|
2976
|
+
renderer2.off(CliRenderEvents3.DESTROY, dispose);
|
|
2977
|
+
};
|
|
2978
|
+
renderer2.on(CliRenderEvents3.FRAME, afterFrame);
|
|
2979
|
+
renderer2.once(CliRenderEvents3.DESTROY, dispose);
|
|
2980
|
+
return { restore, dispose };
|
|
2981
|
+
}
|
|
2982
|
+
|
|
1905
2983
|
// packages/cli/src/tui/native/worker.ts
|
|
1906
2984
|
var replies = createWriteStream("", { fd: 3, autoClose: false });
|
|
1907
2985
|
var reply = (message) => replies.write(JSON.stringify(message) + "\n");
|
|
1908
2986
|
var terminalInput = new PassThrough();
|
|
1909
2987
|
var renderer;
|
|
1910
2988
|
var frontend;
|
|
2989
|
+
var cursorDefaults;
|
|
1911
2990
|
var stopping = false;
|
|
1912
2991
|
var failed = false;
|
|
1913
2992
|
async function close() {
|
|
1914
2993
|
if (stopping) return;
|
|
1915
2994
|
stopping = true;
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2995
|
+
try {
|
|
2996
|
+
await frontend?.dispose();
|
|
2997
|
+
await cursorDefaults?.restore();
|
|
2998
|
+
} finally {
|
|
2999
|
+
cursorDefaults?.dispose();
|
|
3000
|
+
if (renderer) {
|
|
3001
|
+
renderer.destroy();
|
|
3002
|
+
await renderer.closed;
|
|
3003
|
+
}
|
|
1920
3004
|
}
|
|
1921
3005
|
}
|
|
1922
3006
|
async function fail(error) {
|
|
@@ -1936,18 +3020,22 @@ async function command(message) {
|
|
|
1936
3020
|
frontend.update(message.snapshot, message.content);
|
|
1937
3021
|
renderer.requestRender();
|
|
1938
3022
|
await renderer.idle();
|
|
1939
|
-
} else if (message.type === "suspend")
|
|
1940
|
-
|
|
3023
|
+
} else if (message.type === "suspend") {
|
|
3024
|
+
await cursorDefaults.restore();
|
|
3025
|
+
await renderer.suspend();
|
|
3026
|
+
} else if (message.type === "resume") {
|
|
1941
3027
|
await renderer.resume();
|
|
1942
3028
|
renderer.requestRender();
|
|
1943
3029
|
await renderer.idle();
|
|
3030
|
+
await cursorDefaults.restore();
|
|
1944
3031
|
} else if (message.type === "stop") await close();
|
|
1945
3032
|
reply({ type: "ack", id: message.id });
|
|
1946
3033
|
if (message.type === "stop") replies.end(() => process.exit(0));
|
|
1947
3034
|
}
|
|
1948
3035
|
try {
|
|
3036
|
+
const session = new NativeSession(process.stdout);
|
|
1949
3037
|
renderer = await createCliRenderer({
|
|
1950
|
-
nativeSession:
|
|
3038
|
+
nativeSession: session,
|
|
1951
3039
|
stdin: terminalInput,
|
|
1952
3040
|
stdout: process.stdout,
|
|
1953
3041
|
exitOnCtrlC: false,
|
|
@@ -1958,21 +3046,22 @@ try {
|
|
|
1958
3046
|
screenMode: "alternate-screen",
|
|
1959
3047
|
consoleMode: "disabled",
|
|
1960
3048
|
externalOutputMode: "passthrough",
|
|
1961
|
-
backgroundColor:
|
|
3049
|
+
backgroundColor: RGBA3.defaultBackground(),
|
|
1962
3050
|
openConsoleOnError: false,
|
|
1963
3051
|
targetFps: 60,
|
|
1964
3052
|
maxFps: 60,
|
|
1965
3053
|
clearOnShutdown: false
|
|
1966
3054
|
});
|
|
1967
|
-
renderer.on(
|
|
3055
|
+
renderer.on(CliRenderEvents4.RENDER_ERROR, (event) => {
|
|
1968
3056
|
void fail(event.error);
|
|
1969
3057
|
});
|
|
1970
|
-
renderer.on(
|
|
3058
|
+
renderer.on(CliRenderEvents4.HANDLER_ERROR, (event) => {
|
|
1971
3059
|
void fail(event.error);
|
|
1972
3060
|
});
|
|
1973
3061
|
renderer.on("error", (event) => {
|
|
1974
3062
|
void fail(event);
|
|
1975
3063
|
});
|
|
3064
|
+
cursorDefaults = installTerminalCursorDefaults(renderer, session);
|
|
1976
3065
|
frontend = new NativeFrontend(renderer, (event) => reply({ type: "event", event }));
|
|
1977
3066
|
const controls = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
1978
3067
|
let pending = Promise.resolve();
|
|
@@ -2004,6 +3093,7 @@ try {
|
|
|
2004
3093
|
});
|
|
2005
3094
|
renderer.requestRender();
|
|
2006
3095
|
await renderer.idle();
|
|
3096
|
+
await cursorDefaults.restore();
|
|
2007
3097
|
reply({ type: "ready" });
|
|
2008
3098
|
} catch (error) {
|
|
2009
3099
|
await fail(error);
|