omnius 1.0.862 → 1.0.864
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/assets/tray/omnius-checking.ico +0 -0
- package/assets/tray/omnius-checking.png +0 -0
- package/assets/tray/omnius-checking.svg +7 -4
- package/assets/tray/omnius-offline.ico +0 -0
- package/assets/tray/omnius-offline.png +0 -0
- package/assets/tray/omnius-offline.svg +6 -4
- package/assets/tray/omnius-online.ico +0 -0
- package/assets/tray/omnius-online.png +0 -0
- package/assets/tray/omnius-online.svg +5 -4
- package/dist/index.js +2 -2
- package/dist/library.js +11 -11
- package/dist/tui/native/worker.js +3569 -315
- package/docs/DISCOVERY.json +2080 -451
- package/docs/DISCOVERY.md +48 -2
- package/docs/work-orders/desktop-projects.md +42 -0
- package/docs/work-orders/evidence/desktop-indicator-registration-20261016.json +23 -0
- package/docs/work-orders/evidence/inference-device-policy-readonly-20261016.json +56 -0
- 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-clean-transcript-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-collapse-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-devices-expanded-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-divider-collapsed-160x48.png +0 -0
- package/docs/work-orders/evidence/instrument-divider-resized-160x48.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 +11818 -0
- package/docs/work-orders/evidence/instrument-report-broker-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-report-gpu-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-report-stats-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-semantic-components-20261016-updated.json +80 -0
- package/docs/work-orders/evidence/instrument-semantic-components-20261016.json +76 -0
- package/docs/work-orders/evidence/instrument-targeted-verification-20261016-updated.json +430 -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/evidence/instrument-vim-composer-120x36.png +0 -0
- package/docs/work-orders/evidence/tray-native-host-checking.png +0 -0
- package/docs/work-orders/evidence/tray-native-host-dark.png +0 -0
- package/docs/work-orders/evidence/tray-native-host-offline.png +0 -0
- package/docs/work-orders/evidence/tray-native-host.png +0 -0
- package/docs/work-orders/evidence/tray-project-3.png +0 -0
- package/docs/work-orders/evidence/tray-project-4.png +0 -0
- package/docs/work-orders/evidence/tray-project-5.png +0 -0
- package/docs/work-orders/evidence/tray-projects-live.json +338 -0
- package/docs/work-orders/evidence/tray-projects-menu.png +0 -0
- package/docs/work-orders/instrument-command-surface-audit.md +304 -0
- package/docs/work-orders/instrument-tui.md +156 -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 CliRenderEvents5, 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 BoxRenderable5,
|
|
10
|
+
CliRenderEvents as CliRenderEvents3,
|
|
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,280 @@ 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
|
|
141
|
+
};
|
|
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
|
+
}
|
|
173
|
+
|
|
174
|
+
// packages/cli/src/tui/native/components/code.ts
|
|
175
|
+
var aliases = {
|
|
176
|
+
js: "javascript",
|
|
177
|
+
jsx: "javascript",
|
|
178
|
+
ts: "typescript",
|
|
179
|
+
tsx: "typescriptreact",
|
|
180
|
+
py: "python",
|
|
181
|
+
sh: "bash",
|
|
182
|
+
shell: "bash",
|
|
183
|
+
yml: "yaml",
|
|
184
|
+
jsonl: "json",
|
|
185
|
+
json5: "json",
|
|
186
|
+
jsonc: "json"
|
|
187
|
+
};
|
|
188
|
+
function nativeCodeLanguage(language) {
|
|
189
|
+
const name = language?.trim().split(/\s+/)[0]?.toLowerCase();
|
|
190
|
+
return name ? aliases[name] ?? name : void 0;
|
|
191
|
+
}
|
|
192
|
+
function prettyNativeJson(source) {
|
|
193
|
+
try {
|
|
194
|
+
JSON.parse(source);
|
|
195
|
+
} catch {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
let compact = "", quoted = false, escaped = false;
|
|
199
|
+
for (const char of source) {
|
|
200
|
+
if (quoted || !/[\t\r\n ]/.test(char)) compact += char;
|
|
201
|
+
if (quoted) {
|
|
202
|
+
if (escaped) escaped = false;
|
|
203
|
+
else if (char === "\\") escaped = true;
|
|
204
|
+
else if (char === '"') quoted = false;
|
|
205
|
+
} else if (char === '"') quoted = true;
|
|
206
|
+
}
|
|
207
|
+
let result = "", depth = 0;
|
|
208
|
+
quoted = escaped = false;
|
|
209
|
+
const newline = () => "\n" + " ".repeat(depth);
|
|
210
|
+
for (let index = 0; index < compact.length; index++) {
|
|
211
|
+
const char = compact[index];
|
|
212
|
+
if (quoted) {
|
|
213
|
+
result += char;
|
|
214
|
+
if (escaped) escaped = false;
|
|
215
|
+
else if (char === "\\") escaped = true;
|
|
216
|
+
else if (char === '"') quoted = false;
|
|
217
|
+
} else if (char === '"') {
|
|
218
|
+
quoted = true;
|
|
219
|
+
result += char;
|
|
220
|
+
} else if (char === "{" || char === "[") {
|
|
221
|
+
result += char;
|
|
222
|
+
depth++;
|
|
223
|
+
if (compact[index + 1] !== (char === "{" ? "}" : "]"))
|
|
224
|
+
result += newline();
|
|
225
|
+
} else if (char === "}" || char === "]") {
|
|
226
|
+
depth--;
|
|
227
|
+
if (compact[index - 1] !== (char === "}" ? "{" : "["))
|
|
228
|
+
result += newline();
|
|
229
|
+
result += char;
|
|
230
|
+
} else if (char === ",") result += char + newline();
|
|
231
|
+
else if (char === ":") result += ": ";
|
|
232
|
+
else result += char;
|
|
233
|
+
}
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
function nativeCodeContent(source, language) {
|
|
237
|
+
const selected = nativeCodeLanguage(language);
|
|
238
|
+
if (selected === "json" || !selected && /^[\s]*[\[{]/.test(source)) {
|
|
239
|
+
const pretty = prettyNativeJson(source);
|
|
240
|
+
if (pretty !== void 0) return { content: pretty, language: "json" };
|
|
241
|
+
}
|
|
242
|
+
return { content: source, language: selected };
|
|
243
|
+
}
|
|
244
|
+
function nativeCodeHighlights(source) {
|
|
245
|
+
const highlights = [];
|
|
246
|
+
const tokens = /("(?:\\.|[^"\\])*"?|'(?:\\.|[^'\\])*'?|`(?:\\.|[^`\\])*`?)|(\/\/[^\n]*|\/\*[\s\S]*?(?:\*\/|$)|\#[^\n]*)|\b(true|false|null|None|True|False|nil)\b|\b(const|let|var|function|class|def|return|if|else|elif|for|while|in|import|from|export|async|await|try|catch|except|throw|raise|new|public|private|static|void|int|bool|struct|enum|fn|pub|use|match|impl|package|func|select|switch|case|break|continue|end|do|then|fi|done)\b|(?<![\w])(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|([{}\[\](),:;])/g;
|
|
247
|
+
for (const match of source.matchAll(tokens)) {
|
|
248
|
+
const start = match.index, end = start + match[0].length;
|
|
249
|
+
const group = match[1] ? /^\s*:/.test(source.slice(end)) ? "property" : "string" : match[2] ? "comment" : match[3] ? "constant" : match[4] ? "keyword" : match[5] ? "number" : "punctuation";
|
|
250
|
+
highlights.push([start, end, group]);
|
|
251
|
+
}
|
|
252
|
+
return highlights;
|
|
253
|
+
}
|
|
254
|
+
function installNativeSyntax(syntax, colors) {
|
|
255
|
+
const base = { fg: colors.foreground, bg: colors.background };
|
|
256
|
+
for (const [name, attributes] of Object.entries({
|
|
257
|
+
default: {},
|
|
258
|
+
conceal: { dim: true },
|
|
259
|
+
"markup.heading": { bold: true },
|
|
260
|
+
"markup.strong": { bold: true },
|
|
261
|
+
"markup.italic": { italic: true },
|
|
262
|
+
"markup.link": { underline: true },
|
|
263
|
+
"markup.link.url": { dim: true },
|
|
264
|
+
"markup.raw": {},
|
|
265
|
+
"markup.list": { dim: true },
|
|
266
|
+
"markup.quote": { dim: true },
|
|
267
|
+
comment: { dim: true, italic: true },
|
|
268
|
+
keyword: { bold: true },
|
|
269
|
+
property: { bold: true },
|
|
270
|
+
"string.special.key": { bold: true },
|
|
271
|
+
string: { italic: true },
|
|
272
|
+
"string.escape": { bold: true },
|
|
273
|
+
number: { bold: true },
|
|
274
|
+
boolean: { bold: true },
|
|
275
|
+
constant: { bold: true },
|
|
276
|
+
function: { bold: true },
|
|
277
|
+
type: { underline: true },
|
|
278
|
+
punctuation: { dim: true },
|
|
279
|
+
operator: {},
|
|
280
|
+
variable: {},
|
|
281
|
+
tag: { bold: true },
|
|
282
|
+
attribute: { italic: true }
|
|
283
|
+
}))
|
|
284
|
+
syntax.registerStyle(name, { ...base, ...attributes });
|
|
285
|
+
}
|
|
286
|
+
function nativeMarkdownSections(owner, source, streaming = false) {
|
|
287
|
+
const bare = !source.includes("\n```") && !source.includes("\n~~~") && /^[\s]*[\[{]/.test(source) ? prettyNativeJson(source) : void 0;
|
|
288
|
+
if (bare !== void 0)
|
|
289
|
+
return [
|
|
290
|
+
{
|
|
291
|
+
id: `${owner}:json`,
|
|
292
|
+
kind: "code",
|
|
293
|
+
title: "JSON",
|
|
294
|
+
language: "json",
|
|
295
|
+
text: bare,
|
|
296
|
+
collapsible: true,
|
|
297
|
+
streaming
|
|
298
|
+
}
|
|
299
|
+
];
|
|
300
|
+
const rows = source.split("\n"), offsets = [];
|
|
301
|
+
let offset = 0;
|
|
302
|
+
for (const row of rows) {
|
|
303
|
+
offsets.push(offset);
|
|
304
|
+
offset += row.length + 1;
|
|
305
|
+
}
|
|
306
|
+
const output = [], stack = [
|
|
307
|
+
{ level: 0, children: output }
|
|
308
|
+
];
|
|
309
|
+
let prose = [], proseStart = 0, segmented = false;
|
|
310
|
+
const current = () => stack.at(-1).children;
|
|
311
|
+
const flush = () => {
|
|
312
|
+
if (prose.join("\n").trim())
|
|
313
|
+
current().push({
|
|
314
|
+
id: `${owner}:prose:${proseStart}`,
|
|
315
|
+
kind: "markdown",
|
|
316
|
+
text: prose.join("\n"),
|
|
317
|
+
streaming
|
|
318
|
+
});
|
|
319
|
+
prose = [];
|
|
150
320
|
};
|
|
321
|
+
for (let index = 0; index < rows.length; index++) {
|
|
322
|
+
const row = rows[index];
|
|
323
|
+
const fence = row.match(/^([ >]*)(`{3,}|~{3,})([^`]*)$/);
|
|
324
|
+
if (fence) {
|
|
325
|
+
flush();
|
|
326
|
+
segmented = true;
|
|
327
|
+
const start = offsets[index], contents = [], marker = fence[2];
|
|
328
|
+
const quoteDepth = (fence[1].match(/>/g) ?? []).length;
|
|
329
|
+
const indentation = Math.max(
|
|
330
|
+
0,
|
|
331
|
+
fence[1].split(">").at(-1).length - (quoteDepth ? 1 : 0)
|
|
332
|
+
);
|
|
333
|
+
const containerContent = (line) => {
|
|
334
|
+
for (let depth = 0; depth < quoteDepth; depth++)
|
|
335
|
+
line = line.replace(/^ {0,3}> ?/, "");
|
|
336
|
+
return line.replace(new RegExp(`^ {0,${indentation}}`), "");
|
|
337
|
+
};
|
|
338
|
+
while (++index < rows.length) {
|
|
339
|
+
const line = containerContent(rows[index]);
|
|
340
|
+
const closing = line.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
|
|
341
|
+
if (closing && closing[1][0] === marker[0] && closing[1].length >= marker.length)
|
|
342
|
+
break;
|
|
343
|
+
contents.push(line);
|
|
344
|
+
}
|
|
345
|
+
const code = nativeCodeContent(contents.join("\n"), fence[3]);
|
|
346
|
+
current().push({
|
|
347
|
+
id: `${owner}:code:${start}`,
|
|
348
|
+
kind: "code",
|
|
349
|
+
title: code.language?.toUpperCase() ?? "CODE",
|
|
350
|
+
language: code.language,
|
|
351
|
+
text: code.content,
|
|
352
|
+
collapsible: true,
|
|
353
|
+
streaming: streaming && index >= rows.length
|
|
354
|
+
});
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const atx = row.match(/^ {0,3}(#{1,6})(?:\s+(.*?)\s*#*\s*|\s*)$/);
|
|
358
|
+
const setext = row.trim() && rows[index + 1]?.match(/^ {0,3}(=+|-+)\s*$/);
|
|
359
|
+
if (atx || setext) {
|
|
360
|
+
flush();
|
|
361
|
+
segmented = true;
|
|
362
|
+
const level = atx ? atx[1].length : setext[1][0] === "=" ? 1 : 2;
|
|
363
|
+
while (stack.length > 1 && stack.at(-1).level >= level) stack.pop();
|
|
364
|
+
const section = {
|
|
365
|
+
id: `${owner}:section:${offsets[index]}`,
|
|
366
|
+
kind: "column",
|
|
367
|
+
title: atx ? atx[2] ?? "" : row.trim(),
|
|
368
|
+
collapsible: true,
|
|
369
|
+
children: []
|
|
370
|
+
};
|
|
371
|
+
current().push(section);
|
|
372
|
+
stack.push({ level, children: section.children });
|
|
373
|
+
if (!atx) index++;
|
|
374
|
+
} else {
|
|
375
|
+
if (!prose.length) proseStart = offsets[index];
|
|
376
|
+
prose.push(row);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
flush();
|
|
380
|
+
return segmented ? output : void 0;
|
|
151
381
|
}
|
|
152
382
|
|
|
153
383
|
// packages/cli/src/tui/native/components/blocks.ts
|
|
384
|
+
function nativeProgressText(value, working = false) {
|
|
385
|
+
if (value === void 0 || !Number.isFinite(value)) return `[············] ${working ? "working" : "pending"}`;
|
|
386
|
+
const ratio = Math.max(0, Math.min(1, value));
|
|
387
|
+
const filled = Math.round(ratio * 12);
|
|
388
|
+
return `[${"•".repeat(filled)}${"·".repeat(12 - filled)}] ${Math.round(ratio * 100)}%`;
|
|
389
|
+
}
|
|
390
|
+
function nativeControlFocus(control, baseAttributes = 0) {
|
|
391
|
+
const update = () => {
|
|
392
|
+
if (!control.isDestroyed) control.attributes = baseAttributes | (control.focused ? 32 : 0);
|
|
393
|
+
};
|
|
394
|
+
control.on(RenderableEvents.FOCUSED, update);
|
|
395
|
+
control.on(RenderableEvents.BLURRED, update);
|
|
396
|
+
update();
|
|
397
|
+
}
|
|
154
398
|
var NativeBlockView = class {
|
|
155
399
|
constructor(context, spec, inline = false) {
|
|
156
400
|
this.context = context;
|
|
@@ -177,14 +421,15 @@ var NativeBlockView = class {
|
|
|
177
421
|
text;
|
|
178
422
|
body;
|
|
179
423
|
children;
|
|
424
|
+
markdownSections;
|
|
180
425
|
actions;
|
|
181
|
-
|
|
182
|
-
progressValue;
|
|
426
|
+
progress;
|
|
183
427
|
image;
|
|
184
428
|
terminal;
|
|
185
429
|
collapsed = false;
|
|
186
430
|
previousModelCollapsed;
|
|
187
431
|
signature = "";
|
|
432
|
+
actionsSignature = "";
|
|
188
433
|
pixel;
|
|
189
434
|
get block() {
|
|
190
435
|
return this.spec;
|
|
@@ -192,16 +437,18 @@ var NativeBlockView = class {
|
|
|
192
437
|
build() {
|
|
193
438
|
const { renderer: renderer2, colors, syntax } = this.context;
|
|
194
439
|
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.
|
|
440
|
+
if (spec.kind === "card") this.root.borderStyle = "single";
|
|
441
|
+
this.root.border = spec.kind === "card" ? ["left", "bottom"] : false;
|
|
442
|
+
this.root.paddingLeft = spec.kind === "card" ? 1 : 0;
|
|
443
|
+
this.root.paddingRight = 0;
|
|
444
|
+
this.root.marginBottom = 0;
|
|
199
445
|
this.root.flexDirection = spec.kind === "row" ? "row" : "column";
|
|
200
446
|
if (spec.title) {
|
|
201
447
|
this.title = new TextRenderable(renderer2, {
|
|
202
448
|
id: `block-title:${spec.id}`,
|
|
203
449
|
content: "",
|
|
204
|
-
fg: colors.
|
|
450
|
+
fg: colors.foreground,
|
|
451
|
+
attributes: 1,
|
|
205
452
|
flexShrink: 0,
|
|
206
453
|
width: "100%",
|
|
207
454
|
wrapMode: "word",
|
|
@@ -210,8 +457,15 @@ var NativeBlockView = class {
|
|
|
210
457
|
if (event.button !== 0 || !this.spec.collapsible) return;
|
|
211
458
|
this.toggle();
|
|
212
459
|
event.stopPropagation();
|
|
460
|
+
},
|
|
461
|
+
onKeyDown: (key) => {
|
|
462
|
+
if (key.eventType === "release" || !this.spec.collapsible || key.name !== "return" && key.sequence !== " ") return;
|
|
463
|
+
this.toggle();
|
|
464
|
+
key.preventDefault();
|
|
465
|
+
key.stopPropagation();
|
|
213
466
|
}
|
|
214
467
|
});
|
|
468
|
+
nativeControlFocus(this.title, 1);
|
|
215
469
|
this.root.add(this.title);
|
|
216
470
|
}
|
|
217
471
|
this.body = new BoxRenderable(renderer2, {
|
|
@@ -270,14 +524,31 @@ var NativeBlockView = class {
|
|
|
270
524
|
flexShrink: 0,
|
|
271
525
|
internalBlockMode: "top-level",
|
|
272
526
|
conceal: true,
|
|
273
|
-
tableOptions: { style: "columns", widthMode: "full", wrapMode: "word", selectable: true }
|
|
527
|
+
tableOptions: { style: "columns", widthMode: "full", wrapMode: "word", selectable: true },
|
|
528
|
+
renderNode: (token) => {
|
|
529
|
+
if (token.type !== "code") return;
|
|
530
|
+
const value = nativeCodeContent(token.text, token.lang);
|
|
531
|
+
return new CodeRenderable(renderer2, {
|
|
532
|
+
content: value.content,
|
|
533
|
+
filetype: value.language ?? "text",
|
|
534
|
+
syntaxStyle: syntax,
|
|
535
|
+
fg: colors.foreground,
|
|
536
|
+
bg: colors.background,
|
|
537
|
+
width: "100%",
|
|
538
|
+
flexShrink: 0,
|
|
539
|
+
wrapMode: "word",
|
|
540
|
+
selectable: true,
|
|
541
|
+
streaming: this.spec.streaming ?? false,
|
|
542
|
+
onHighlight: (highlights, context) => highlights.length ? void 0 : nativeCodeHighlights(context.content)
|
|
543
|
+
});
|
|
544
|
+
}
|
|
274
545
|
});
|
|
275
546
|
this.body.add(this.text);
|
|
276
547
|
} else if (spec.kind === "code") {
|
|
277
548
|
this.text = new CodeRenderable(renderer2, {
|
|
278
549
|
id: `code:${spec.id}`,
|
|
279
550
|
content: "",
|
|
280
|
-
filetype: spec.language,
|
|
551
|
+
filetype: spec.language ?? "text",
|
|
281
552
|
syntaxStyle: syntax,
|
|
282
553
|
fg: colors.foreground,
|
|
283
554
|
bg: colors.background,
|
|
@@ -286,7 +557,8 @@ var NativeBlockView = class {
|
|
|
286
557
|
streaming: spec.streaming ?? false,
|
|
287
558
|
selectable: true,
|
|
288
559
|
wrapMode: "word",
|
|
289
|
-
drawUnstyledText: true
|
|
560
|
+
drawUnstyledText: true,
|
|
561
|
+
onHighlight: (highlights, context) => highlights.length ? void 0 : nativeCodeHighlights(context.content)
|
|
290
562
|
});
|
|
291
563
|
this.body.add(this.text);
|
|
292
564
|
} else if (spec.text !== void 0) {
|
|
@@ -303,36 +575,37 @@ var NativeBlockView = class {
|
|
|
303
575
|
this.body.add(this.text);
|
|
304
576
|
}
|
|
305
577
|
if (spec.kind === "progress") {
|
|
306
|
-
this.
|
|
578
|
+
this.progress = new TextRenderable(renderer2, {
|
|
307
579
|
id: `progress-track:${spec.id}`,
|
|
308
580
|
width: "100%",
|
|
309
581
|
height: 1,
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
backgroundColor: colors.accent
|
|
582
|
+
content: "",
|
|
583
|
+
fg: colors.foreground,
|
|
584
|
+
bg: colors.background,
|
|
585
|
+
attributes: 2,
|
|
586
|
+
wrapMode: "none",
|
|
587
|
+
selectable: false
|
|
317
588
|
});
|
|
318
|
-
this.
|
|
319
|
-
this.body.add(this.progressBar);
|
|
589
|
+
this.body.add(this.progress);
|
|
320
590
|
}
|
|
321
591
|
this.children = new NativeBlockList(this.context, this.body);
|
|
322
592
|
this.actions = new BoxRenderable(renderer2, {
|
|
323
593
|
id: `block-actions:${spec.id}`,
|
|
324
594
|
flexDirection: "row",
|
|
325
|
-
gap:
|
|
595
|
+
gap: 1,
|
|
326
596
|
flexShrink: 0
|
|
327
597
|
});
|
|
328
598
|
this.root.add(this.actions);
|
|
329
599
|
}
|
|
330
600
|
reset() {
|
|
331
601
|
this.children?.destroy();
|
|
602
|
+
this.markdownSections?.destroy();
|
|
603
|
+
this.markdownSections = void 0;
|
|
332
604
|
if (this.terminal) this.context.terminals.delete(this.spec.id);
|
|
333
605
|
for (const child of [...this.root.getChildren()]) child.destroyRecursively();
|
|
334
|
-
this.title = this.text = this.body = this.actions = this.
|
|
606
|
+
this.title = this.text = this.body = this.actions = this.progress = this.image = this.terminal = void 0;
|
|
335
607
|
this.children = void 0;
|
|
608
|
+
this.actionsSignature = "";
|
|
336
609
|
this.build();
|
|
337
610
|
}
|
|
338
611
|
update(spec) {
|
|
@@ -346,27 +619,43 @@ var NativeBlockView = class {
|
|
|
346
619
|
this.collapsed = spec.collapsed ?? false;
|
|
347
620
|
this.previousModelCollapsed = spec.collapsed;
|
|
348
621
|
}
|
|
349
|
-
if (this.title)
|
|
622
|
+
if (this.title) {
|
|
623
|
+
this.title.content = `${spec.collapsible ? this.collapsed ? "▸ " : "· " : ""}${plainText(spec.title)}`;
|
|
624
|
+
this.title.focusable = Boolean(spec.collapsible);
|
|
625
|
+
}
|
|
350
626
|
if (this.body) this.body.visible = !this.collapsed;
|
|
627
|
+
const sections = spec.kind === "markdown" ? nativeMarkdownSections(spec.id, plainText(spec.text), spec.streaming) : void 0;
|
|
628
|
+
if (this.text && spec.kind === "markdown") this.text.visible = !sections;
|
|
629
|
+
if (sections && this.body) {
|
|
630
|
+
this.markdownSections ??= new NativeBlockList(this.context, this.body);
|
|
631
|
+
this.markdownSections.replace(sections);
|
|
632
|
+
} else if (this.markdownSections) {
|
|
633
|
+
this.markdownSections.destroy();
|
|
634
|
+
this.markdownSections = void 0;
|
|
635
|
+
}
|
|
351
636
|
if (this.text instanceof TextRenderable) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
|
|
352
637
|
else if (this.text instanceof MarkdownRenderable || this.text instanceof CodeRenderable) {
|
|
353
|
-
this.text
|
|
638
|
+
const code = this.text instanceof CodeRenderable ? nativeCodeContent(plainText(spec.text), spec.language) : void 0;
|
|
639
|
+
this.text.content = sections ? "" : code?.content ?? plainText(spec.text);
|
|
354
640
|
this.text.streaming = spec.streaming ?? false;
|
|
355
|
-
if (this.text instanceof CodeRenderable) this.text.filetype =
|
|
641
|
+
if (this.text instanceof CodeRenderable) this.text.filetype = code?.language ?? "text";
|
|
356
642
|
}
|
|
357
643
|
if (this.image && spec.imagePath && this.image.source !== spec.imagePath) this.image.source = spec.imagePath;
|
|
358
644
|
if (this.terminal) this.terminal.height = spec.height ?? 14;
|
|
359
645
|
if (this.image) this.image.height = spec.height ?? 12;
|
|
360
|
-
if (this.
|
|
646
|
+
if (this.progress) this.progress.content = nativeProgressText(spec.progress, spec.streaming);
|
|
361
647
|
this.children?.replace(spec.children ?? []);
|
|
362
|
-
|
|
648
|
+
const actionsSignature = JSON.stringify(spec.actions ?? []);
|
|
649
|
+
if (this.actions && actionsSignature !== this.actionsSignature) {
|
|
650
|
+
this.actionsSignature = actionsSignature;
|
|
363
651
|
for (const child of [...this.actions.getChildren()]) child.destroy();
|
|
364
652
|
for (const action of spec.actions ?? []) {
|
|
365
653
|
const button = new TextRenderable(this.context.renderer, {
|
|
366
654
|
id: `block-action:${spec.id}:${action.id}`,
|
|
367
|
-
content:
|
|
368
|
-
fg:
|
|
369
|
-
|
|
655
|
+
content: plainText(action.label),
|
|
656
|
+
fg: this.context.colors.foreground,
|
|
657
|
+
bg: this.context.colors.background,
|
|
658
|
+
attributes: action.disabled ? 2 : action.active ? 1 : 0,
|
|
370
659
|
flexShrink: 0,
|
|
371
660
|
selectable: false,
|
|
372
661
|
onMouseDown: (event) => {
|
|
@@ -375,12 +664,13 @@ var NativeBlockView = class {
|
|
|
375
664
|
event.stopPropagation();
|
|
376
665
|
},
|
|
377
666
|
onKeyDown: (key) => {
|
|
378
|
-
if (key.name !== "return" || action.disabled) return;
|
|
667
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
379
668
|
this.context.emit({ type: "action", action: action.command ?? action.id, id: this.spec.id });
|
|
380
669
|
key.preventDefault();
|
|
381
670
|
}
|
|
382
671
|
});
|
|
383
672
|
button.focusable = !action.disabled;
|
|
673
|
+
nativeControlFocus(button, action.disabled ? 2 : action.active ? 1 : 0);
|
|
384
674
|
if (action.id.startsWith("pixel:") && !action.disabled) {
|
|
385
675
|
button.onMouseOver = () => this.context.emit({ type: "action", action: `hover:${action.id}`, id: this.spec.id });
|
|
386
676
|
button.onMouseOut = () => this.context.emit({ type: "action", action: `hover-end:${action.id}`, id: this.spec.id });
|
|
@@ -394,29 +684,37 @@ var NativeBlockView = class {
|
|
|
394
684
|
toggle() {
|
|
395
685
|
this.collapsed = !this.collapsed;
|
|
396
686
|
if (this.body) this.body.visible = !this.collapsed;
|
|
397
|
-
if (this.title) this.title.content =
|
|
687
|
+
if (this.title) this.title.content = `${this.collapsed ? "▸ " : "· "}${plainText(this.spec.title)}`;
|
|
398
688
|
this.context.emit({ type: "action", action: "toggle-block", id: this.spec.id });
|
|
399
689
|
}
|
|
400
690
|
applyTheme() {
|
|
401
691
|
const colors = this.context.colors;
|
|
402
692
|
this.root.backgroundColor = colors.background;
|
|
403
|
-
if (this.spec.kind === "card") this.root.borderColor =
|
|
693
|
+
if (this.spec.kind === "card") this.root.borderColor = colors.border;
|
|
404
694
|
else this.root.border = false;
|
|
405
695
|
if (this.title) {
|
|
406
|
-
this.title.fg =
|
|
696
|
+
this.title.fg = colors.foreground;
|
|
407
697
|
this.title.bg = colors.background;
|
|
408
698
|
}
|
|
409
699
|
if (this.body) this.body.backgroundColor = colors.background;
|
|
410
700
|
if (this.text) {
|
|
411
|
-
this.text.fg =
|
|
701
|
+
this.text.fg = colors.foreground;
|
|
412
702
|
this.text.bg = colors.background;
|
|
413
703
|
}
|
|
414
|
-
if (this.
|
|
415
|
-
|
|
704
|
+
if (this.progress) {
|
|
705
|
+
this.progress.fg = colors.foreground;
|
|
706
|
+
this.progress.bg = colors.background;
|
|
707
|
+
}
|
|
708
|
+
for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable) {
|
|
709
|
+
action.fg = colors.foreground;
|
|
710
|
+
action.bg = colors.background;
|
|
711
|
+
}
|
|
416
712
|
this.children?.applyTheme();
|
|
713
|
+
this.markdownSections?.applyTheme();
|
|
417
714
|
}
|
|
418
715
|
destroy() {
|
|
419
716
|
this.children?.destroy();
|
|
717
|
+
this.markdownSections?.destroy();
|
|
420
718
|
if (this.terminal) this.context.terminals.delete(this.spec.id);
|
|
421
719
|
this.root.destroyRecursively();
|
|
422
720
|
}
|
|
@@ -476,19 +774,104 @@ var NativeBlockList = class {
|
|
|
476
774
|
|
|
477
775
|
// packages/cli/src/tui/native/components/modals.ts
|
|
478
776
|
import {
|
|
479
|
-
BoxRenderable as
|
|
777
|
+
BoxRenderable as BoxRenderable3,
|
|
480
778
|
InputRenderable,
|
|
481
779
|
ScrollBoxRenderable as ScrollBoxRenderable2,
|
|
482
780
|
TextareaRenderable,
|
|
781
|
+
TextAttributes as TextAttributes4,
|
|
483
782
|
TextRenderable as TextRenderable2
|
|
484
783
|
} from "@opentui/core";
|
|
485
784
|
|
|
486
785
|
// packages/cli/src/tui/native/components/select.ts
|
|
487
786
|
import { SelectRenderable } from "@opentui/core";
|
|
787
|
+
|
|
788
|
+
// packages/cli/src/tui/native/components/scrollbar.ts
|
|
789
|
+
import { TextAttributes as TextAttributes2 } from "@opentui/core";
|
|
790
|
+
function drawDotRail(buffer, x, y, length, horizontal, progress, colors) {
|
|
791
|
+
length = Math.max(0, Math.trunc(length));
|
|
792
|
+
if (!length) return;
|
|
793
|
+
buffer.fillRect(x, y, horizontal ? length : 1, horizontal ? 1 : length, colors.background);
|
|
794
|
+
if (horizontal) buffer.drawText("─".repeat(length), x, y, colors.foreground, colors.background, TextAttributes2.DIM);
|
|
795
|
+
else for (let row = 0; row < length; row++) buffer.drawText("│", x, y + row, colors.foreground, colors.background, TextAttributes2.DIM);
|
|
796
|
+
const position = Math.round(Math.max(0, Math.min(1, progress)) * (length - 1));
|
|
797
|
+
buffer.drawText("•", x + (horizontal ? position : 0), y + (horizontal ? 0 : position), colors.foreground, colors.background, TextAttributes2.BOLD);
|
|
798
|
+
}
|
|
799
|
+
function styleNativeScrollbars(scroll, colors) {
|
|
800
|
+
for (const bar of [scroll.horizontalScrollBar, scroll.verticalScrollBar]) {
|
|
801
|
+
bar.showArrows = false;
|
|
802
|
+
const slider = bar.slider;
|
|
803
|
+
slider.foregroundColor = colors.foreground;
|
|
804
|
+
slider.backgroundColor = colors.background;
|
|
805
|
+
slider.renderAfter = (buffer) => {
|
|
806
|
+
const horizontal = slider.orientation === "horizontal";
|
|
807
|
+
drawDotRail(
|
|
808
|
+
buffer,
|
|
809
|
+
slider.x,
|
|
810
|
+
slider.y,
|
|
811
|
+
horizontal ? slider.width : slider.height,
|
|
812
|
+
horizontal,
|
|
813
|
+
slider.max > slider.min ? (slider.value - slider.min) / (slider.max - slider.min) : 0,
|
|
814
|
+
colors
|
|
815
|
+
);
|
|
816
|
+
};
|
|
817
|
+
let dragging = false;
|
|
818
|
+
const seek = (event) => {
|
|
819
|
+
const horizontal = slider.orientation === "horizontal";
|
|
820
|
+
const length = horizontal ? slider.width : slider.height;
|
|
821
|
+
const offset = horizontal ? event.x - slider.x : event.y - slider.y;
|
|
822
|
+
const progress = length > 1 ? Math.max(0, Math.min(1, offset / (length - 1))) : 0;
|
|
823
|
+
slider.value = slider.min + progress * (slider.max - slider.min);
|
|
824
|
+
event.preventDefault();
|
|
825
|
+
event.stopPropagation();
|
|
826
|
+
};
|
|
827
|
+
slider.onMouseDown = (event) => {
|
|
828
|
+
if (event.button === 0) {
|
|
829
|
+
dragging = true;
|
|
830
|
+
seek(event);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
slider.onMouseDrag = (event) => {
|
|
834
|
+
if (dragging) seek(event);
|
|
835
|
+
};
|
|
836
|
+
slider.onMouseUp = (event) => {
|
|
837
|
+
if (dragging) seek(event);
|
|
838
|
+
dragging = false;
|
|
839
|
+
};
|
|
840
|
+
slider.onMouseDragEnd = () => {
|
|
841
|
+
dragging = false;
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// packages/cli/src/tui/native/components/select.ts
|
|
488
847
|
var NativeSelectRenderable = class extends SelectRenderable {
|
|
489
848
|
onActivate;
|
|
849
|
+
draggingRail = false;
|
|
490
850
|
constructor(renderer2, options) {
|
|
491
851
|
super(renderer2, options);
|
|
852
|
+
const colors = palette({ mode: "system", bg: -1, accent: -1, textPrimary: -1, textDim: -1, boxColor: -1 });
|
|
853
|
+
this.renderAfter = (buffer) => {
|
|
854
|
+
const count = this.visibleCount();
|
|
855
|
+
if (!this.showScrollIndicator || this.options.length <= count) return;
|
|
856
|
+
const offset = Math.max(0, Math.min(this.getSelectedIndex() - Math.floor(count / 2), this.options.length - count));
|
|
857
|
+
drawDotRail(buffer, this.width - 1, 0, this.height, false, offset / (this.options.length - count), colors);
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
visibleCount() {
|
|
861
|
+
return Math.max(1, Math.floor(this.height / (this.showDescription ? 2 : 1)));
|
|
862
|
+
}
|
|
863
|
+
seekRail(event) {
|
|
864
|
+
const progress = this.height > 1 ? Math.max(0, Math.min(1, (event.y - this.y) / (this.height - 1))) : 0;
|
|
865
|
+
const target = Math.round(progress * Math.max(0, this.options.length - 1));
|
|
866
|
+
for (let distance = 0; distance < this.options.length; distance++) {
|
|
867
|
+
const index = [target + distance, target - distance].find((candidate) => candidate >= 0 && candidate < this.options.length && !this.disabled(candidate));
|
|
868
|
+
if (index !== void 0) {
|
|
869
|
+
this.setSelectedIndex(index);
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
event.preventDefault();
|
|
874
|
+
event.stopPropagation();
|
|
492
875
|
}
|
|
493
876
|
disabled(index) {
|
|
494
877
|
const option = this.options[index];
|
|
@@ -517,6 +900,11 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
517
900
|
if (!this.disabled(this.getSelectedIndex())) super.selectCurrent();
|
|
518
901
|
}
|
|
519
902
|
onMouseEvent(event) {
|
|
903
|
+
if ((event.type === "drag" || event.type === "up") && this.draggingRail) {
|
|
904
|
+
this.seekRail(event);
|
|
905
|
+
if (event.type === "up") this.draggingRail = false;
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
520
908
|
if (event.type === "scroll") {
|
|
521
909
|
if (event.scroll?.direction === "up") this.moveUp(1);
|
|
522
910
|
else if (event.scroll?.direction === "down") this.moveDown(1);
|
|
@@ -524,6 +912,11 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
524
912
|
return;
|
|
525
913
|
}
|
|
526
914
|
if (event.type !== "down" || event.button !== 0) return;
|
|
915
|
+
if (this.showScrollIndicator && this.options.length > this.visibleCount() && event.x === this.x + this.width - 1) {
|
|
916
|
+
this.draggingRail = true;
|
|
917
|
+
this.seekRail(event);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
527
920
|
const lines = this.showDescription ? 2 : 1;
|
|
528
921
|
const count = Math.max(1, Math.floor(this.height / lines));
|
|
529
922
|
const start = Math.max(0, Math.min(this.getSelectedIndex() - Math.floor(count / 2), this.options.length - count));
|
|
@@ -536,6 +929,169 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
536
929
|
}
|
|
537
930
|
};
|
|
538
931
|
|
|
932
|
+
// packages/cli/src/tui/native/components/corner-frame.ts
|
|
933
|
+
import { BoxRenderable as BoxRenderable2, CliRenderEvents as CliRenderEvents2, RenderableEvents as RenderableEvents2, TextAttributes as TextAttributes3 } from "@opentui/core";
|
|
934
|
+
function drawCornerFrame(buffer, panel, colors, progress = 1) {
|
|
935
|
+
const { x, y, width, height } = panel;
|
|
936
|
+
if (width <= 0 || height <= 0) return;
|
|
937
|
+
const draw = (text, column, row) => buffer.drawText(text, column, row, colors.foreground, colors.background, TextAttributes3.DIM);
|
|
938
|
+
draw(" ".repeat(width), x, y);
|
|
939
|
+
if (height > 1) draw(" ".repeat(width), x, y + height - 1);
|
|
940
|
+
for (let row = 1; row < height - 1; row++) {
|
|
941
|
+
draw(" ", x, y + row);
|
|
942
|
+
if (width > 1) draw(" ", x + width - 1, y + row);
|
|
943
|
+
}
|
|
944
|
+
if (height === 1) {
|
|
945
|
+
draw("─".repeat(width), x, y);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (width === 1) {
|
|
949
|
+
for (let row = 0; row < height; row++) draw("│", x, y + row);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
const fraction = Math.max(0, Math.min(1, progress));
|
|
953
|
+
const horizontal = Math.round(Math.min(4, Math.max(0, Math.floor((width - 3) / 2))) * fraction);
|
|
954
|
+
const vertical = Math.round(Math.min(2, Math.max(0, Math.floor((height - 3) / 2))) * fraction);
|
|
955
|
+
draw(`┌${"─".repeat(horizontal)}`, x, y);
|
|
956
|
+
draw(`${"─".repeat(horizontal)}┐`, x + width - horizontal - 1, y);
|
|
957
|
+
draw(`└${"─".repeat(horizontal)}`, x, y + height - 1);
|
|
958
|
+
draw(`${"─".repeat(horizontal)}┘`, x + width - horizontal - 1, y + height - 1);
|
|
959
|
+
for (let arm = 1; arm <= vertical; arm++) {
|
|
960
|
+
for (const column of [x, x + width - 1]) {
|
|
961
|
+
draw("│", column, y + arm);
|
|
962
|
+
draw("│", column, y + height - arm - 1);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
var NativeCornerFrame = class {
|
|
967
|
+
constructor(panel, colors, renderer2, container = panel.parent) {
|
|
968
|
+
this.panel = panel;
|
|
969
|
+
this.colors = colors;
|
|
970
|
+
this.renderer = renderer2;
|
|
971
|
+
this.container = container;
|
|
972
|
+
this.chrome = new BoxRenderable2(renderer2, {
|
|
973
|
+
id: `${panel.id}:corners`,
|
|
974
|
+
position: "absolute",
|
|
975
|
+
left: 0,
|
|
976
|
+
top: 0,
|
|
977
|
+
width: 1,
|
|
978
|
+
height: 1,
|
|
979
|
+
backgroundColor: colors.background,
|
|
980
|
+
shouldFill: true,
|
|
981
|
+
zIndex: 0,
|
|
982
|
+
visible: false
|
|
983
|
+
});
|
|
984
|
+
this.chrome.renderAfter = (buffer) => {
|
|
985
|
+
drawCornerFrame(buffer, this.chrome, this.colors, this.progress);
|
|
986
|
+
if (this.progress < 0.1 && this.chrome.width >= 3 && this.chrome.height >= 3)
|
|
987
|
+
buffer.drawText("■", this.chrome.x + Math.floor(this.chrome.width / 2), this.chrome.y + Math.floor(this.chrome.height / 2), this.colors.foreground, this.colors.background);
|
|
988
|
+
this.painted = true;
|
|
989
|
+
};
|
|
990
|
+
(container ?? renderer2.root).add(this.chrome, 0);
|
|
991
|
+
panel.border = false;
|
|
992
|
+
panel.shouldFill = false;
|
|
993
|
+
panel.zIndex = 1;
|
|
994
|
+
panel.opacity = this.reduced ? 1 : 0;
|
|
995
|
+
panel.once(RenderableEvents2.DESTROYED, this.destroy);
|
|
996
|
+
if (this.reduced) {
|
|
997
|
+
this.progress = 1;
|
|
998
|
+
this.phase = "open";
|
|
999
|
+
}
|
|
1000
|
+
renderer2.on(CliRenderEvents2.FRAME, this.onFrame);
|
|
1001
|
+
}
|
|
1002
|
+
panel;
|
|
1003
|
+
colors;
|
|
1004
|
+
renderer;
|
|
1005
|
+
container;
|
|
1006
|
+
chrome;
|
|
1007
|
+
progress = 0;
|
|
1008
|
+
timer;
|
|
1009
|
+
disposed = false;
|
|
1010
|
+
phase = "opening";
|
|
1011
|
+
started = false;
|
|
1012
|
+
painted = false;
|
|
1013
|
+
geometry = "";
|
|
1014
|
+
reduced = process.env["OMNIUS_TUI_REDUCE_MOTION"] === "1" || process.env["REDUCE_MOTION"] === "1";
|
|
1015
|
+
get animating() {
|
|
1016
|
+
return !this.disposed && (this.phase === "opening" || this.phase === "closing");
|
|
1017
|
+
}
|
|
1018
|
+
get closing() {
|
|
1019
|
+
return this.phase === "closing" || this.phase === "closed";
|
|
1020
|
+
}
|
|
1021
|
+
get bounds() {
|
|
1022
|
+
return this.chrome;
|
|
1023
|
+
}
|
|
1024
|
+
setColors(colors) {
|
|
1025
|
+
this.colors = colors;
|
|
1026
|
+
this.chrome.backgroundColor = colors.background;
|
|
1027
|
+
}
|
|
1028
|
+
onFrame = () => {
|
|
1029
|
+
this.layout();
|
|
1030
|
+
if (!this.started && this.painted && this.panel.width > 0 && this.panel.height > 0) {
|
|
1031
|
+
this.started = true;
|
|
1032
|
+
if (this.phase === "opening") this.animate(1, 10);
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
layout() {
|
|
1036
|
+
if (this.disposed || this.panel.width < 1 || this.panel.height < 1) return;
|
|
1037
|
+
const target = this.panel;
|
|
1038
|
+
const width = Math.round(Math.min(5, target.width) + (target.width - Math.min(5, target.width)) * this.progress);
|
|
1039
|
+
const height = Math.round(Math.min(3, target.height) + (target.height - Math.min(3, target.height)) * this.progress);
|
|
1040
|
+
const left = target.x - (this.container?.x ?? 0) + Math.floor((target.width - width) / 2);
|
|
1041
|
+
const top = target.y - (this.container?.y ?? 0) + Math.floor((target.height - height) / 2);
|
|
1042
|
+
const geometry = `${left}:${top}:${width}:${height}`;
|
|
1043
|
+
if (this.geometry === geometry) return;
|
|
1044
|
+
this.geometry = geometry;
|
|
1045
|
+
this.chrome.left = left;
|
|
1046
|
+
this.chrome.top = top;
|
|
1047
|
+
this.chrome.width = width;
|
|
1048
|
+
this.chrome.height = height;
|
|
1049
|
+
this.chrome.visible = true;
|
|
1050
|
+
this.renderer.requestRender();
|
|
1051
|
+
}
|
|
1052
|
+
animate(to, steps, done) {
|
|
1053
|
+
if (this.timer) clearTimeout(this.timer);
|
|
1054
|
+
const from = this.progress;
|
|
1055
|
+
let step = 0;
|
|
1056
|
+
const advance = () => {
|
|
1057
|
+
if (this.disposed) return;
|
|
1058
|
+
const t = ++step / steps;
|
|
1059
|
+
this.progress = from + (to - from) * (t * t * (3 - 2 * t));
|
|
1060
|
+
this.layout();
|
|
1061
|
+
this.renderer.requestRender();
|
|
1062
|
+
if (step < steps) this.timer = setTimeout(advance, 16);
|
|
1063
|
+
else if (to === 1) {
|
|
1064
|
+
this.phase = "open";
|
|
1065
|
+
this.panel.opacity = 1;
|
|
1066
|
+
this.timer = void 0;
|
|
1067
|
+
} else this.timer = setTimeout(() => {
|
|
1068
|
+
this.timer = void 0;
|
|
1069
|
+
this.phase = "closed";
|
|
1070
|
+
done?.();
|
|
1071
|
+
}, 32);
|
|
1072
|
+
};
|
|
1073
|
+
this.timer = setTimeout(advance, 16);
|
|
1074
|
+
}
|
|
1075
|
+
close(done) {
|
|
1076
|
+
if (this.closing || this.disposed) return;
|
|
1077
|
+
this.phase = "closing";
|
|
1078
|
+
this.panel.opacity = 0;
|
|
1079
|
+
if (this.reduced) {
|
|
1080
|
+
this.phase = "closed";
|
|
1081
|
+
done();
|
|
1082
|
+
} else this.animate(0, 8, done);
|
|
1083
|
+
}
|
|
1084
|
+
destroy = () => {
|
|
1085
|
+
if (this.disposed) return;
|
|
1086
|
+
this.disposed = true;
|
|
1087
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
1088
|
+
this.timer = void 0;
|
|
1089
|
+
this.panel.off(RenderableEvents2.DESTROYED, this.destroy);
|
|
1090
|
+
this.renderer.off(CliRenderEvents2.FRAME, this.onFrame);
|
|
1091
|
+
this.chrome.destroyRecursively();
|
|
1092
|
+
};
|
|
1093
|
+
};
|
|
1094
|
+
|
|
539
1095
|
// packages/cli/src/tui/native/components/modals.ts
|
|
540
1096
|
var NativeModalView = class {
|
|
541
1097
|
constructor(context, spec, close2) {
|
|
@@ -543,7 +1099,7 @@ var NativeModalView = class {
|
|
|
543
1099
|
this.close = close2;
|
|
544
1100
|
this.spec = spec;
|
|
545
1101
|
const { renderer: renderer2, colors } = context;
|
|
546
|
-
this.root = new
|
|
1102
|
+
this.root = new BoxRenderable3(renderer2, {
|
|
547
1103
|
id: `modal:${spec.id}`,
|
|
548
1104
|
position: "absolute",
|
|
549
1105
|
top: 0,
|
|
@@ -554,22 +1110,32 @@ var NativeModalView = class {
|
|
|
554
1110
|
justifyContent: "center",
|
|
555
1111
|
alignItems: "center",
|
|
556
1112
|
zIndex: 100,
|
|
557
|
-
backgroundColor:
|
|
1113
|
+
backgroundColor: "transparent",
|
|
1114
|
+
shouldFill: false,
|
|
1115
|
+
onMouseDown: (event) => {
|
|
1116
|
+
const bounds = this.frame.bounds;
|
|
1117
|
+
if (event.button === 0 && (event.x < bounds.x || event.x >= bounds.x + bounds.width || event.y < bounds.y || event.y >= bounds.y + bounds.height)) {
|
|
1118
|
+
this.cancel();
|
|
1119
|
+
event.preventDefault();
|
|
1120
|
+
event.stopPropagation();
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
558
1123
|
});
|
|
559
|
-
this.panel = new
|
|
1124
|
+
this.panel = new BoxRenderable3(renderer2, {
|
|
560
1125
|
id: `modal-panel:${spec.id}`,
|
|
561
1126
|
flexDirection: "column",
|
|
562
1127
|
width: "94%",
|
|
563
1128
|
maxHeight: "94%",
|
|
564
|
-
border:
|
|
565
|
-
borderStyle: "rounded",
|
|
566
|
-
borderColor: colors.border,
|
|
1129
|
+
border: false,
|
|
567
1130
|
backgroundColor: colors.background,
|
|
568
|
-
|
|
1131
|
+
shouldFill: false,
|
|
1132
|
+
paddingX: 2,
|
|
569
1133
|
paddingY: 1,
|
|
1134
|
+
focusable: true,
|
|
570
1135
|
onMouseDown: () => this.focus()
|
|
571
1136
|
});
|
|
572
1137
|
this.root.add(this.panel);
|
|
1138
|
+
this.frame = new NativeCornerFrame(this.panel, colors, renderer2, this.root);
|
|
573
1139
|
this.breadcrumb = this.addText(`modal-breadcrumb:${spec.id}`);
|
|
574
1140
|
this.heading = this.addText(`modal-title:${spec.id}`);
|
|
575
1141
|
this.description = this.addText(`modal-description:${spec.id}`);
|
|
@@ -585,20 +1151,29 @@ var NativeModalView = class {
|
|
|
585
1151
|
viewportCulling: true,
|
|
586
1152
|
contentOptions: { flexDirection: "column" }
|
|
587
1153
|
});
|
|
1154
|
+
styleNativeScrollbars(this.document, colors);
|
|
588
1155
|
this.blocks = new NativeBlockList(this.modalContext(), this.document);
|
|
589
1156
|
this.panel.add(this.document);
|
|
590
1157
|
} else {
|
|
591
|
-
this.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
1158
|
+
this.progress = new TextRenderable2(renderer2, {
|
|
1159
|
+
id: `modal-progress:${spec.id}`,
|
|
1160
|
+
width: "100%",
|
|
1161
|
+
height: 1,
|
|
1162
|
+
content: "",
|
|
1163
|
+
fg: colors.foreground,
|
|
1164
|
+
bg: colors.background,
|
|
1165
|
+
attributes: 2,
|
|
1166
|
+
wrapMode: "none",
|
|
1167
|
+
selectable: false
|
|
1168
|
+
});
|
|
1169
|
+
this.panel.add(this.progress);
|
|
1170
|
+
const progressBlocks = new BoxRenderable3(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
|
|
596
1171
|
this.panel.add(progressBlocks);
|
|
597
1172
|
this.blocks = new NativeBlockList(this.modalContext(), progressBlocks);
|
|
598
1173
|
}
|
|
599
1174
|
this.error = this.addText(`modal-error:${spec.id}`);
|
|
600
1175
|
this.hints = this.addText(`modal-hints:${spec.id}`);
|
|
601
|
-
this.actions = new
|
|
1176
|
+
this.actions = new BoxRenderable3(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", gap: 1, width: "100%", marginTop: 0 });
|
|
602
1177
|
this.panel.add(this.actions);
|
|
603
1178
|
this.update(spec);
|
|
604
1179
|
}
|
|
@@ -606,6 +1181,7 @@ var NativeModalView = class {
|
|
|
606
1181
|
close;
|
|
607
1182
|
root;
|
|
608
1183
|
panel;
|
|
1184
|
+
frame;
|
|
609
1185
|
spec;
|
|
610
1186
|
heading;
|
|
611
1187
|
description;
|
|
@@ -619,11 +1195,11 @@ var NativeModalView = class {
|
|
|
619
1195
|
select;
|
|
620
1196
|
document;
|
|
621
1197
|
blocks;
|
|
622
|
-
|
|
623
|
-
progressValue;
|
|
1198
|
+
progress;
|
|
624
1199
|
filtered = [];
|
|
625
1200
|
filter = "";
|
|
626
1201
|
changing = false;
|
|
1202
|
+
actionsSignature = "";
|
|
627
1203
|
addText(id) {
|
|
628
1204
|
const text = new TextRenderable2(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
|
|
629
1205
|
this.panel.add(text);
|
|
@@ -636,6 +1212,7 @@ var NativeModalView = class {
|
|
|
636
1212
|
width: "100%",
|
|
637
1213
|
value: this.spec.filter ?? "",
|
|
638
1214
|
placeholder: "Filter…",
|
|
1215
|
+
placeholderColor: colors.foreground,
|
|
639
1216
|
textColor: colors.foreground,
|
|
640
1217
|
backgroundColor: colors.background,
|
|
641
1218
|
focusedTextColor: colors.foreground,
|
|
@@ -682,8 +1259,8 @@ var NativeModalView = class {
|
|
|
682
1259
|
focusedTextColor: colors.foreground,
|
|
683
1260
|
focusedBackgroundColor: colors.background,
|
|
684
1261
|
placeholder: this.spec.placeholder ?? "",
|
|
685
|
-
|
|
686
|
-
cursorColor: colors.
|
|
1262
|
+
placeholderColor: colors.foreground,
|
|
1263
|
+
cursorColor: colors.foreground,
|
|
687
1264
|
onContentChange: () => {
|
|
688
1265
|
if (!this.editor || this.changing) return;
|
|
689
1266
|
this.refreshMask();
|
|
@@ -713,14 +1290,14 @@ var NativeModalView = class {
|
|
|
713
1290
|
const spec = this.spec;
|
|
714
1291
|
this.changing = true;
|
|
715
1292
|
try {
|
|
716
|
-
this.heading.content =
|
|
1293
|
+
this.heading.content = plainText(spec.title);
|
|
717
1294
|
this.description.content = styledText(spec.description);
|
|
718
1295
|
this.description.visible = plainText(spec.description).length > 0;
|
|
719
1296
|
this.breadcrumb.content = (spec.breadcrumbs ?? []).join(" › ");
|
|
720
1297
|
this.breadcrumb.visible = (spec.breadcrumbs?.length ?? 0) > 0;
|
|
721
1298
|
this.error.content = spec.validationError ?? "";
|
|
722
1299
|
this.error.visible = Boolean(spec.validationError);
|
|
723
|
-
this.hints.content = spec.hints ??
|
|
1300
|
+
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
1301
|
if (this.filterInput && patch.filter !== void 0 && patch.filter !== old.filter) {
|
|
725
1302
|
this.filter = patch.filter;
|
|
726
1303
|
this.filterInput.setText(patch.filter);
|
|
@@ -729,29 +1306,36 @@ var NativeModalView = class {
|
|
|
729
1306
|
this.refreshMask();
|
|
730
1307
|
this.refreshOptions(patch.selectedIndex);
|
|
731
1308
|
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
|
-
|
|
1309
|
+
if (this.progress) this.progress.content = nativeProgressText(spec.progress, true);
|
|
1310
|
+
const actionsSignature = JSON.stringify(spec.actions ?? []);
|
|
1311
|
+
if (actionsSignature !== this.actionsSignature) {
|
|
1312
|
+
this.actionsSignature = actionsSignature;
|
|
1313
|
+
for (const child of [...this.actions.getChildren()]) child.destroy();
|
|
1314
|
+
for (const action of spec.actions ?? []) {
|
|
1315
|
+
const button = new TextRenderable2(this.context.renderer, {
|
|
1316
|
+
id: `modal-action:${spec.id}:${action.id}`,
|
|
1317
|
+
content: plainText(action.label),
|
|
1318
|
+
fg: this.context.colors.foreground,
|
|
1319
|
+
bg: this.context.colors.background,
|
|
1320
|
+
attributes: action.disabled ? 2 : action.active ? 1 : 0,
|
|
1321
|
+
selectable: false,
|
|
1322
|
+
onMouseDown: (event) => {
|
|
1323
|
+
if (event.button !== 0 || action.disabled) return;
|
|
1324
|
+
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1325
|
+
event.stopPropagation();
|
|
1326
|
+
},
|
|
1327
|
+
onKeyDown: (key) => {
|
|
1328
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
1329
|
+
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1330
|
+
key.preventDefault();
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
button.focusable = !action.disabled;
|
|
1334
|
+
nativeControlFocus(button, action.disabled ? 2 : action.active ? 1 : 0);
|
|
1335
|
+
this.actions.add(button);
|
|
1336
|
+
}
|
|
1337
|
+
this.actions.visible = (spec.actions?.length ?? 0) > 0;
|
|
753
1338
|
}
|
|
754
|
-
this.actions.visible = (spec.actions?.length ?? 0) > 0;
|
|
755
1339
|
} finally {
|
|
756
1340
|
this.changing = false;
|
|
757
1341
|
}
|
|
@@ -790,6 +1374,7 @@ var NativeModalView = class {
|
|
|
790
1374
|
this.context.emit({ type: "modal-action", id: this.spec.id, action, key: current?.item.key, index: current?.index, value });
|
|
791
1375
|
}
|
|
792
1376
|
confirm() {
|
|
1377
|
+
if (this.frame.closing) return;
|
|
793
1378
|
if (this.spec.kind === "progress") {
|
|
794
1379
|
this.emitAction("enter");
|
|
795
1380
|
return;
|
|
@@ -804,6 +1389,7 @@ var NativeModalView = class {
|
|
|
804
1389
|
this.close(this.spec.id, { confirmed: true, key: current?.item.key ?? null, index: current?.index ?? -1, ...value === void 0 ? {} : { value } });
|
|
805
1390
|
}
|
|
806
1391
|
cancel(action = "cancel") {
|
|
1392
|
+
if (this.frame.closing) return;
|
|
807
1393
|
if (this.spec.deferSelection) {
|
|
808
1394
|
this.emitAction(action);
|
|
809
1395
|
return;
|
|
@@ -811,25 +1397,49 @@ var NativeModalView = class {
|
|
|
811
1397
|
this.close(this.spec.id, { confirmed: false, key: this.spec.breadcrumbs?.length ? "__back__" : null, index: this.current()?.index ?? -1 });
|
|
812
1398
|
}
|
|
813
1399
|
focus() {
|
|
1400
|
+
if (this.frame.closing) return;
|
|
814
1401
|
if (this.filterInput) this.filterInput.focus();
|
|
815
1402
|
else if (this.editor && !this.spec.secret) this.editor.focus();
|
|
816
1403
|
else if (this.document) this.document.focus();
|
|
817
1404
|
else this.panel.focus();
|
|
818
1405
|
}
|
|
1406
|
+
nextControl(direction) {
|
|
1407
|
+
const controls = [];
|
|
1408
|
+
const visit = (node) => {
|
|
1409
|
+
if (!node.visible || node.isDestroyed) return;
|
|
1410
|
+
if (node !== this.panel && node.focusable) controls.push(node);
|
|
1411
|
+
for (const child of node.getChildren()) visit(child);
|
|
1412
|
+
};
|
|
1413
|
+
visit(this.panel);
|
|
1414
|
+
if (!controls.length) {
|
|
1415
|
+
this.panel.focus();
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
const focused = this.context.renderer.currentFocusedRenderable;
|
|
1419
|
+
const current = focused ? controls.indexOf(focused) : -1;
|
|
1420
|
+
const next = controls[current < 0 ? direction > 0 ? 0 : controls.length - 1 : (current + direction + controls.length) % controls.length];
|
|
1421
|
+
next.focus();
|
|
1422
|
+
this.document?.scrollChildIntoView(next.id);
|
|
1423
|
+
}
|
|
819
1424
|
handleKey(key) {
|
|
820
1425
|
const consume = () => {
|
|
821
1426
|
key.preventDefault();
|
|
822
1427
|
key.stopPropagation();
|
|
823
1428
|
return true;
|
|
824
1429
|
};
|
|
1430
|
+
if (this.frame.closing) return consume();
|
|
1431
|
+
if (key.eventType === "release") return false;
|
|
825
1432
|
if (key.name === "escape" || key.ctrl && key.name === "c") {
|
|
826
1433
|
this.cancel();
|
|
827
1434
|
return consume();
|
|
828
1435
|
}
|
|
829
1436
|
if (key.name === "tab") {
|
|
830
|
-
this.emitAction(key.shift ? "shift-tab" : "tab");
|
|
1437
|
+
if (this.spec.deferSelection || this.spec.captureKeys) this.emitAction(key.shift ? "shift-tab" : "tab");
|
|
1438
|
+
else this.nextControl(key.shift ? -1 : 1);
|
|
831
1439
|
return consume();
|
|
832
1440
|
}
|
|
1441
|
+
const focusedId = this.context.renderer.currentFocusedRenderable?.id ?? "";
|
|
1442
|
+
if ((key.name === "return" || key.sequence === " ") && /^(?:block-title:|block-action:|modal-action:)/.test(focusedId)) return false;
|
|
833
1443
|
if (this.spec.kind === "select") {
|
|
834
1444
|
if (key.name === "up" || key.name === "down" || key.name === "pageup" || key.name === "pagedown") {
|
|
835
1445
|
const amount = key.name.startsWith("page") ? Math.max(1, Math.floor((this.select?.height ?? 5) / (this.select?.showDescription ? 2 : 1))) : 1;
|
|
@@ -886,6 +1496,11 @@ var NativeModalView = class {
|
|
|
886
1496
|
return false;
|
|
887
1497
|
}
|
|
888
1498
|
handlePaste(event) {
|
|
1499
|
+
if (this.frame.closing) {
|
|
1500
|
+
event.preventDefault();
|
|
1501
|
+
event.stopPropagation();
|
|
1502
|
+
return true;
|
|
1503
|
+
}
|
|
889
1504
|
const text = new TextDecoder().decode(event.bytes);
|
|
890
1505
|
if (this.overBudget(text, this.filterInput?.plainText ?? this.editor?.plainText ?? "")) {
|
|
891
1506
|
event.preventDefault();
|
|
@@ -910,14 +1525,22 @@ var NativeModalView = class {
|
|
|
910
1525
|
}
|
|
911
1526
|
applyTheme() {
|
|
912
1527
|
const colors = this.context.colors;
|
|
913
|
-
this.root.backgroundColor = colors.background;
|
|
914
1528
|
this.panel.backgroundColor = colors.background;
|
|
915
|
-
this.
|
|
916
|
-
this.heading.fg = colors.
|
|
1529
|
+
this.frame.setColors(colors);
|
|
1530
|
+
this.heading.fg = colors.foreground;
|
|
1531
|
+
this.heading.attributes = 1;
|
|
917
1532
|
this.description.fg = colors.foreground;
|
|
918
1533
|
this.breadcrumb.fg = colors.dim;
|
|
919
|
-
this.
|
|
1534
|
+
this.breadcrumb.attributes = 2;
|
|
1535
|
+
this.error.fg = colors.foreground;
|
|
1536
|
+
this.error.attributes = 1;
|
|
920
1537
|
this.hints.fg = colors.dim;
|
|
1538
|
+
this.hints.attributes = 2;
|
|
1539
|
+
if (this.progress) {
|
|
1540
|
+
this.progress.fg = colors.foreground;
|
|
1541
|
+
this.progress.bg = colors.background;
|
|
1542
|
+
}
|
|
1543
|
+
if (this.document) styleNativeScrollbars(this.document, colors);
|
|
921
1544
|
if (this.select) {
|
|
922
1545
|
this.select.backgroundColor = this.select.focusedBackgroundColor = colors.background;
|
|
923
1546
|
this.select.textColor = this.select.focusedTextColor = colors.foreground;
|
|
@@ -929,13 +1552,20 @@ var NativeModalView = class {
|
|
|
929
1552
|
for (const editor of [this.editor, this.filterInput]) if (editor) {
|
|
930
1553
|
editor.backgroundColor = editor.focusedBackgroundColor = colors.background;
|
|
931
1554
|
editor.textColor = editor.focusedTextColor = colors.foreground;
|
|
932
|
-
editor.selectionBg =
|
|
933
|
-
editor.
|
|
1555
|
+
editor.selectionBg = void 0;
|
|
1556
|
+
editor.selectionFg = void 0;
|
|
1557
|
+
editor.cursorColor = colors.foreground;
|
|
1558
|
+
editor.placeholderColor = colors.foreground;
|
|
934
1559
|
}
|
|
935
1560
|
if (this.secretMask) this.secretMask.fg = colors.foreground;
|
|
1561
|
+
for (const action of this.actions.getChildren()) if (action instanceof TextRenderable2) {
|
|
1562
|
+
action.fg = colors.foreground;
|
|
1563
|
+
action.bg = colors.background;
|
|
1564
|
+
}
|
|
936
1565
|
this.blocks?.applyTheme();
|
|
937
1566
|
}
|
|
938
1567
|
destroy() {
|
|
1568
|
+
this.frame.destroy();
|
|
939
1569
|
this.blocks?.destroy();
|
|
940
1570
|
if (this.spec.secret) this.editor?.destroy();
|
|
941
1571
|
this.root.destroyRecursively();
|
|
@@ -945,11 +1575,23 @@ var NativeModalStack = class {
|
|
|
945
1575
|
constructor(context, restoreFocus) {
|
|
946
1576
|
this.context = context;
|
|
947
1577
|
this.restoreFocus = restoreFocus;
|
|
1578
|
+
context.renderer.addPostProcessFn(this.dimUnderlay);
|
|
948
1579
|
}
|
|
949
1580
|
context;
|
|
950
1581
|
restoreFocus;
|
|
951
1582
|
views = [];
|
|
952
1583
|
byId = /* @__PURE__ */ new Map();
|
|
1584
|
+
dimUnderlay = (buffer) => {
|
|
1585
|
+
const panel = this.active?.panel;
|
|
1586
|
+
if (!panel || panel.isDestroyed || !panel.visible) return;
|
|
1587
|
+
const { x, y, width, height } = this.active.frame.bounds;
|
|
1588
|
+
buffer.withBuffers((cells) => {
|
|
1589
|
+
for (let row = 0; row < cells.height; row++) for (let column = 0; column < cells.width; column++) {
|
|
1590
|
+
if (column < x || column >= x + width || row < y || row >= y + height)
|
|
1591
|
+
cells.attributes[row * cells.width + column] |= TextAttributes4.DIM;
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
};
|
|
953
1595
|
get active() {
|
|
954
1596
|
return this.views.at(-1);
|
|
955
1597
|
}
|
|
@@ -973,96 +1615,2468 @@ var NativeModalStack = class {
|
|
|
973
1615
|
close(id, result, notify = false) {
|
|
974
1616
|
const view = this.byId.get(id);
|
|
975
1617
|
if (!view) return;
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
if (
|
|
982
|
-
this.active
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1618
|
+
view.frame.close(() => {
|
|
1619
|
+
const wasActive = view === this.active;
|
|
1620
|
+
view.destroy();
|
|
1621
|
+
this.byId.delete(id);
|
|
1622
|
+
this.views = this.views.filter((candidate) => candidate !== view);
|
|
1623
|
+
if (wasActive) {
|
|
1624
|
+
if (this.active) {
|
|
1625
|
+
this.active.root.visible = true;
|
|
1626
|
+
this.active.focus();
|
|
1627
|
+
} else this.restoreFocus();
|
|
1628
|
+
}
|
|
1629
|
+
if (notify) this.context.emit({ type: "modal-result", id, result });
|
|
1630
|
+
});
|
|
987
1631
|
}
|
|
988
1632
|
applyTheme() {
|
|
989
1633
|
for (const view of this.views) view.applyTheme();
|
|
990
1634
|
}
|
|
991
1635
|
destroy() {
|
|
1636
|
+
this.context.renderer.removePostProcessFn(this.dimUnderlay);
|
|
992
1637
|
for (const view of this.views) view.destroy();
|
|
993
1638
|
this.views = [];
|
|
994
1639
|
this.byId.clear();
|
|
995
1640
|
}
|
|
996
1641
|
};
|
|
997
1642
|
|
|
998
|
-
// packages/cli/src/tui/native/components/
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1643
|
+
// packages/cli/src/tui/native/components/instruments.ts
|
|
1644
|
+
import {
|
|
1645
|
+
BoxRenderable as BoxRenderable4,
|
|
1646
|
+
RGBA as RGBA2,
|
|
1647
|
+
RenderableEvents as RenderableEvents3,
|
|
1648
|
+
ScrollBoxRenderable as ScrollBoxRenderable3,
|
|
1649
|
+
TextAttributes as TextAttributes5,
|
|
1650
|
+
TextRenderable as TextRenderable3
|
|
1651
|
+
} from "@opentui/core";
|
|
1652
|
+
|
|
1653
|
+
// packages/cli/src/tui/terminal-cells.ts
|
|
1654
|
+
var graphemes = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1655
|
+
var extendedPictographic = /\p{Extended_Pictographic}/u;
|
|
1656
|
+
var emojiPresentation = /\p{Emoji_Presentation}/u;
|
|
1657
|
+
var zeroWidthGrapheme = /^[\p{Mark}\p{Cf}\u200c\u200d\ufe0e\ufe0f]+$/u;
|
|
1658
|
+
function escapeEnd(value, start) {
|
|
1659
|
+
if (value[start] !== "\x1B") return start + 1;
|
|
1660
|
+
const kind = value[start + 1];
|
|
1661
|
+
if (kind === "[") {
|
|
1662
|
+
for (let i = start + 2; i < value.length; i++) {
|
|
1663
|
+
const code = value.charCodeAt(i);
|
|
1664
|
+
if (code >= 64 && code <= 126) return i + 1;
|
|
1665
|
+
}
|
|
1666
|
+
return value.length;
|
|
1667
|
+
}
|
|
1668
|
+
if (kind === "]") {
|
|
1669
|
+
for (let i = start + 2; i < value.length; i++) {
|
|
1670
|
+
if (value[i] === "\x07") return i + 1;
|
|
1671
|
+
if (value[i] === "\x1B" && value[i + 1] === "\\") return i + 2;
|
|
1672
|
+
}
|
|
1673
|
+
return value.length;
|
|
1674
|
+
}
|
|
1675
|
+
if (kind === "P" || kind === "X" || kind === "^" || kind === "_") {
|
|
1676
|
+
for (let i = start + 2; i < value.length; i++) {
|
|
1677
|
+
if (value[i] === "\x1B" && value[i + 1] === "\\") return i + 2;
|
|
1678
|
+
}
|
|
1679
|
+
return value.length;
|
|
1680
|
+
}
|
|
1681
|
+
return Math.min(value.length, start + 2);
|
|
1682
|
+
}
|
|
1683
|
+
function stripTerminalSequences(value) {
|
|
1684
|
+
let out = "";
|
|
1685
|
+
for (let i = 0; i < value.length; ) {
|
|
1686
|
+
if (value[i] === "\x1B") {
|
|
1687
|
+
i = escapeEnd(value, i);
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
const code = value.charCodeAt(i);
|
|
1691
|
+
if (code >= 0 && code < 32 || code === 127) {
|
|
1692
|
+
i++;
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
const cp = value.codePointAt(i);
|
|
1696
|
+
out += String.fromCodePoint(cp);
|
|
1697
|
+
i += cp > 65535 ? 2 : 1;
|
|
1698
|
+
}
|
|
1699
|
+
return out;
|
|
1700
|
+
}
|
|
1701
|
+
function normalizeTerminalLine(value) {
|
|
1702
|
+
return value.replace(/\r\n|\r|\n/g, " ").replace(/\t/g, " ");
|
|
1703
|
+
}
|
|
1704
|
+
function isWideCodePoint(cp) {
|
|
1705
|
+
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;
|
|
1706
|
+
}
|
|
1707
|
+
function graphemeCellWidth(value) {
|
|
1708
|
+
if (!value || zeroWidthGrapheme.test(value)) return 0;
|
|
1709
|
+
const cp = value.codePointAt(0);
|
|
1710
|
+
if (cp < 32 || cp >= 127 && cp < 160) return 0;
|
|
1711
|
+
if (emojiPresentation.test(value) || value.includes("️") || value.includes("") && extendedPictographic.test(value) || cp >= 127462 && cp <= 127487) {
|
|
1712
|
+
return 2;
|
|
1713
|
+
}
|
|
1714
|
+
return isWideCodePoint(cp) ? 2 : 1;
|
|
1715
|
+
}
|
|
1716
|
+
function plainGraphemes(value) {
|
|
1717
|
+
return Array.from(
|
|
1718
|
+
graphemes.segment(stripTerminalSequences(value)),
|
|
1719
|
+
(part) => part.segment
|
|
1720
|
+
);
|
|
1721
|
+
}
|
|
1722
|
+
function terminalCellWidth(value) {
|
|
1723
|
+
let width = 0;
|
|
1724
|
+
for (const grapheme of plainGraphemes(normalizeTerminalLine(value))) {
|
|
1725
|
+
width += graphemeCellWidth(grapheme);
|
|
1726
|
+
}
|
|
1727
|
+
return width;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// packages/cli/src/tui/native/components/instrument-format.ts
|
|
1731
|
+
var graphemes2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1732
|
+
function fitInstrumentText(value, cells, marker = "~") {
|
|
1733
|
+
const text = stripTerminalSequences(normalizeTerminalLine(value));
|
|
1734
|
+
const width = Math.max(0, Math.floor(cells));
|
|
1735
|
+
if (terminalCellWidth(text) <= width) return text;
|
|
1736
|
+
const suffix = width > 0 ? marker : "";
|
|
1737
|
+
const budget = Math.max(0, width - terminalCellWidth(suffix));
|
|
1738
|
+
let used = 0, result = "";
|
|
1739
|
+
for (const { segment } of graphemes2.segment(text)) {
|
|
1740
|
+
const size = graphemeCellWidth(segment);
|
|
1741
|
+
if (used + size > budget) break;
|
|
1742
|
+
used += size;
|
|
1743
|
+
result += segment;
|
|
1744
|
+
}
|
|
1745
|
+
return result + suffix;
|
|
1746
|
+
}
|
|
1747
|
+
function formatTelemetryRow(label, value, width, reserved = 12) {
|
|
1748
|
+
const cells = Math.max(0, Math.floor(width));
|
|
1749
|
+
if (!cells) return "";
|
|
1750
|
+
const reading = stripTerminalSequences(normalizeTerminalLine(value));
|
|
1751
|
+
const readingCells = terminalCellWidth(reading);
|
|
1752
|
+
if (readingCells >= cells) return fitInstrumentText(reading, cells);
|
|
1753
|
+
const field = Math.min(cells, Math.max(readingCells, Math.floor(reserved)));
|
|
1754
|
+
const labelCells = Math.max(0, cells - field - 1);
|
|
1755
|
+
const left = fitInstrumentText(label.toUpperCase(), labelCells);
|
|
1756
|
+
const dots = ".".repeat(Math.max(0, cells - field - terminalCellWidth(left)));
|
|
1757
|
+
return left + dots + " ".repeat(Math.max(0, field - readingCells)) + reading;
|
|
1758
|
+
}
|
|
1759
|
+
function instrumentNumber(value, precision = 0, unit = "") {
|
|
1760
|
+
return value === void 0 || !Number.isFinite(value) || value < 0 ? "--" : `${value.toFixed(Math.max(0, Math.min(6, precision)))}${unit}`;
|
|
1761
|
+
}
|
|
1762
|
+
function formatSignalMeter(value, maximum, width = 8) {
|
|
1763
|
+
const cells = Math.max(1, Math.floor(width));
|
|
1764
|
+
if (value === void 0 || maximum === void 0 || !Number.isFinite(value) || !Number.isFinite(maximum) || value < 0 || maximum <= 0)
|
|
1765
|
+
return `[${".".repeat(cells)}] --`;
|
|
1766
|
+
const ratio = Math.max(0, Math.min(1, value / maximum));
|
|
1767
|
+
const filled = Math.round(ratio * cells);
|
|
1768
|
+
return `[${"●".repeat(filled)}${"○".repeat(cells - filled)}] ${Math.round(ratio * 100)}%`;
|
|
1769
|
+
}
|
|
1770
|
+
function formatSectionBanner(title, width, expanded = true) {
|
|
1771
|
+
const cells = Math.max(0, Math.floor(width));
|
|
1772
|
+
const label = fitInstrumentText(
|
|
1773
|
+
`${expanded ? "-" : "+"} ${title.toUpperCase()} `,
|
|
1774
|
+
cells
|
|
1775
|
+
);
|
|
1776
|
+
return label + "─".repeat(Math.max(0, cells - terminalCellWidth(label)));
|
|
1777
|
+
}
|
|
1778
|
+
function instrumentStatus(value) {
|
|
1779
|
+
const status = (value || "unknown").toLowerCase();
|
|
1780
|
+
if (status === "running") return "ACTIVE";
|
|
1781
|
+
if (status === "completed" || status === "succeeded") return "DONE";
|
|
1782
|
+
if (status === "failed" || status === "error") return "ERR";
|
|
1783
|
+
if (status === "waiting") return "STDBY";
|
|
1784
|
+
if (status === "paused") return "PAUSED";
|
|
1785
|
+
if (status === "stopped") return "STOPPED";
|
|
1786
|
+
return status.toUpperCase();
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
// packages/cli/src/tui/native/components/instruments.ts
|
|
1790
|
+
var foreground = RGBA2.defaultForeground();
|
|
1791
|
+
var background = RGBA2.defaultBackground();
|
|
1792
|
+
var SectionBanner = class extends TextRenderable3 {
|
|
1793
|
+
constructor(renderer2, title, id, activate) {
|
|
1794
|
+
super(renderer2, {
|
|
1795
|
+
id,
|
|
1796
|
+
content: `- ${title.toUpperCase()}`,
|
|
1018
1797
|
width: "100%",
|
|
1019
|
-
|
|
1798
|
+
height: 1,
|
|
1020
1799
|
flexShrink: 0,
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
});
|
|
1026
|
-
this.root.add(this.header);
|
|
1027
|
-
const headerRow = new BoxRenderable3(renderer2, { id: "omnius-header-row", width: "100%", height: 1, flexDirection: "row", gap: 2 });
|
|
1028
|
-
this.header.add(headerRow);
|
|
1029
|
-
this.identity = new TextRenderable3(renderer2, {
|
|
1030
|
-
id: "omnius-identity",
|
|
1031
|
-
content: "Omnius",
|
|
1032
|
-
fg: colors.foreground,
|
|
1033
|
-
flexShrink: 1,
|
|
1034
|
-
flexGrow: 1,
|
|
1800
|
+
fg: foreground,
|
|
1801
|
+
bg: background,
|
|
1802
|
+
attributes: TextAttributes5.BOLD,
|
|
1803
|
+
selectable: false,
|
|
1035
1804
|
wrapMode: "none",
|
|
1036
1805
|
truncate: true,
|
|
1037
|
-
|
|
1038
|
-
|
|
1806
|
+
onMouseDown: (event) => {
|
|
1807
|
+
if (event.button === 0) {
|
|
1808
|
+
this.focus();
|
|
1809
|
+
this.activate();
|
|
1810
|
+
event.stopPropagation();
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
1813
|
+
onKeyDown: (key) => {
|
|
1814
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
1815
|
+
this.activate();
|
|
1816
|
+
key.preventDefault();
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1039
1819
|
});
|
|
1040
|
-
|
|
1041
|
-
this.
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
flexShrink: 0,
|
|
1046
|
-
minWidth: 0,
|
|
1047
|
-
height: 1,
|
|
1048
|
-
scrollX: true,
|
|
1049
|
-
scrollY: false,
|
|
1050
|
-
contentOptions: { flexDirection: "row", gap: 2 },
|
|
1051
|
-
verticalScrollbarOptions: { visible: false },
|
|
1052
|
-
horizontalScrollbarOptions: { visible: false }
|
|
1820
|
+
this.activate = activate;
|
|
1821
|
+
this.heading = title;
|
|
1822
|
+
this.focusable = true;
|
|
1823
|
+
this.on(RenderableEvents3.FOCUSED, () => {
|
|
1824
|
+
this.attributes = TextAttributes5.BOLD | TextAttributes5.INVERSE;
|
|
1053
1825
|
});
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1826
|
+
this.on(RenderableEvents3.BLURRED, () => {
|
|
1827
|
+
this.attributes = TextAttributes5.BOLD;
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
activate;
|
|
1831
|
+
expanded = true;
|
|
1832
|
+
heading;
|
|
1833
|
+
onResize(width, height) {
|
|
1834
|
+
super.onResize(width, height);
|
|
1835
|
+
this.refresh();
|
|
1836
|
+
}
|
|
1837
|
+
update(title, expanded = this.expanded) {
|
|
1838
|
+
this.heading = title;
|
|
1839
|
+
this.expanded = expanded;
|
|
1840
|
+
this.refresh();
|
|
1841
|
+
}
|
|
1842
|
+
refresh() {
|
|
1843
|
+
const next = formatSectionBanner(this.heading, this.width, this.expanded);
|
|
1844
|
+
if (this.plainText !== next) this.content = next;
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
var TelemetryRow = class extends TextRenderable3 {
|
|
1848
|
+
constructor(renderer2, label, id, reserved = 12) {
|
|
1849
|
+
super(renderer2, {
|
|
1850
|
+
id,
|
|
1851
|
+
content: "",
|
|
1852
|
+
width: "100%",
|
|
1853
|
+
height: 1,
|
|
1854
|
+
flexShrink: 0,
|
|
1855
|
+
fg: foreground,
|
|
1856
|
+
bg: background,
|
|
1857
|
+
wrapMode: "none",
|
|
1858
|
+
truncate: true
|
|
1859
|
+
});
|
|
1860
|
+
this.reserved = reserved;
|
|
1861
|
+
this.label = label;
|
|
1862
|
+
}
|
|
1863
|
+
reserved;
|
|
1864
|
+
label;
|
|
1865
|
+
value = "--";
|
|
1866
|
+
onResize(width, height) {
|
|
1867
|
+
super.onResize(width, height);
|
|
1868
|
+
this.refresh();
|
|
1869
|
+
}
|
|
1870
|
+
update(value, label = this.label) {
|
|
1871
|
+
this.label = label;
|
|
1872
|
+
this.value = value;
|
|
1873
|
+
this.refresh();
|
|
1874
|
+
}
|
|
1875
|
+
refresh() {
|
|
1876
|
+
const next = formatTelemetryRow(
|
|
1877
|
+
this.label,
|
|
1878
|
+
this.value,
|
|
1879
|
+
this.width,
|
|
1880
|
+
this.reserved
|
|
1881
|
+
);
|
|
1882
|
+
if (this.plainText !== next) this.content = next;
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1885
|
+
var SignalMeter = class extends TelemetryRow {
|
|
1886
|
+
constructor(renderer2, label, id) {
|
|
1887
|
+
super(renderer2, label, id, 18);
|
|
1888
|
+
}
|
|
1889
|
+
updateReading(value, maximum) {
|
|
1890
|
+
this.update(formatSignalMeter(value, maximum));
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
var InstrumentDivider = class extends TextRenderable3 {
|
|
1894
|
+
constructor(renderer2, actions) {
|
|
1895
|
+
super(renderer2, {
|
|
1896
|
+
id: "omnius-instrument-divider",
|
|
1897
|
+
width: 1,
|
|
1898
|
+
minWidth: 1,
|
|
1899
|
+
maxWidth: 1,
|
|
1900
|
+
height: "100%",
|
|
1901
|
+
flexGrow: 0,
|
|
1902
|
+
flexShrink: 0,
|
|
1903
|
+
fg: foreground,
|
|
1904
|
+
bg: background,
|
|
1905
|
+
selectable: false,
|
|
1906
|
+
wrapMode: "none",
|
|
1907
|
+
attributes: TextAttributes5.DIM,
|
|
1908
|
+
onMouseDown: (event) => {
|
|
1909
|
+
if (event.button !== 0) return;
|
|
1910
|
+
this.held = true;
|
|
1911
|
+
this.moved = false;
|
|
1912
|
+
this.startX = event.x;
|
|
1913
|
+
this.actions.start?.();
|
|
1914
|
+
this.attributes = TextAttributes5.BOLD;
|
|
1915
|
+
event.preventDefault();
|
|
1916
|
+
event.stopPropagation();
|
|
1917
|
+
},
|
|
1918
|
+
onMouseDrag: (event) => {
|
|
1919
|
+
this.handleDrag(event);
|
|
1920
|
+
},
|
|
1921
|
+
onMouseUp: (event) => {
|
|
1922
|
+
this.handleRelease(event);
|
|
1923
|
+
},
|
|
1924
|
+
onMouseDragEnd: (event) => {
|
|
1925
|
+
this.handleRelease(event);
|
|
1926
|
+
},
|
|
1927
|
+
onMouseOver: () => {
|
|
1928
|
+
this.attributes = TextAttributes5.BOLD;
|
|
1929
|
+
},
|
|
1930
|
+
onMouseOut: () => {
|
|
1931
|
+
if (!this.held) this.attributes = this.focused ? TextAttributes5.INVERSE : TextAttributes5.DIM;
|
|
1932
|
+
},
|
|
1933
|
+
onKeyDown: (key) => {
|
|
1934
|
+
if (key.eventType === "release") return;
|
|
1935
|
+
if (key.name === "return" || key.sequence === " ") this.actions.toggle();
|
|
1936
|
+
else if (key.name === "left" || key.name === "right") this.actions.resize(this.x + (key.name === "left" ? -1 : 1) * (key.shift ? 8 : 2));
|
|
1937
|
+
else return;
|
|
1938
|
+
key.preventDefault();
|
|
1939
|
+
key.stopPropagation();
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
this.actions = actions;
|
|
1943
|
+
this.focusable = true;
|
|
1944
|
+
this.on(RenderableEvents3.FOCUSED, () => {
|
|
1945
|
+
this.attributes = TextAttributes5.INVERSE;
|
|
1946
|
+
});
|
|
1947
|
+
this.on(RenderableEvents3.BLURRED, () => {
|
|
1948
|
+
this.attributes = TextAttributes5.DIM;
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
actions;
|
|
1952
|
+
collapsed = false;
|
|
1953
|
+
held = false;
|
|
1954
|
+
moved = false;
|
|
1955
|
+
startX = 0;
|
|
1956
|
+
setCollapsed(collapsed) {
|
|
1957
|
+
this.collapsed = collapsed;
|
|
1958
|
+
this.refresh();
|
|
1959
|
+
}
|
|
1960
|
+
handleDrag(event) {
|
|
1961
|
+
if (!this.held) return;
|
|
1962
|
+
if (event.x !== this.startX) this.moved = true;
|
|
1963
|
+
if (this.moved) this.actions.resize(event.x);
|
|
1964
|
+
event.preventDefault();
|
|
1965
|
+
event.stopPropagation();
|
|
1966
|
+
}
|
|
1967
|
+
handleRelease(event) {
|
|
1968
|
+
if (!this.held) return;
|
|
1969
|
+
this.release(event.x);
|
|
1970
|
+
event.preventDefault();
|
|
1971
|
+
event.stopPropagation();
|
|
1972
|
+
}
|
|
1973
|
+
onResize(width, height) {
|
|
1974
|
+
super.onResize(width, height);
|
|
1975
|
+
this.refresh();
|
|
1976
|
+
}
|
|
1977
|
+
refresh() {
|
|
1978
|
+
const rows = Array.from({ length: Math.max(0, this.height) }, (_, row) => row === Math.floor(this.height / 2) ? this.collapsed ? "‹" : "›" : "│");
|
|
1979
|
+
const text = rows.join("\n");
|
|
1980
|
+
if (this.plainText !== text) this.content = text;
|
|
1981
|
+
}
|
|
1982
|
+
release(column) {
|
|
1983
|
+
if (this.moved) this.actions.resize(column);
|
|
1984
|
+
else this.actions.toggle();
|
|
1985
|
+
this.held = false;
|
|
1986
|
+
this.actions.finish?.();
|
|
1987
|
+
this.attributes = this.focused ? TextAttributes5.INVERSE : TextAttributes5.DIM;
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
var DeviceMonitor = class extends BoxRenderable4 {
|
|
1991
|
+
constructor(renderer2, action) {
|
|
1992
|
+
super(renderer2, { id: "omnius-devices", width: "100%", height: 3, minHeight: 3, maxHeight: "60%", flexShrink: 1, flexDirection: "column", visible: false });
|
|
1993
|
+
this.action = action;
|
|
1994
|
+
this.heading = new SectionBanner(renderer2, "DEVICES", "omnius-devices:heading", () => this.toggle());
|
|
1995
|
+
this.heading.update("DEVICES", false);
|
|
1996
|
+
this.add(this.heading);
|
|
1997
|
+
this.summary = new TextRenderable3(renderer2, { id: "omnius-devices:summary", content: "--", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
|
|
1998
|
+
this.add(this.summary);
|
|
1999
|
+
this.scroll = new ScrollBoxRenderable3(renderer2, {
|
|
2000
|
+
id: "omnius-devices:scroll",
|
|
2001
|
+
width: "100%",
|
|
2002
|
+
flexGrow: 1,
|
|
2003
|
+
flexShrink: 1,
|
|
2004
|
+
minHeight: 0,
|
|
2005
|
+
visible: false,
|
|
2006
|
+
scrollY: true,
|
|
2007
|
+
scrollX: false,
|
|
2008
|
+
viewportCulling: true,
|
|
2009
|
+
horizontalScrollbarOptions: { visible: false },
|
|
2010
|
+
verticalScrollbarOptions: { showArrows: false },
|
|
2011
|
+
contentOptions: { flexDirection: "column" }
|
|
2012
|
+
});
|
|
2013
|
+
this.add(this.scroll);
|
|
2014
|
+
this.notes = new TextRenderable3(renderer2, {
|
|
2015
|
+
id: "omnius-devices:notes",
|
|
2016
|
+
content: "",
|
|
2017
|
+
width: "100%",
|
|
2018
|
+
height: 1,
|
|
2019
|
+
flexShrink: 0,
|
|
2020
|
+
fg: foreground,
|
|
2021
|
+
bg: background,
|
|
2022
|
+
wrapMode: "none",
|
|
2023
|
+
truncate: true
|
|
2024
|
+
});
|
|
2025
|
+
this.scroll.add(this.notes);
|
|
2026
|
+
const controls = new BoxRenderable4(renderer2, { id: "omnius-devices:controls", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2 });
|
|
2027
|
+
this.add(controls);
|
|
2028
|
+
this.policy = this.button(renderer2, "omnius-devices:policy", "GPU POLICY", () => {
|
|
2029
|
+
if (this.canSelect()) this.action?.("/gpu");
|
|
2030
|
+
});
|
|
2031
|
+
this.status = this.button(renderer2, "omnius-devices:status", "STATUS", () => this.action?.("/gpu status"));
|
|
2032
|
+
controls.add(this.policy);
|
|
2033
|
+
controls.add(this.status);
|
|
2034
|
+
}
|
|
2035
|
+
action;
|
|
2036
|
+
heading;
|
|
2037
|
+
scroll;
|
|
2038
|
+
summary;
|
|
2039
|
+
policy;
|
|
2040
|
+
status;
|
|
2041
|
+
notes;
|
|
2042
|
+
expanded = false;
|
|
2043
|
+
snapshot;
|
|
2044
|
+
rows = /* @__PURE__ */ new Map();
|
|
2045
|
+
button(renderer2, id, label, activate) {
|
|
2046
|
+
const button = new TextRenderable3(renderer2, {
|
|
2047
|
+
id,
|
|
2048
|
+
content: label,
|
|
2049
|
+
height: 1,
|
|
2050
|
+
flexShrink: 0,
|
|
2051
|
+
fg: foreground,
|
|
2052
|
+
bg: background,
|
|
2053
|
+
selectable: false,
|
|
2054
|
+
wrapMode: "none",
|
|
2055
|
+
truncate: true,
|
|
2056
|
+
onMouseDown: (event) => {
|
|
2057
|
+
if (event.button === 0 && button.focusable) {
|
|
2058
|
+
button.focus();
|
|
2059
|
+
activate();
|
|
2060
|
+
event.preventDefault();
|
|
2061
|
+
event.stopPropagation();
|
|
2062
|
+
}
|
|
2063
|
+
},
|
|
2064
|
+
onKeyDown: (key) => {
|
|
2065
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ") && button.focusable) {
|
|
2066
|
+
activate();
|
|
2067
|
+
key.preventDefault();
|
|
2068
|
+
key.stopPropagation();
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
button.focusable = Boolean(this.action);
|
|
2073
|
+
button.on(RenderableEvents3.FOCUSED, () => {
|
|
2074
|
+
button.attributes = TextAttributes5.INVERSE;
|
|
2075
|
+
});
|
|
2076
|
+
button.on(RenderableEvents3.BLURRED, () => {
|
|
2077
|
+
button.attributes = button.focusable ? 0 : TextAttributes5.DIM;
|
|
2078
|
+
});
|
|
2079
|
+
return button;
|
|
2080
|
+
}
|
|
2081
|
+
canSelect() {
|
|
2082
|
+
return Boolean(this.action && this.snapshot?.selectionAvailable && !this.snapshot.selectionLocked);
|
|
2083
|
+
}
|
|
2084
|
+
canSelectDevice(device) {
|
|
2085
|
+
return this.canSelect() && device.eligible === true && !device.unifiedMemory && /^GPU-[\w-]+$/.test(device.uuid);
|
|
2086
|
+
}
|
|
2087
|
+
update(snapshot) {
|
|
2088
|
+
this.snapshot = snapshot;
|
|
2089
|
+
this.visible = Boolean(snapshot);
|
|
2090
|
+
const allowed = this.canSelect();
|
|
2091
|
+
if (this.policy.focused && !allowed) this.heading.focus();
|
|
2092
|
+
this.policy.focusable = allowed;
|
|
2093
|
+
this.policy.content = snapshot?.selectionLocked ? "POLICY LOCKED" : "GPU POLICY";
|
|
2094
|
+
this.policy.attributes = allowed ? this.policy.focused ? TextAttributes5.INVERSE : 0 : TextAttributes5.DIM;
|
|
2095
|
+
const keep = new Set(snapshot?.devices.map((device) => device.uuid) ?? []);
|
|
2096
|
+
for (const [uuid, row] of this.rows) if (!keep.has(uuid)) {
|
|
2097
|
+
if (row.title.focused) this.heading.focus();
|
|
2098
|
+
row.root.destroyRecursively();
|
|
2099
|
+
this.rows.delete(uuid);
|
|
2100
|
+
}
|
|
2101
|
+
for (const device of snapshot?.devices ?? []) {
|
|
2102
|
+
let row = this.rows.get(device.uuid);
|
|
2103
|
+
if (!row) {
|
|
2104
|
+
const root = new BoxRenderable4(this._ctx, {
|
|
2105
|
+
id: `omnius-device:${device.uuid}`,
|
|
2106
|
+
width: "100%",
|
|
2107
|
+
flexDirection: "column",
|
|
2108
|
+
flexShrink: 0,
|
|
2109
|
+
onSizeChange: () => {
|
|
2110
|
+
if (!row) return;
|
|
2111
|
+
for (const child of [row.title, row.load, row.memory, row.reason]) child.width = row.root.width;
|
|
2112
|
+
this.refresh();
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
const title = this.button(this._ctx, `${root.id}:select`, "", () => {
|
|
2116
|
+
const current = this.snapshot?.devices.find((item) => item.uuid === device.uuid);
|
|
2117
|
+
if (current && this.canSelectDevice(current)) this.action?.(`/gpu select ${current.uuid}`);
|
|
2118
|
+
});
|
|
2119
|
+
title.width = "100%";
|
|
2120
|
+
title.on(RenderableEvents3.FOCUSED, () => this.scroll.scrollChildIntoView(root.id));
|
|
2121
|
+
const load = new TelemetryRow(this._ctx, "LOAD", `${root.id}:load`, 16);
|
|
2122
|
+
const memory = new TelemetryRow(this._ctx, "VRAM", `${root.id}:memory`, 16);
|
|
2123
|
+
const reason = new TextRenderable3(this._ctx, {
|
|
2124
|
+
id: `${root.id}:reason`,
|
|
2125
|
+
content: "",
|
|
2126
|
+
width: "100%",
|
|
2127
|
+
height: 1,
|
|
2128
|
+
fg: foreground,
|
|
2129
|
+
bg: background,
|
|
2130
|
+
attributes: TextAttributes5.DIM,
|
|
2131
|
+
wrapMode: "none",
|
|
2132
|
+
truncate: true,
|
|
2133
|
+
visible: false
|
|
2134
|
+
});
|
|
2135
|
+
root.add(title);
|
|
2136
|
+
root.add(load);
|
|
2137
|
+
root.add(memory);
|
|
2138
|
+
root.add(reason);
|
|
2139
|
+
this.scroll.add(root);
|
|
2140
|
+
row = { root, title, load, memory, reason };
|
|
2141
|
+
this.rows.set(device.uuid, row);
|
|
2142
|
+
}
|
|
2143
|
+
const selectable = this.canSelectDevice(device);
|
|
2144
|
+
if (row.title.focused && !selectable) this.heading.focus();
|
|
2145
|
+
row.title.focusable = selectable;
|
|
2146
|
+
row.title.attributes = row.title.focused ? TextAttributes5.INVERSE : device.preferred ? TextAttributes5.BOLD : selectable ? 0 : TextAttributes5.DIM;
|
|
2147
|
+
row.load.update(formatSignalMeter(device.utilization, 100, 6));
|
|
2148
|
+
const used = device.memoryUsedMB === void 0 ? "--" : instrumentNumber(device.memoryUsedMB / 1024, 1);
|
|
2149
|
+
const total = device.memoryTotalMB === void 0 || device.memoryTotalMB <= 0 ? "--" : instrumentNumber(device.memoryTotalMB / 1024, 1);
|
|
2150
|
+
row.memory.update(`${used}/${total} GiB`, device.unifiedMemory ? "UMA" : "VRAM");
|
|
2151
|
+
row.reason.content = device.policyReason ?? "";
|
|
2152
|
+
row.reason.visible = Boolean(device.policyReason);
|
|
2153
|
+
}
|
|
2154
|
+
this.refresh();
|
|
2155
|
+
}
|
|
2156
|
+
toggle() {
|
|
2157
|
+
this.expanded = !this.expanded;
|
|
2158
|
+
this.scroll.visible = this.expanded;
|
|
2159
|
+
this.minHeight = this.expanded ? 4 : 3;
|
|
2160
|
+
this.scroll.minHeight = this.expanded ? 1 : 0;
|
|
2161
|
+
this.height = this.expanded ? Math.min(12, Math.max(5, Math.floor(this._ctx.terminalHeight / 3))) : 3;
|
|
2162
|
+
this.refresh();
|
|
2163
|
+
}
|
|
2164
|
+
onResize(width, height) {
|
|
2165
|
+
super.onResize(width, height);
|
|
2166
|
+
this.refresh();
|
|
2167
|
+
}
|
|
2168
|
+
refresh() {
|
|
2169
|
+
if (!this.heading) return;
|
|
2170
|
+
const data = this.snapshot, devices = data?.devices ?? [];
|
|
2171
|
+
this.heading.update(data?.source === "jtop" ? "DEVICES / JETSON" : "DEVICES", this.expanded);
|
|
2172
|
+
const measured = devices.map((device) => device.utilization).filter((value) => value !== void 0 && Number.isFinite(value) && value >= 0);
|
|
2173
|
+
const on = devices.filter((device) => device.enabled === true).length;
|
|
2174
|
+
const policyKnown = devices.every((device) => device.enabled !== void 0);
|
|
2175
|
+
this.summary.content = !devices.length ? data?.source === "unavailable" ? "Telemetry unavailable" : "No local GPUs reported" : `${devices.length} GPU · ${policyKnown ? on : "--"} ON · ${measured.length === devices.length ? Math.round(Math.max(...measured)) : "--"}% PEAK`;
|
|
2176
|
+
this.notes.content = [
|
|
2177
|
+
data?.local === false ? "REMOTE" : void 0,
|
|
2178
|
+
data ? `${data.source.toUpperCase()} · ${data.policy.toUpperCase()} · ${data.broker.toUpperCase()}` : void 0,
|
|
2179
|
+
data?.warnings.length ? `${data.warnings.length} notices` : void 0
|
|
2180
|
+
].filter(Boolean).join(" · ");
|
|
2181
|
+
for (const device of devices) {
|
|
2182
|
+
const row = this.rows.get(device.uuid);
|
|
2183
|
+
if (!row) continue;
|
|
2184
|
+
const state = device.enabled === void 0 ? "--" : device.enabled ? "ON" : "OFF";
|
|
2185
|
+
row.title.content = formatTelemetryRow(`GPU ${device.index} ${device.name}`, `${state}${device.preferred ? " *" : ""}`, row.root.width, 6);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
focusTargets() {
|
|
2189
|
+
return !this.visible ? [] : [
|
|
2190
|
+
this.heading,
|
|
2191
|
+
...this.policy.focusable ? [this.policy] : [],
|
|
2192
|
+
...this.status.focusable ? [this.status] : [],
|
|
2193
|
+
...this.expanded ? [...this.rows.values()].map((row) => row.title).filter((title) => title.focusable) : []
|
|
2194
|
+
];
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
var ChannelMap = class extends BoxRenderable4 {
|
|
2198
|
+
constructor(renderer2, id, inspect, reveal) {
|
|
2199
|
+
super(renderer2, {
|
|
2200
|
+
id,
|
|
2201
|
+
width: "100%",
|
|
2202
|
+
flexDirection: "column",
|
|
2203
|
+
flexShrink: 0
|
|
2204
|
+
});
|
|
2205
|
+
this.inspect = inspect;
|
|
2206
|
+
this.reveal = reveal;
|
|
2207
|
+
}
|
|
2208
|
+
inspect;
|
|
2209
|
+
reveal;
|
|
2210
|
+
rows = /* @__PURE__ */ new Map();
|
|
2211
|
+
channels = /* @__PURE__ */ new Map();
|
|
2212
|
+
agents = [];
|
|
2213
|
+
onResize(width, height) {
|
|
2214
|
+
super.onResize(width, height);
|
|
2215
|
+
this.refresh();
|
|
2216
|
+
}
|
|
2217
|
+
update(agents) {
|
|
2218
|
+
this.agents = agents;
|
|
2219
|
+
const keep = new Set(agents.map((agent) => agent.id));
|
|
2220
|
+
for (const [id, row] of this.rows)
|
|
2221
|
+
if (!keep.has(id)) {
|
|
2222
|
+
row.destroyRecursively();
|
|
2223
|
+
this.rows.delete(id);
|
|
2224
|
+
}
|
|
2225
|
+
for (const agent of agents) {
|
|
2226
|
+
if (!this.channels.has(agent.id))
|
|
2227
|
+
this.channels.set(agent.id, this.channels.size + 1);
|
|
2228
|
+
if (!this.rows.has(agent.id)) {
|
|
2229
|
+
const row = new TextRenderable3(this._ctx, {
|
|
2230
|
+
id: `${this.id}:channel:${agent.id}`,
|
|
2231
|
+
content: "",
|
|
2232
|
+
width: "100%",
|
|
2233
|
+
height: 1,
|
|
2234
|
+
flexShrink: 0,
|
|
2235
|
+
fg: foreground,
|
|
2236
|
+
bg: background,
|
|
2237
|
+
wrapMode: "none",
|
|
2238
|
+
truncate: true,
|
|
2239
|
+
selectable: false,
|
|
2240
|
+
onMouseDown: (event) => {
|
|
2241
|
+
if (event.button === 0) {
|
|
2242
|
+
row.focus();
|
|
2243
|
+
this.inspect?.(agent.id);
|
|
2244
|
+
event.stopPropagation();
|
|
2245
|
+
}
|
|
2246
|
+
},
|
|
2247
|
+
onKeyDown: (key) => {
|
|
2248
|
+
if (key.eventType === "release") return;
|
|
2249
|
+
if (key.name === "return" || key.sequence === " ") {
|
|
2250
|
+
this.inspect?.(agent.id);
|
|
2251
|
+
key.preventDefault();
|
|
2252
|
+
} else if (key.name === "down" || key.name === "up") {
|
|
2253
|
+
const targets = this.focusTargets();
|
|
2254
|
+
const index = targets.indexOf(row), delta = key.name === "down" ? 1 : -1;
|
|
2255
|
+
targets[(index + delta + targets.length) % targets.length]?.focus();
|
|
2256
|
+
key.preventDefault();
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2260
|
+
row.focusable = !!this.inspect;
|
|
2261
|
+
row.on(RenderableEvents3.FOCUSED, () => {
|
|
2262
|
+
row.attributes = TextAttributes5.INVERSE;
|
|
2263
|
+
this.reveal?.(row);
|
|
2264
|
+
});
|
|
2265
|
+
row.on(RenderableEvents3.BLURRED, () => {
|
|
2266
|
+
row.attributes = 0;
|
|
2267
|
+
});
|
|
2268
|
+
this.rows.set(agent.id, row);
|
|
2269
|
+
this.add(row);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
this.refresh();
|
|
2273
|
+
}
|
|
2274
|
+
focusTargets() {
|
|
2275
|
+
return this.agents.flatMap((agent) => {
|
|
2276
|
+
const row = this.rows.get(agent.id);
|
|
2277
|
+
return row?.focusable ? [row] : [];
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
refresh() {
|
|
2281
|
+
for (const agent of this.agents) {
|
|
2282
|
+
const row = this.rows.get(agent.id);
|
|
2283
|
+
if (!row) continue;
|
|
2284
|
+
const channel = String(this.channels.get(agent.id)).padStart(2, "0");
|
|
2285
|
+
const next = formatTelemetryRow(
|
|
2286
|
+
`${agent.selected ? ">" : " "}${channel} ${agent.label}`,
|
|
2287
|
+
instrumentStatus(agent.status),
|
|
2288
|
+
this.width,
|
|
2289
|
+
7
|
|
2290
|
+
);
|
|
2291
|
+
if (row.plainText !== next) row.content = next;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
var InstrumentRail = class extends BoxRenderable4 {
|
|
2296
|
+
scroll;
|
|
2297
|
+
inferenceHeader;
|
|
2298
|
+
sessionHeader;
|
|
2299
|
+
channels;
|
|
2300
|
+
devices;
|
|
2301
|
+
inference;
|
|
2302
|
+
session;
|
|
2303
|
+
model;
|
|
2304
|
+
rows = /* @__PURE__ */ new Map();
|
|
2305
|
+
contextMeter;
|
|
2306
|
+
contextHeader;
|
|
2307
|
+
contextDetails;
|
|
2308
|
+
constructor(renderer2, options = {}) {
|
|
2309
|
+
const id = options.id ?? "omnius-instruments";
|
|
2310
|
+
super(renderer2, {
|
|
2311
|
+
id,
|
|
2312
|
+
width: "100%",
|
|
2313
|
+
height: "100%",
|
|
2314
|
+
minWidth: 0,
|
|
2315
|
+
minHeight: 1,
|
|
2316
|
+
flexDirection: "column",
|
|
2317
|
+
flexGrow: 1,
|
|
2318
|
+
flexShrink: 1
|
|
2319
|
+
});
|
|
2320
|
+
this.scroll = new ScrollBoxRenderable3(renderer2, {
|
|
2321
|
+
id: `${id}:scroll`,
|
|
2322
|
+
width: "100%",
|
|
2323
|
+
flexGrow: 1,
|
|
2324
|
+
minHeight: 1,
|
|
2325
|
+
minWidth: 0,
|
|
2326
|
+
scrollY: true,
|
|
2327
|
+
scrollX: false,
|
|
2328
|
+
viewportCulling: true,
|
|
2329
|
+
verticalScrollbarOptions: { showArrows: false },
|
|
2330
|
+
horizontalScrollbarOptions: { visible: false },
|
|
2331
|
+
contentOptions: { flexDirection: "column", paddingRight: 1 }
|
|
2332
|
+
});
|
|
2333
|
+
this.add(this.scroll);
|
|
2334
|
+
this.inferenceHeader = new SectionBanner(
|
|
2335
|
+
renderer2,
|
|
2336
|
+
"INFERENCE",
|
|
2337
|
+
`${id}:inference`,
|
|
2338
|
+
() => this.toggleSection("inference")
|
|
2339
|
+
);
|
|
2340
|
+
this.inferenceHeader.on(
|
|
2341
|
+
RenderableEvents3.FOCUSED,
|
|
2342
|
+
() => this.reveal(this.inferenceHeader)
|
|
2343
|
+
);
|
|
2344
|
+
this.scroll.add(this.inferenceHeader);
|
|
2345
|
+
this.inference = new BoxRenderable4(renderer2, {
|
|
2346
|
+
id: `${id}:inference-body`,
|
|
2347
|
+
width: "100%",
|
|
2348
|
+
flexDirection: "column",
|
|
2349
|
+
flexShrink: 0
|
|
2350
|
+
});
|
|
2351
|
+
this.scroll.add(this.inference);
|
|
2352
|
+
const addRow = (parent, key, label, reserved = 12) => {
|
|
2353
|
+
const row = new TelemetryRow(renderer2, label, `${id}:${key}`, reserved);
|
|
2354
|
+
this.rows.set(key, row);
|
|
2355
|
+
parent.add(row);
|
|
2356
|
+
return row;
|
|
2357
|
+
};
|
|
2358
|
+
addRow(this.inference, "route", "ROUTE");
|
|
2359
|
+
addRow(this.inference, "backend", "BACKEND");
|
|
2360
|
+
this.model = new TextRenderable3(renderer2, {
|
|
2361
|
+
id: `${id}:model`,
|
|
2362
|
+
content: "MODEL --",
|
|
2363
|
+
width: "100%",
|
|
2364
|
+
height: 1,
|
|
2365
|
+
fg: foreground,
|
|
2366
|
+
bg: background,
|
|
2367
|
+
wrapMode: "none",
|
|
2368
|
+
truncate: true,
|
|
2369
|
+
flexShrink: 0
|
|
2370
|
+
});
|
|
2371
|
+
this.inference.add(this.model);
|
|
2372
|
+
addRow(this.inference, "input", "IN TOK");
|
|
2373
|
+
addRow(this.inference, "output", "OUT TOK");
|
|
2374
|
+
addRow(this.inference, "speed", "RATE");
|
|
2375
|
+
addRow(this.inference, "context", "CTX TOK", 20);
|
|
2376
|
+
this.contextMeter = new SignalMeter(
|
|
2377
|
+
renderer2,
|
|
2378
|
+
"CTX LOAD",
|
|
2379
|
+
`${id}:context-load`
|
|
2380
|
+
);
|
|
2381
|
+
this.inference.add(this.contextMeter);
|
|
2382
|
+
addRow(this.inference, "state", "STATE");
|
|
2383
|
+
this.contextHeader = new SectionBanner(renderer2, "CONTEXT DETAILS", `${id}:context-details`, () => {
|
|
2384
|
+
this.contextDetails.visible = !this.contextDetails.visible;
|
|
2385
|
+
this.contextHeader.update("CONTEXT DETAILS", this.contextDetails.visible);
|
|
2386
|
+
});
|
|
2387
|
+
this.contextHeader.on(RenderableEvents3.FOCUSED, () => this.reveal(this.contextHeader));
|
|
2388
|
+
this.inference.add(this.contextHeader);
|
|
2389
|
+
this.contextDetails = new BoxRenderable4(renderer2, { id: `${id}:context-detail-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
|
|
2390
|
+
this.inference.add(this.contextDetails);
|
|
2391
|
+
this.contextHeader.update("CONTEXT DETAILS", false);
|
|
2392
|
+
addRow(this.contextDetails, "messages", "MESSAGES");
|
|
2393
|
+
addRow(this.contextDetails, "headroom", "HEADROOM");
|
|
2394
|
+
addRow(this.contextDetails, "context-tools", "TOOL CALLS");
|
|
2395
|
+
addRow(this.contextDetails, "adversary", "ADVERSARY");
|
|
2396
|
+
addRow(this.contextDetails, "duration", "LAST GEN");
|
|
2397
|
+
this.sessionHeader = new SectionBanner(
|
|
2398
|
+
renderer2,
|
|
2399
|
+
"SESSION / AGENTS",
|
|
2400
|
+
`${id}:session`,
|
|
2401
|
+
() => this.toggleSection("session")
|
|
2402
|
+
);
|
|
2403
|
+
this.sessionHeader.on(
|
|
2404
|
+
RenderableEvents3.FOCUSED,
|
|
2405
|
+
() => this.reveal(this.sessionHeader)
|
|
2406
|
+
);
|
|
2407
|
+
this.scroll.add(this.sessionHeader);
|
|
2408
|
+
this.session = new BoxRenderable4(renderer2, {
|
|
2409
|
+
id: `${id}:session-body`,
|
|
2410
|
+
width: "100%",
|
|
2411
|
+
flexDirection: "column",
|
|
2412
|
+
flexShrink: 0
|
|
2413
|
+
});
|
|
2414
|
+
this.scroll.add(this.session);
|
|
2415
|
+
addRow(this.session, "selected", "VIEW");
|
|
2416
|
+
addRow(this.session, "elapsed", "ELAPSED");
|
|
2417
|
+
addRow(this.session, "turns", "TURNS");
|
|
2418
|
+
addRow(this.session, "tools", "TOOLS");
|
|
2419
|
+
addRow(this.session, "toolResults", "TOOL OK/ERR");
|
|
2420
|
+
addRow(this.session, "queued", "QUEUED");
|
|
2421
|
+
this.channels = new ChannelMap(
|
|
2422
|
+
renderer2,
|
|
2423
|
+
`${id}:channels`,
|
|
2424
|
+
options.onInspectAgent,
|
|
2425
|
+
(row) => this.reveal(row)
|
|
2426
|
+
);
|
|
2427
|
+
this.session.add(this.channels);
|
|
2428
|
+
this.session.visible = false;
|
|
2429
|
+
this.sessionHeader.update("SESSION / AGENTS", false);
|
|
2430
|
+
this.devices = new DeviceMonitor(renderer2, options.onAction);
|
|
2431
|
+
this.add(this.devices);
|
|
2432
|
+
if (options.colors) this.applyTheme(options.colors);
|
|
2433
|
+
}
|
|
2434
|
+
update(state) {
|
|
2435
|
+
const data = state.instruments;
|
|
2436
|
+
const inference = data?.inference;
|
|
2437
|
+
const session = data?.session;
|
|
2438
|
+
const set = (id, value) => this.rows.get(id).update(value);
|
|
2439
|
+
set("messages", instrumentNumber(inference?.contextMessages));
|
|
2440
|
+
set("headroom", inference?.contextHeadroom !== void 0 && Number.isFinite(inference.contextHeadroom) ? String(Math.round(inference.contextHeadroom)) : "--");
|
|
2441
|
+
set("context-tools", instrumentNumber(inference?.contextToolCalls));
|
|
2442
|
+
set("adversary", instrumentNumber(inference?.contextAdversaryOutcomes));
|
|
2443
|
+
set("duration", inference?.generationDurationMs === void 0 ? "--" : instrumentNumber(inference.generationDurationMs / 1e3, 1, "s"));
|
|
2444
|
+
this.contextHeader.visible = [inference?.contextMessages, inference?.contextHeadroom, inference?.contextToolCalls, inference?.contextAdversaryOutcomes, inference?.generationDurationMs].some((value) => value !== void 0);
|
|
2445
|
+
this.contextDetails.visible = this.contextHeader.visible && this.contextHeader.expanded;
|
|
2446
|
+
this.devices.update(data?.devices);
|
|
2447
|
+
set("route", (inference?.route || "UNKNOWN").toUpperCase());
|
|
2448
|
+
set("backend", (inference?.backend || "--").toUpperCase());
|
|
2449
|
+
const model = `MODEL ${inference?.model || "--"}`;
|
|
2450
|
+
if (this.model.plainText !== model) this.model.content = model;
|
|
2451
|
+
set("input", instrumentNumber(inference?.inputTokens));
|
|
2452
|
+
set(
|
|
2453
|
+
"output",
|
|
2454
|
+
`${inference?.outputEstimated ? "~" : ""}${instrumentNumber(inference?.outputTokens)}`
|
|
2455
|
+
);
|
|
2456
|
+
set("speed", instrumentNumber(inference?.tokensPerSecond, 1, " TOK/S"));
|
|
2457
|
+
const used = instrumentNumber(inference?.contextUsedTokens), limit = instrumentNumber(inference?.contextLimitTokens);
|
|
2458
|
+
set("context", `${inference?.contextEstimated ? "~" : ""}${used}/${limit}`);
|
|
2459
|
+
this.contextMeter.updateReading(
|
|
2460
|
+
inference?.contextUsedTokens,
|
|
2461
|
+
inference?.contextLimitTokens
|
|
2462
|
+
);
|
|
2463
|
+
set(
|
|
2464
|
+
"state",
|
|
2465
|
+
inference?.streaming ? "STREAMING" : instrumentStatus(session?.status)
|
|
2466
|
+
);
|
|
2467
|
+
set(
|
|
2468
|
+
"selected",
|
|
2469
|
+
session?.selectedLabel || state.tabs.find((tab) => tab.id === state.activeViewId)?.label || state.activeViewId
|
|
2470
|
+
);
|
|
2471
|
+
set("elapsed", session?.elapsed ?? "--");
|
|
2472
|
+
set("turns", instrumentNumber(session?.turns));
|
|
2473
|
+
set("tools", instrumentNumber(session?.tools));
|
|
2474
|
+
set(
|
|
2475
|
+
"toolResults",
|
|
2476
|
+
`${instrumentNumber(session?.successfulTools)}/${instrumentNumber(session?.failedTools)}`
|
|
2477
|
+
);
|
|
2478
|
+
set("queued", instrumentNumber(session?.queued ?? state.queued.length));
|
|
2479
|
+
this.channels.update(
|
|
2480
|
+
session?.agents ?? state.tabs.map((tab) => ({
|
|
2481
|
+
id: tab.id,
|
|
2482
|
+
label: tab.label,
|
|
2483
|
+
status: "unknown",
|
|
2484
|
+
selected: tab.id === state.activeViewId
|
|
2485
|
+
}))
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
applyTheme(colors) {
|
|
2489
|
+
this.backgroundColor = colors.background;
|
|
2490
|
+
const visit = (node) => {
|
|
2491
|
+
if (node instanceof TextRenderable3) {
|
|
2492
|
+
node.fg = colors.foreground;
|
|
2493
|
+
node.bg = colors.background;
|
|
2494
|
+
}
|
|
2495
|
+
for (const child of node.getChildren()) visit(child);
|
|
2496
|
+
};
|
|
2497
|
+
visit(this);
|
|
2498
|
+
styleNativeScrollbars(this.scroll, colors);
|
|
2499
|
+
styleNativeScrollbars(this.devices.scroll, colors);
|
|
2500
|
+
}
|
|
2501
|
+
toggleSection(id) {
|
|
2502
|
+
const header = id === "inference" ? this.inferenceHeader : this.sessionHeader;
|
|
2503
|
+
const body = id === "inference" ? this.inference : this.session;
|
|
2504
|
+
body.visible = !body.visible;
|
|
2505
|
+
header.update(
|
|
2506
|
+
id === "inference" ? "INFERENCE" : "SESSION / AGENTS",
|
|
2507
|
+
body.visible
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
focusTargets() {
|
|
2511
|
+
if (!this.visible) return [];
|
|
2512
|
+
return [
|
|
2513
|
+
this.inferenceHeader,
|
|
2514
|
+
...this.inference.visible && this.contextHeader.visible ? [this.contextHeader] : [],
|
|
2515
|
+
this.sessionHeader,
|
|
2516
|
+
...this.session.visible ? this.channels.focusTargets() : [],
|
|
2517
|
+
...this.devices.focusTargets()
|
|
2518
|
+
];
|
|
2519
|
+
}
|
|
2520
|
+
focusSection(index = 0) {
|
|
2521
|
+
const target = [this.inferenceHeader, this.sessionHeader][index];
|
|
2522
|
+
if (!target || !this.visible) return false;
|
|
2523
|
+
target.focus();
|
|
2524
|
+
return true;
|
|
2525
|
+
}
|
|
2526
|
+
reveal(target) {
|
|
2527
|
+
const viewport = this.scroll.viewport;
|
|
2528
|
+
if (target.y < viewport.y) this.scroll.scrollBy(target.y - viewport.y);
|
|
2529
|
+
else if (target.y + target.height > viewport.y + viewport.height)
|
|
2530
|
+
this.scroll.scrollBy(
|
|
2531
|
+
target.y + target.height - viewport.y - viewport.height
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
// packages/cli/src/tui/command-registry.ts
|
|
2537
|
+
var COMMAND_SIGNATURES = [
|
|
2538
|
+
["/model", "Select from available models"],
|
|
2539
|
+
["/model <name>", "Switch to a specific model"],
|
|
2540
|
+
["/models", "List all available models"],
|
|
2541
|
+
["/endpoint", "Show current endpoint"],
|
|
2542
|
+
["/endpoint <url>", "Set backend URL (auto-detects type)"],
|
|
2543
|
+
["/endpoint <url> --auth <t>", "Set endpoint with Bearer auth"],
|
|
2544
|
+
["/fallback", "Show remote inference fallback mode"],
|
|
2545
|
+
["/fallback status", "Show remote inference fallback mode"],
|
|
2546
|
+
["/fallback auto", "Automatically use remote inference after local failure (default)"],
|
|
2547
|
+
["/fallback manual", "Ask before remote inference after each local failure"],
|
|
2548
|
+
["/config", "Show current configuration"],
|
|
2549
|
+
["/color", "Use terminal colors and thin section rules"],
|
|
2550
|
+
["/color status", "Show terminal appearance preferences"],
|
|
2551
|
+
["/color frill on|off|toggle|status", "Legacy text ornament preference"],
|
|
2552
|
+
[
|
|
2553
|
+
"/color flow on|off|toggle|status",
|
|
2554
|
+
"Legacy border animation preference"
|
|
2555
|
+
],
|
|
2556
|
+
["/theme", "Alias for /color"],
|
|
2557
|
+
["/setup", "Open guided cloud setup; /setup local opens local model setup"],
|
|
2558
|
+
["/wizard", "Alias for /setup"],
|
|
2559
|
+
["/parallel", "Show current Ollama parallel inference slots"],
|
|
2560
|
+
["/parallel <1-15>", "Set parallel slots (restarts Ollama, max 15)"],
|
|
2561
|
+
["/gpu", "Select enabled GPUs with arrow keys and Space"],
|
|
2562
|
+
["/gpu status|reset", "Inspect or reset the host-local GPU policy"],
|
|
2563
|
+
["/gpu select <UUID>", "Open the GPU policy selector focused on a device"],
|
|
2564
|
+
[
|
|
2565
|
+
"/broker",
|
|
2566
|
+
"Show resource broker status — loaded models, in-flight loads, RAM/VRAM headroom"
|
|
2567
|
+
],
|
|
2568
|
+
[
|
|
2569
|
+
"/broker evict <host>:<name>",
|
|
2570
|
+
"Force eviction of a tracked model (e.g. /broker evict ollama:moondream)"
|
|
2571
|
+
],
|
|
2572
|
+
[
|
|
2573
|
+
"/broker threshold <ram|vram|idle> <v>",
|
|
2574
|
+
"Tune broker thresholds (RAM/VRAM in MB, idle in seconds)"
|
|
2575
|
+
],
|
|
2576
|
+
["/ollama cleanup", "Dry-run stale Ollama pool process cleanup"],
|
|
2577
|
+
["/ollama cleanup --execute", "Terminate guarded stale Ollama pool runners"],
|
|
2578
|
+
["/mcp", "Show MCP server/tool status and controls"],
|
|
2579
|
+
["/mcp reload", "Reload MCP servers and tools"],
|
|
2580
|
+
["/indicator", "Start the native system indicator and report readiness"],
|
|
2581
|
+
["/indicator start", "Start the native system indicator and report readiness"],
|
|
2582
|
+
["/indicator status", "Report native system indicator readiness and daemon health"],
|
|
2583
|
+
["/tray", "Alias for /indicator"],
|
|
2584
|
+
["/tray start", "Alias for /indicator start"],
|
|
2585
|
+
["/tray status", "Alias for /indicator status"],
|
|
2586
|
+
["/update", "Check for updates and auto-install"],
|
|
2587
|
+
["/upgrade", "Alias for /update"],
|
|
2588
|
+
["/update auto", "Auto-install updates, restart, and resume work (default)"],
|
|
2589
|
+
["/update manual", "Disable auto-update (only update on /update)"],
|
|
2590
|
+
["/agents", "Open the agent command center"],
|
|
2591
|
+
[
|
|
2592
|
+
"/update check",
|
|
2593
|
+
"Check for Omnius and Ollama updates without opening the menu"
|
|
2594
|
+
],
|
|
2595
|
+
["/update quick", "Install the latest Omnius package non-interactively"],
|
|
2596
|
+
[
|
|
2597
|
+
"/update full",
|
|
2598
|
+
"Run full non-interactive update: package, deps, rebuild, Python, cloudflared"
|
|
2599
|
+
],
|
|
2600
|
+
["/voice", "Toggle TTS voice feedback"],
|
|
2601
|
+
[
|
|
2602
|
+
"/voice <model>",
|
|
2603
|
+
"Set voice: glados, overwatch, luxtts:announcer-testchamber03, kokoro, luxtts, misotts, supertonic, or a selected voicebox_* model"
|
|
2604
|
+
],
|
|
2605
|
+
["/voice clone <file>", "Set voice clone reference audio (wav/mp3/ogg/flac)"],
|
|
2606
|
+
["/voice asr", "Open ASR system and model selection"],
|
|
2607
|
+
["/voice asr status", "Show selected ASR engine/model and activation state"],
|
|
2608
|
+
["/voice asr list", "List ASR engines, models, and live/file capabilities"],
|
|
2609
|
+
["/voice asr use <engine> [model]", "Select and activate an installed ASR engine/model"],
|
|
2610
|
+
["/voice asr setup <engine> [model] [device=N]", "Install pinned ASR runtime/weights and activate on an exact CUDA device"],
|
|
2611
|
+
[
|
|
2612
|
+
"/voice clone glados",
|
|
2613
|
+
"Generate clone ref from GLaDOS for LuxTTS/MisoTTS voice cloning"
|
|
2614
|
+
],
|
|
2615
|
+
[
|
|
2616
|
+
"/voice clone overwatch",
|
|
2617
|
+
"Generate clone ref from Overwatch for LuxTTS/MisoTTS voice cloning"
|
|
2618
|
+
],
|
|
2619
|
+
["/think", "Toggle thinking/reasoning mode (Qwen3, DeepSeek-R1, etc.)"],
|
|
2620
|
+
["/stream", "Toggle real-time token streaming (earth syntax highlighting)"],
|
|
2621
|
+
["/tree", "Switch between the action waterfall and an explorable action tree"],
|
|
2622
|
+
["/tree on|off|status", "Show, hide, or inspect the action-tree presentation"],
|
|
2623
|
+
["/realtime", "Toggle short spoken-dialogue mode for ASR/TTS-backed chat"],
|
|
2624
|
+
["/realtime on|off|status", "Set or inspect realtime conversation mode"],
|
|
2625
|
+
["/mouse", "Toggle terminal mouse tracking"],
|
|
2626
|
+
["/neovim", "Toggle embedded Neovim editor (Ctrl+N to switch focus)"],
|
|
2627
|
+
["/neovim <file>", "Open file in embedded Neovim editor"],
|
|
2628
|
+
["/nvim <file>", "Alias for /neovim <file>"],
|
|
2629
|
+
["/vim", "Toggle Vim composer editing (Esc normal; i insert)"],
|
|
2630
|
+
["/vim on|off|status", "Set or inspect Vim composer editing"],
|
|
2631
|
+
[
|
|
2632
|
+
"/dream",
|
|
2633
|
+
"Start dream mode - creative idle exploration (writes to .omnius/dreams/)"
|
|
2634
|
+
],
|
|
2635
|
+
[
|
|
2636
|
+
"/dream deep",
|
|
2637
|
+
"Deep dream - multi-cycle exploration with sleep architecture"
|
|
2638
|
+
],
|
|
2639
|
+
[
|
|
2640
|
+
"/dream lucid",
|
|
2641
|
+
"Lucid dream - implements proposals, tests, evaluates in cycles"
|
|
2642
|
+
],
|
|
2643
|
+
["/dream stop", "Wake up - stop dreaming"],
|
|
2644
|
+
["/listen", "Toggle live microphone transcription (Whisper)"],
|
|
2645
|
+
["/mic", "Alias for /listen"],
|
|
2646
|
+
["/listen <model>", "Set model: tiny, base, small, medium, large"],
|
|
2647
|
+
["/listen manual", "Manual mode - press Enter to submit (turns off auto)"],
|
|
2648
|
+
["/listen auto", "Auto-submit after 3s silence (blinking indicator)"],
|
|
2649
|
+
["/listen stop", "Stop listening"],
|
|
2650
|
+
["/live", "Open live sensor stream menu for video/audio context"],
|
|
2651
|
+
["/live status", "Show live sensor devices, selected streams, and latest snapshot"],
|
|
2652
|
+
["/live run [agent]", "Start low-latency video/audio feedback; agent enables background PFC review"],
|
|
2653
|
+
["/live chat", "Start low-latency voicechat with live audio/video context and async agent review"],
|
|
2654
|
+
["/livechat", "Alias for /live chat, used by the top live button"],
|
|
2655
|
+
["/live camera <device>", "Select a camera and render an ASCII preview"],
|
|
2656
|
+
["/live resolution [camera] 720p|1080p|1440p|4k|native", "Set per-camera live stream resolution (default 1080p)"],
|
|
2657
|
+
["/live fps [camera] 1|2|4|8|12|15|24|30", "Set per-camera live stream FPS (default 8)"],
|
|
2658
|
+
["/live camera-on|camera-off [camera]", "Enable or disable an individual live camera stream"],
|
|
2659
|
+
["/live rotate auto|cw|ccw|180|none [camera]", "Detect or set persistent camera orientation correction"],
|
|
2660
|
+
["/live infer [now|on|off]", "Toggle or run camera object/location inference"],
|
|
2661
|
+
["/live clip [on|off]", "Toggle CLIP visual-memory recognition on live frames"],
|
|
2662
|
+
["/live audio|asr|sounds on|off", "Toggle live audio context streams"],
|
|
2663
|
+
["/live stop", "Disable all live sensor streams"],
|
|
2664
|
+
["/camera", "Open compact camera preset menu shared with /live"],
|
|
2665
|
+
["/camera status", "Show selected camera and per-device presets"],
|
|
2666
|
+
["/camera select|preview|resolution|fps|rotate", "Manage camera source, preview, resolution, FPS, and orientation"],
|
|
2667
|
+
["/paste", "Attach clipboard image content to the current or next prompt"],
|
|
2668
|
+
["/image", "Open image-generation model/setup menu"],
|
|
2669
|
+
[
|
|
2670
|
+
"/image <prompt>",
|
|
2671
|
+
"Generate an image from a prompt and show an ASCII preview"
|
|
2672
|
+
],
|
|
2673
|
+
["/image --model <model> <prompt>", "Generate with an explicit image model"],
|
|
2674
|
+
[
|
|
2675
|
+
"/image setup <ollama|diffusers|sdcpp>",
|
|
2676
|
+
"Show setup commands for an image-generation backend"
|
|
2677
|
+
],
|
|
2678
|
+
[
|
|
2679
|
+
"/image list",
|
|
2680
|
+
"List image models by category, quality, size, and hardware fit"
|
|
2681
|
+
],
|
|
2682
|
+
["/sound", "Open sound-effect model/setup menu"],
|
|
2683
|
+
["/sound <prompt>", "Generate a sound effect or ambience clip from a prompt"],
|
|
2684
|
+
[
|
|
2685
|
+
"/sound --model <model> <prompt>",
|
|
2686
|
+
"Generate sound with an explicit audio model"
|
|
2687
|
+
],
|
|
2688
|
+
[
|
|
2689
|
+
"/sound setup <diffusers|audiocraft|stable-audio|tangoflux>",
|
|
2690
|
+
"Show setup commands for a sound-generation backend"
|
|
2691
|
+
],
|
|
2692
|
+
[
|
|
2693
|
+
"/sound list",
|
|
2694
|
+
"List sound models by category, quality, size, and hardware fit"
|
|
2695
|
+
],
|
|
2696
|
+
["/music", "Open music-generation model/setup menu"],
|
|
2697
|
+
["/music <prompt>", "Generate a music clip from a prompt"],
|
|
2698
|
+
[
|
|
2699
|
+
"/music --model <model> <prompt>",
|
|
2700
|
+
"Generate music with an explicit music model"
|
|
2701
|
+
],
|
|
2702
|
+
[
|
|
2703
|
+
"/music setup <transformers|audiocraft|stable-audio|diffusers>",
|
|
2704
|
+
"Show setup commands for a music-generation backend"
|
|
2705
|
+
],
|
|
2706
|
+
[
|
|
2707
|
+
"/music list",
|
|
2708
|
+
"List music models by category, quality, size, and hardware fit"
|
|
2709
|
+
],
|
|
2710
|
+
["/video", "Open video-generation model/setup menu"],
|
|
2711
|
+
[
|
|
2712
|
+
"/video <prompt>",
|
|
2713
|
+
"Generate a short video from a prompt and show an ASCII thumbnail"
|
|
2714
|
+
],
|
|
2715
|
+
["/video --model <model> <prompt>", "Generate video with an explicit model"],
|
|
2716
|
+
[
|
|
2717
|
+
"/video --image <path|url> <prompt>",
|
|
2718
|
+
"Image-to-video animation from a still"
|
|
2719
|
+
],
|
|
2720
|
+
[
|
|
2721
|
+
"/video setup <diffusers|comfyui>",
|
|
2722
|
+
"Show setup commands for a video-generation backend"
|
|
2723
|
+
],
|
|
2724
|
+
[
|
|
2725
|
+
"/video list",
|
|
2726
|
+
"List video models by category, quality, size, and hardware fit"
|
|
2727
|
+
],
|
|
2728
|
+
["/video prewarm", "Download and warm up the selected video model"],
|
|
2729
|
+
["/pointcloud", "Open point-cloud generation model/setup menu"],
|
|
2730
|
+
[
|
|
2731
|
+
"/pointcloud --image <path>",
|
|
2732
|
+
"Generate a 3D point cloud PLY from an image using depth estimation"
|
|
2733
|
+
],
|
|
2734
|
+
[
|
|
2735
|
+
"/pointcloud --image <path> --model <model>",
|
|
2736
|
+
"Generate with an explicit depth model (depth-anything-v3-* or mda-*)"
|
|
2737
|
+
],
|
|
2738
|
+
[
|
|
2739
|
+
"/pointcloud --image <path1,path2> --backend mda",
|
|
2740
|
+
"Multi-view depth fusion from comma-separated image paths"
|
|
2741
|
+
],
|
|
2742
|
+
[
|
|
2743
|
+
"/pointcloud setup <depth-anything|mda>",
|
|
2744
|
+
"Show setup commands for a point-cloud backend"
|
|
2745
|
+
],
|
|
2746
|
+
[
|
|
2747
|
+
"/pointcloud list",
|
|
2748
|
+
"List point-cloud models by backend, quality, and VRAM fit"
|
|
2749
|
+
],
|
|
2750
|
+
["/pointcloud prewarm", "Download and warm up the selected depth model"],
|
|
2751
|
+
["/points <args>", "Alias for /pointcloud"],
|
|
2752
|
+
["/pcl <args>", "Alias for /pointcloud"],
|
|
2753
|
+
["/call", "Start voice call session (cloudflared tunnel + ASR/TTS)"],
|
|
2754
|
+
["/hangup", "End active call session"],
|
|
2755
|
+
[
|
|
2756
|
+
"/queue <prompt>",
|
|
2757
|
+
"Queue a prompt for the next turn without interrupting the current run"
|
|
2758
|
+
],
|
|
2759
|
+
["/q <prompt>", "Alias for /queue <prompt>"],
|
|
2760
|
+
["/retry", "Retry the last submitted request"],
|
|
2761
|
+
["/undo", "Undo the last queued or saved exchange when available"],
|
|
2762
|
+
["/title [name]", "Show or set the current session title"],
|
|
2763
|
+
[
|
|
2764
|
+
"/rollback [checkpoint]",
|
|
2765
|
+
"List checkpoints or restore an explicit checkpoint when policy allows"
|
|
2766
|
+
],
|
|
2767
|
+
["/background <prompt>", "Run a prompt in an independent background session"],
|
|
2768
|
+
["/bg <prompt>", "Alias for /background <prompt>"],
|
|
2769
|
+
["/cost", "Show session token cost breakdown"],
|
|
2770
|
+
["/usage", "Show current session token count and cost"],
|
|
2771
|
+
["/insights", "Show historical session insights"],
|
|
2772
|
+
["/evaluate", "Evaluate last completed task (LLM quality scoring)"],
|
|
2773
|
+
[
|
|
2774
|
+
"/score",
|
|
2775
|
+
"Show inference capability scorecard (memory, compute, speed, models)"
|
|
2776
|
+
],
|
|
2777
|
+
[
|
|
2778
|
+
"/task-type",
|
|
2779
|
+
"Set task type (code, document, analysis, plan, general, auto)"
|
|
2780
|
+
],
|
|
2781
|
+
["/stats", "Show session dashboard (metrics, tool usage, task history)"],
|
|
2782
|
+
[
|
|
2783
|
+
"/metrics",
|
|
2784
|
+
"Show runtime metrics: tokens, context window, throughput, and tool usage"
|
|
2785
|
+
],
|
|
2786
|
+
["/sessions", "Browse saved sessions"],
|
|
2787
|
+
["/new", "Start a fresh chat in the current project"],
|
|
2788
|
+
[
|
|
2789
|
+
"/pause",
|
|
2790
|
+
"Pause after current turn finishes (gentle halt, /resume to continue)"
|
|
2791
|
+
],
|
|
2792
|
+
[
|
|
2793
|
+
"/stop",
|
|
2794
|
+
"Kill current inference immediately and save state (/resume to continue)"
|
|
2795
|
+
],
|
|
2796
|
+
[
|
|
2797
|
+
"/resume",
|
|
2798
|
+
"Resume a paused task or choose a saved project chat"
|
|
2799
|
+
],
|
|
2800
|
+
["/resume <session>", "Resume a saved project chat by id or title"],
|
|
2801
|
+
[
|
|
2802
|
+
"/destroy processes",
|
|
2803
|
+
"Find and kill orphaned Omnius child processes (this project)"
|
|
2804
|
+
],
|
|
2805
|
+
[
|
|
2806
|
+
"/destroy processes --global",
|
|
2807
|
+
"Kill ALL orphaned Omnius processes system-wide"
|
|
2808
|
+
],
|
|
2809
|
+
[
|
|
2810
|
+
"/destroy project",
|
|
2811
|
+
"Remove .omnius folder, kill all tasks, clear console, and exit"
|
|
2812
|
+
],
|
|
2813
|
+
["/context save", "Force-save session context to .omnius/context/"],
|
|
2814
|
+
["/context restore", "Restore context from previous sessions into next task"],
|
|
2815
|
+
["/context show", "Show saved session context status"],
|
|
2816
|
+
["/compact", "Force context compaction now (default strategy)"],
|
|
2817
|
+
["/compacted", "Review applied, held, rejected, and failed compaction attempts"],
|
|
2818
|
+
[
|
|
2819
|
+
"/compact <strategy>",
|
|
2820
|
+
"Compact with strategy: aggressive, decisions, errors, summary, structured"
|
|
2821
|
+
],
|
|
2822
|
+
["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
|
|
2823
|
+
[
|
|
2824
|
+
"/deep",
|
|
2825
|
+
"Toggle deep context - relaxes compaction so large models use 85% of context"
|
|
2826
|
+
],
|
|
2827
|
+
[
|
|
2828
|
+
"/flow",
|
|
2829
|
+
"Toggle FlowState protocol - immediate action, tight loops, goal clarity"
|
|
2830
|
+
],
|
|
2831
|
+
["/emojis", "Toggle emoji rendering in TUI messages"],
|
|
2832
|
+
["/tools", "List agent-created custom tools"],
|
|
2833
|
+
["/skills", "List available AIWG skills"],
|
|
2834
|
+
["/skill", "Alias for /skills"],
|
|
2835
|
+
["/skills <keyword>", "Filter skills by name or trigger"],
|
|
2836
|
+
["/skillify [request]", "Extract a reusable skill from the current session"],
|
|
2837
|
+
["/<skill-name> [args]", "Invoke an AIWG skill directly"],
|
|
2838
|
+
[
|
|
2839
|
+
"/full-send-bless",
|
|
2840
|
+
"Infinite warm loop - DMN self-reflection chains tasks autonomously"
|
|
2841
|
+
],
|
|
2842
|
+
["/bless stop", "End blessed mode + DMN"],
|
|
2843
|
+
[
|
|
2844
|
+
"/telegram --key <token>",
|
|
2845
|
+
"Save Telegram bot token (persisted to settings)"
|
|
2846
|
+
],
|
|
2847
|
+
[
|
|
2848
|
+
"/telegram --admin <userid>",
|
|
2849
|
+
"Set admin user filter (only this user can interact)"
|
|
2850
|
+
],
|
|
2851
|
+
["/telegram", "Toggle Telegram bridge on/off (uses saved key)"],
|
|
2852
|
+
["/telegram status", "Show Telegram bridge status"],
|
|
2853
|
+
["/telegram stop", "Disconnect Telegram bridge"],
|
|
2854
|
+
["/telegram disable", "Persistently disable Telegram long polling"],
|
|
2855
|
+
["/telegram enable", "Re-enable Telegram long polling"],
|
|
2856
|
+
[
|
|
2857
|
+
"/telegram mode auto|chat|action",
|
|
2858
|
+
"Set Telegram interaction routing: auto, fast chat, or action sub-agent"
|
|
2859
|
+
],
|
|
2860
|
+
[
|
|
2861
|
+
"/telegram subagents <1-5>",
|
|
2862
|
+
"Set global Telegram work-slot limit across all chats/groups"
|
|
2863
|
+
],
|
|
2864
|
+
[
|
|
2865
|
+
"/telegram model",
|
|
2866
|
+
"Pick the Telegram-isolated model (model list from the Telegram endpoint)"
|
|
2867
|
+
],
|
|
2868
|
+
[
|
|
2869
|
+
"/telegram model <name|reset>",
|
|
2870
|
+
"Set or clear the Telegram-isolated model (separate from main agent)"
|
|
2871
|
+
],
|
|
2872
|
+
[
|
|
2873
|
+
"/telegram endpoint",
|
|
2874
|
+
"Pick the Telegram-isolated backend endpoint (same menu as /endpoint, bot-scoped)"
|
|
2875
|
+
],
|
|
2876
|
+
[
|
|
2877
|
+
"/telegram endpoint <url|reset> [--auth <t>]",
|
|
2878
|
+
"Set or clear the Telegram-isolated endpoint"
|
|
2879
|
+
],
|
|
2880
|
+
[
|
|
2881
|
+
"/telegram auth",
|
|
2882
|
+
"Show a TUI-only one-time code for Telegram admin authentication"
|
|
2883
|
+
],
|
|
2884
|
+
[
|
|
2885
|
+
"/telegram auth cancel",
|
|
2886
|
+
"Cancel the pending Telegram admin authentication code"
|
|
2887
|
+
],
|
|
2888
|
+
[
|
|
2889
|
+
"/telegram bot <username> <text>",
|
|
2890
|
+
"Send a Bot API bot-to-bot message by @username"
|
|
2891
|
+
],
|
|
2892
|
+
[
|
|
2893
|
+
"/telegram poll <chat> <question> | <option> [| <option>]",
|
|
2894
|
+
"Send a Telegram poll"
|
|
2895
|
+
],
|
|
2896
|
+
[
|
|
2897
|
+
"/telegram live-photo <chat> <live_photo> <photo> [caption]",
|
|
2898
|
+
"Send a Telegram live photo"
|
|
2899
|
+
],
|
|
2900
|
+
[
|
|
2901
|
+
"/telegram draft <chat_id> <draft_id> [text]",
|
|
2902
|
+
"Set or clear a Telegram message draft"
|
|
2903
|
+
],
|
|
2904
|
+
[
|
|
2905
|
+
"/telegram admins <chat> [--bots]",
|
|
2906
|
+
"List chat administrators, optionally including bots"
|
|
2907
|
+
],
|
|
2908
|
+
[
|
|
2909
|
+
"/telegram personal <user_id> <limit>",
|
|
2910
|
+
"Fetch last messages from a user's profile personal chat"
|
|
2911
|
+
],
|
|
2912
|
+
[
|
|
2913
|
+
"/telegram access get <managed_bot_user_id>",
|
|
2914
|
+
"Show managed-bot access restriction settings"
|
|
2915
|
+
],
|
|
2916
|
+
[
|
|
2917
|
+
"/telegram access set <managed_bot_user_id> <restricted|open> [added_user_ids]",
|
|
2918
|
+
"Set managed-bot access restrictions"
|
|
2919
|
+
],
|
|
2920
|
+
[
|
|
2921
|
+
"/telegram tools [group <chat>|panel <chat> [policy_chat]]",
|
|
2922
|
+
"Show unified Telegram tool policy or send inline admin toggle buttons"
|
|
2923
|
+
],
|
|
2924
|
+
[
|
|
2925
|
+
"/telegram delete-message <chat> <message> [reason]",
|
|
2926
|
+
"Delete one Telegram message after policy and Bot API rights checks"
|
|
2927
|
+
],
|
|
2928
|
+
[
|
|
2929
|
+
"/telegram delete-messages <chat> <message,message> [reason]",
|
|
2930
|
+
"Delete multiple Telegram messages after policy and Bot API rights checks"
|
|
2931
|
+
],
|
|
2932
|
+
[
|
|
2933
|
+
"/telegram delete-reaction <chat> <message> [--user <id>|--actor <chat>]",
|
|
2934
|
+
"Delete a Telegram message reaction"
|
|
2935
|
+
],
|
|
2936
|
+
[
|
|
2937
|
+
"/telegram delete-reactions <chat> [--user <id>|--actor <chat>]",
|
|
2938
|
+
"Delete recent Telegram message reactions by user or chat"
|
|
2939
|
+
],
|
|
2940
|
+
["/platforms", "Show gateway platform state"],
|
|
2941
|
+
["/platforms status", "Show gateway platform state"],
|
|
2942
|
+
["/platforms menu", "Open interactive platform connectivity menu"],
|
|
2943
|
+
["/platforms setup", "Open platform onboarding menu"],
|
|
2944
|
+
["/platforms onboard", "Open platform onboarding menu"],
|
|
2945
|
+
["/platforms config", "Show redacted platform adapter configuration"],
|
|
2946
|
+
["/platforms enable <id>", "Enable a platform adapter"],
|
|
2947
|
+
["/platforms disable <id>", "Disable a platform adapter"],
|
|
2948
|
+
[
|
|
2949
|
+
"/platforms set <id> token-env <ENV>",
|
|
2950
|
+
"Set adapter token environment variable"
|
|
2951
|
+
],
|
|
2952
|
+
["/platforms set <id> base-url <url>", "Set adapter base URL or webhook URL"],
|
|
2953
|
+
[
|
|
2954
|
+
"/platforms set <id> target <target>",
|
|
2955
|
+
"Set adapter default channel, room, or target"
|
|
2956
|
+
],
|
|
2957
|
+
[
|
|
2958
|
+
"/platforms set <id> mode <polling|webhook>",
|
|
2959
|
+
"Set adapter polling or webhook mode"
|
|
2960
|
+
],
|
|
2961
|
+
[
|
|
2962
|
+
"/platforms clear <id> <key>",
|
|
2963
|
+
"Clear saved adapter token, token-env, base-url, or target"
|
|
2964
|
+
],
|
|
2965
|
+
["/gateway", "Alias for /platforms"],
|
|
2966
|
+
["/gateway menu", "Alias for /platforms menu"],
|
|
2967
|
+
[
|
|
2968
|
+
"/cohere",
|
|
2969
|
+
"Toggle COHERE cognitive commons - join distributed memory/identity mesh"
|
|
2970
|
+
],
|
|
2971
|
+
[
|
|
2972
|
+
"/cohere stats",
|
|
2973
|
+
"Show network stats - queries in/out, model usage, peer transparency"
|
|
2974
|
+
],
|
|
2975
|
+
["/cohere models", "List downloaded models with COHERE exposure status"],
|
|
2976
|
+
["/cohere allow <model>", "Allow a model for remote COHERE queries"],
|
|
2977
|
+
["/cohere deny <model>", "Hide a model from remote COHERE queries"],
|
|
2978
|
+
["/nexus", "Show nexus P2P network status"],
|
|
2979
|
+
["/nexus connect", "Connect to the agent mesh network"],
|
|
2980
|
+
[
|
|
2981
|
+
"/nexus name <name>",
|
|
2982
|
+
"Set custom agent name (persists globally across all Omnius instances)"
|
|
2983
|
+
],
|
|
2984
|
+
["/nexus name", "Show current agent name"],
|
|
2985
|
+
["/nexus restart", "Kill daemon and reconnect (picks up new version)"],
|
|
2986
|
+
["/nexus restart --clean", "Restart + clear metering data for fresh stats"],
|
|
2987
|
+
["/nexus disconnect", "Kill daemon without reconnecting"],
|
|
2988
|
+
["/nexus wallet", "Show wallet address and x402 payment status"],
|
|
2989
|
+
["/ipfs", "Show IPFS/Helia status"],
|
|
2990
|
+
["/ipfs pin <cid>", "Pin an IPFS CID"],
|
|
2991
|
+
["/ipfs publish", "Publish identity snapshot to IPFS"],
|
|
2992
|
+
["/ipfs share <tool|skill> <name>", "Share a tool or skill through IPFS"],
|
|
2993
|
+
["/ipfs import <cid>", "Import a shared IPFS tool or skill reference"],
|
|
2994
|
+
["/sponsor", "Sponsor inference - onboarding wizard or dashboard"],
|
|
2995
|
+
["/sponsor status", "Show sponsor dashboard with usage metrics"],
|
|
2996
|
+
["/sponsor image <prompt>", "Generate an image on a sponsored peer"],
|
|
2997
|
+
["/sponsor video <prompt>", "Generate a video on a sponsored peer"],
|
|
2998
|
+
["/sponsor sound <prompt>", "Generate a sound effect on a sponsored peer"],
|
|
2999
|
+
["/sponsor music <prompt>", "Generate music on a sponsored peer"],
|
|
3000
|
+
["/sponsor pause", "Pause active sponsorship"],
|
|
3001
|
+
["/sponsor remove", "Remove sponsorship entirely"],
|
|
3002
|
+
[
|
|
3003
|
+
"/endpoint sponsor",
|
|
3004
|
+
"Browse and connect to sponsored inference from the nexus mesh"
|
|
3005
|
+
],
|
|
3006
|
+
["/expose <backend>", "Expose local inference via libp2p (default)"],
|
|
3007
|
+
["/expose <backend> --tunnel", "Expose via cloudflared tunnel"],
|
|
3008
|
+
["/expose passthrough", "Forward configured /endpoint through libp2p P2P"],
|
|
3009
|
+
[
|
|
3010
|
+
"/expose forward --loadbalance",
|
|
3011
|
+
"Passthrough with distributed rate-limit budget"
|
|
3012
|
+
],
|
|
3013
|
+
["/expose config", "Interactive expose configuration menu"],
|
|
3014
|
+
["/expose status", "Show gateway stats (requests, models, budget)"],
|
|
3015
|
+
["/expose stop", "Stop all expose gateways"],
|
|
3016
|
+
["/expose stop --tunnel", "Stop tunnel gateway only"],
|
|
3017
|
+
["/expose stop --libp2p", "Stop libp2p gateway only"],
|
|
3018
|
+
["/access", "Show access policy and host binding"],
|
|
3019
|
+
[
|
|
3020
|
+
"/access <loopback|lan|any>",
|
|
3021
|
+
"Set access policy (OMNIUS_ACCESS) and restart daemon"
|
|
3022
|
+
],
|
|
3023
|
+
["/k <any|lan|loopback|config>", "Quick network access helper"],
|
|
3024
|
+
["/scheduler", "Scheduled tasks control panel (list/kill/toggle)"],
|
|
3025
|
+
["/cron", "Cron/scheduled job management"],
|
|
3026
|
+
["/cron list", "List cron jobs"],
|
|
3027
|
+
["/cron add <schedule> <prompt>", "Create a new cron job"],
|
|
3028
|
+
["/cron edit <id> <field> <val>", "Update a cron job field"],
|
|
3029
|
+
["/cron pause <id>", "Pause a cron job"],
|
|
3030
|
+
["/cron resume <id>", "Resume a paused cron job"],
|
|
3031
|
+
["/cron run <id>", "Execute a cron job immediately"],
|
|
3032
|
+
["/cron remove <id>", "Delete a cron job"],
|
|
3033
|
+
["/cron outputs <id>", "Show saved output files for a job"],
|
|
3034
|
+
["/cron tick", "Run scheduler tick"],
|
|
3035
|
+
["/scheduler menu", "Interactive scheduler menu (toggle/kill)"],
|
|
3036
|
+
["/scheduler list", "List all scheduled tasks and timers"],
|
|
3037
|
+
[
|
|
3038
|
+
"/scheduler kill",
|
|
3039
|
+
"Kill schedulers + active runs (with escalation if needed)"
|
|
3040
|
+
],
|
|
3041
|
+
["/reminder [list|all]", "List scoped TUI reminders"],
|
|
3042
|
+
[
|
|
3043
|
+
"/remind <when> <message>",
|
|
3044
|
+
"Set a scoped person notification; a due time is required"
|
|
3045
|
+
],
|
|
3046
|
+
[
|
|
3047
|
+
"/remind in 30m <message>",
|
|
3048
|
+
"Set a scoped person notification with a relative due time"
|
|
3049
|
+
],
|
|
3050
|
+
["/reminders", "Alias for /reminder"],
|
|
3051
|
+
["/daemon", "Show or manage the local API daemon"],
|
|
3052
|
+
["/daemon <start|stop|restart|takeover>", "Control the local API daemon"],
|
|
3053
|
+
["/apikey", "Show current API key (for pasting into Web UI / clients)"],
|
|
3054
|
+
["/apikey copy", "Copy the API key to clipboard"],
|
|
3055
|
+
["/apikey new", "Rotate to a new API key (regenerate)"],
|
|
3056
|
+
["/secrets", "List registered secrets"],
|
|
3057
|
+
["/secrets set <name> <value>", "Register a secret for redacted tool use"],
|
|
3058
|
+
["/secrets delete <name>", "Remove a registered secret"],
|
|
3059
|
+
["/key", "Alias for /apikey"],
|
|
3060
|
+
["/codegraph", "Code-graph snapshot: stats, top files, recent activity"],
|
|
3061
|
+
[
|
|
3062
|
+
"/codegraph watch [N]",
|
|
3063
|
+
"Subscribe to live code-graph events for N seconds (default 30)"
|
|
3064
|
+
],
|
|
3065
|
+
["/cg", "Alias for /codegraph"],
|
|
3066
|
+
["/storage", "Show .omnius storage usage and safety summary"],
|
|
3067
|
+
["/files", "Show searchable working-directory file overview"],
|
|
3068
|
+
["/files <query>", "Filter the working-directory file overview"],
|
|
3069
|
+
["/files menu", "Open searchable workspace file selector"],
|
|
3070
|
+
["/workspace", "Alias for /files"],
|
|
3071
|
+
["/workspace <query>", "Alias for /files <query>"],
|
|
3072
|
+
["/workspace menu", "Alias for /files menu"],
|
|
3073
|
+
["/ingest <file>", "Ingest an audio, PDF, or text file into memory"],
|
|
3074
|
+
["/transcribe <file>", "Alias for /ingest <file>"],
|
|
3075
|
+
["/fortemi", "Manage Fortemi React bridge integration"],
|
|
3076
|
+
[
|
|
3077
|
+
"/host <host[:port]>",
|
|
3078
|
+
"Set bind host:port (OMNIUS_HOST) and restart daemon"
|
|
3079
|
+
],
|
|
3080
|
+
["/network config", "Interactive network access menu"],
|
|
3081
|
+
["/p2p start", "Join the P2P agent mesh network"],
|
|
3082
|
+
["/p2p status", "Show mesh peers and rooms"],
|
|
3083
|
+
["/p2p stop", "Leave the mesh network"],
|
|
3084
|
+
["/style", "Show current response style"],
|
|
3085
|
+
["/style <preset>", "Set style: concise, balanced, verbose, pedagogical"],
|
|
3086
|
+
[
|
|
3087
|
+
"/personality <preset>",
|
|
3088
|
+
"Alias family for response style/personality controls"
|
|
3089
|
+
],
|
|
3090
|
+
["/commands auto", "Allow agent to invoke slash commands as tools"],
|
|
3091
|
+
["/cmds auto", "Alias for /commands auto"],
|
|
3092
|
+
["/commands manual", "Slash commands are user-only (default)"],
|
|
3093
|
+
[
|
|
3094
|
+
"/selfmodify",
|
|
3095
|
+
"Toggle agent self-modify mode for natural-language runtime commands"
|
|
3096
|
+
],
|
|
3097
|
+
[
|
|
3098
|
+
"/selfmodify on",
|
|
3099
|
+
"Allow the agent to decide when to invoke self-modifying slash commands"
|
|
3100
|
+
],
|
|
3101
|
+
[
|
|
3102
|
+
"/selfmodify off",
|
|
3103
|
+
"Disable agent self-modifying slash-command access (default)"
|
|
3104
|
+
],
|
|
3105
|
+
["/selfmodify status", "Show current self-modify mode"],
|
|
3106
|
+
[
|
|
3107
|
+
"/selfimprove status",
|
|
3108
|
+
"Show durable review/proposal backlog, leases, retries, and last error"
|
|
3109
|
+
],
|
|
3110
|
+
[
|
|
3111
|
+
"/selfimprove start",
|
|
3112
|
+
"Enable review mode and drain eligible work with the parent model"
|
|
3113
|
+
],
|
|
3114
|
+
["/selfimprove stop", "Disable processing while retaining the durable backlog"],
|
|
3115
|
+
["/selfimprove disable", "Alias for /selfimprove stop"],
|
|
3116
|
+
[
|
|
3117
|
+
"/selfimprove review",
|
|
3118
|
+
"Run evidence review and candidate generation without active promotion"
|
|
3119
|
+
],
|
|
3120
|
+
[
|
|
3121
|
+
"/selfimprove autonomous",
|
|
3122
|
+
"Opt in to downstream candidate experiment and promotion orchestration"
|
|
3123
|
+
],
|
|
3124
|
+
[
|
|
3125
|
+
"/selfimprove rollback <promotion-id> [reason]",
|
|
3126
|
+
"Restore the exact pre-promotion baseline and verify the rollback"
|
|
3127
|
+
],
|
|
3128
|
+
["/debug", "Toggle debug mode — show/hide trust_tier wrappers and REG fires"],
|
|
3129
|
+
["/debug on", "Show trust_tier wrappers and REG fires in terminal"],
|
|
3130
|
+
["/debug off", "Hide trust_tier wrappers and REG fires (default)"],
|
|
3131
|
+
["/debug", "Toggle debug mode — show/hide trust_tier wrappers and REG fires"],
|
|
3132
|
+
["/debug on", "Show trust_tier wrappers and REG fires in terminal"],
|
|
3133
|
+
["/debug off", "Hide trust_tier wrappers and REG fires (default)"],
|
|
3134
|
+
["/hf", "Show HF token status (set / not set)"],
|
|
3135
|
+
[
|
|
3136
|
+
"/hf <token>",
|
|
3137
|
+
"Save Hugging Face access token to ~/.omnius/hf_token and unblock the active gated download"
|
|
3138
|
+
],
|
|
3139
|
+
["/hf clear", "Remove the persisted HF token"],
|
|
3140
|
+
[
|
|
3141
|
+
"/hf cancel",
|
|
3142
|
+
"Cancel any pending HF token prompt and fall back immediately"
|
|
3143
|
+
],
|
|
3144
|
+
[
|
|
3145
|
+
"/models",
|
|
3146
|
+
"Show unified ~/.omnius model store: disk usage, cached repos, last-used"
|
|
3147
|
+
],
|
|
3148
|
+
["/models 3d", "Select CAD/3D generation and reconstruction model adapters"],
|
|
3149
|
+
["/models cad", "Select text-to-CAD model adapters such as cad0"],
|
|
3150
|
+
[
|
|
3151
|
+
"/models viewer [file]",
|
|
3152
|
+
"Start a temporary standalone CAD/3D viewer endpoint"
|
|
3153
|
+
],
|
|
3154
|
+
[
|
|
3155
|
+
"/models hf list --modality 3d",
|
|
3156
|
+
"List curated and installed Hugging Face CAD/3D adapters"
|
|
3157
|
+
],
|
|
3158
|
+
[
|
|
3159
|
+
"/models evict <repo>",
|
|
3160
|
+
"Remove a cached HF repo from the unified store to reclaim disk"
|
|
3161
|
+
],
|
|
3162
|
+
[
|
|
3163
|
+
"/models evict --free <GB>",
|
|
3164
|
+
"LRU-evict cached models until <GB> of free disk is available"
|
|
3165
|
+
],
|
|
3166
|
+
[
|
|
3167
|
+
"/models cleanup",
|
|
3168
|
+
"Migrate legacy per-repo .omnius/{image,video,audio}-gen caches into the unified store and delete the originals"
|
|
3169
|
+
],
|
|
3170
|
+
[
|
|
3171
|
+
"/models cleanup --dry-run",
|
|
3172
|
+
"Preview /models cleanup without touching disk"
|
|
3173
|
+
],
|
|
3174
|
+
["/voicechat", "Start voice chat session (async voice conversation)"],
|
|
3175
|
+
["/voicechat stop", "Stop voice chat session"],
|
|
3176
|
+
["/memory", "Open the interactive memory pixel map and provenance trace"],
|
|
3177
|
+
["/memory legacy", "Open the legacy list-based memory browser"],
|
|
3178
|
+
[
|
|
3179
|
+
"/metabolize [--dry-run] [--verbose] [--min-score <n>]",
|
|
3180
|
+
"Run memory maintenance: import deferred memory, prune episodes, compact graph stores"
|
|
3181
|
+
],
|
|
3182
|
+
[
|
|
3183
|
+
"/prune [now|dry|infer|aggressive|stats] [--target <n>]",
|
|
3184
|
+
"Shrink knowledge.db in the background: evict low-signal graph nodes (archived), then VACUUM. 'infer' = model-guided sleep consolidation. No arg shows stats + usage"
|
|
3185
|
+
],
|
|
3186
|
+
["/verbose", "Toggle verbose mode"],
|
|
3187
|
+
["/clear", "Clear the screen"],
|
|
3188
|
+
["/help", "Show this help"],
|
|
3189
|
+
["/quit", "Exit omnius"]
|
|
3190
|
+
].map(([signature, description]) => ({ signature, description }));
|
|
3191
|
+
var PLANNED_SIGNATURES = [
|
|
3192
|
+
["/provider", "Show or select provider profile"],
|
|
3193
|
+
["/prompt", "Show or edit the active prompt template"],
|
|
3194
|
+
["/reasoning", "Alias family for thinking/reasoning controls"],
|
|
3195
|
+
["/statusbar", "Configure the status bar"],
|
|
3196
|
+
["/sb", "Alias for /statusbar"],
|
|
3197
|
+
["/skin", "Configure the visual skin/theme bundle"],
|
|
3198
|
+
["/toolsets", "Show or select tool exposure sets"],
|
|
3199
|
+
["/browser connect|disconnect|status", "Manage browser-agent connectivity"],
|
|
3200
|
+
["/reload-mcp", "Reload MCP servers and tools"],
|
|
3201
|
+
["/plugins", "List or manage plugins"],
|
|
3202
|
+
["/status", "Show overall runtime status"],
|
|
3203
|
+
["/sethome", "Set the project home for gateway/session use"],
|
|
3204
|
+
["/approve", "Approve a pending gateway/admin action"],
|
|
3205
|
+
["/deny", "Deny a pending gateway/admin action"]
|
|
3206
|
+
].map(([signature, description]) => ({
|
|
3207
|
+
signature,
|
|
3208
|
+
description,
|
|
3209
|
+
implementationStatus: "planned"
|
|
3210
|
+
}));
|
|
3211
|
+
var CATEGORY_OVERRIDES = {
|
|
3212
|
+
model: "model",
|
|
3213
|
+
models: "model",
|
|
3214
|
+
endpoint: "network",
|
|
3215
|
+
fallback: "network",
|
|
3216
|
+
provider: "network",
|
|
3217
|
+
parallel: "runtime",
|
|
3218
|
+
gpu: "runtime",
|
|
3219
|
+
broker: "runtime",
|
|
3220
|
+
setup: "runtime",
|
|
3221
|
+
wizard: "runtime",
|
|
3222
|
+
mcp: "tools",
|
|
3223
|
+
mcps: "tools",
|
|
3224
|
+
update: "admin",
|
|
3225
|
+
upgrade: "admin",
|
|
3226
|
+
voice: "media",
|
|
3227
|
+
voicechat: "media",
|
|
3228
|
+
listen: "media",
|
|
3229
|
+
mic: "media",
|
|
3230
|
+
paste: "media",
|
|
3231
|
+
image: "media",
|
|
3232
|
+
video: "media",
|
|
3233
|
+
pointcloud: "media",
|
|
3234
|
+
points: "media",
|
|
3235
|
+
pcl: "media",
|
|
3236
|
+
ingest: "media",
|
|
3237
|
+
transcribe: "media",
|
|
3238
|
+
call: "media",
|
|
3239
|
+
hangup: "media",
|
|
3240
|
+
queue: "session",
|
|
3241
|
+
q: "session",
|
|
3242
|
+
retry: "session",
|
|
3243
|
+
undo: "session",
|
|
3244
|
+
title: "session",
|
|
3245
|
+
rollback: "session",
|
|
3246
|
+
background: "session",
|
|
3247
|
+
bg: "session",
|
|
3248
|
+
pause: "session",
|
|
3249
|
+
stop: "session",
|
|
3250
|
+
resume: "session",
|
|
3251
|
+
new: "session",
|
|
3252
|
+
context: "session",
|
|
3253
|
+
compact: "session",
|
|
3254
|
+
compacted: "session",
|
|
3255
|
+
gc: "session",
|
|
3256
|
+
sessions: "session",
|
|
3257
|
+
session: "session",
|
|
3258
|
+
dream: "runtime",
|
|
3259
|
+
"full-send-bless": "runtime",
|
|
3260
|
+
bless: "runtime",
|
|
3261
|
+
telegram: "gateway",
|
|
3262
|
+
cohere: "network",
|
|
3263
|
+
nexus: "network",
|
|
3264
|
+
ipfs: "network",
|
|
3265
|
+
storage: "admin",
|
|
3266
|
+
files: "tools",
|
|
3267
|
+
workspace: "tools",
|
|
3268
|
+
"task-type": "runtime",
|
|
3269
|
+
tasktype: "runtime",
|
|
3270
|
+
sponsor: "network",
|
|
3271
|
+
expose: "gateway",
|
|
3272
|
+
access: "network",
|
|
3273
|
+
host: "network",
|
|
3274
|
+
network: "network",
|
|
3275
|
+
k: "network",
|
|
3276
|
+
scheduler: "runtime",
|
|
3277
|
+
reminder: "runtime",
|
|
3278
|
+
daemon: "runtime",
|
|
3279
|
+
indicator: "ui",
|
|
3280
|
+
apikey: "secret",
|
|
3281
|
+
key: "secret",
|
|
3282
|
+
secrets: "secret",
|
|
3283
|
+
secret: "secret",
|
|
3284
|
+
vault: "secret",
|
|
3285
|
+
codegraph: "tools",
|
|
3286
|
+
cg: "tools",
|
|
3287
|
+
p2p: "network",
|
|
3288
|
+
tools: "tools",
|
|
3289
|
+
skills: "tools",
|
|
3290
|
+
skill: "tools",
|
|
3291
|
+
skillify: "tools",
|
|
3292
|
+
memory: "memory",
|
|
3293
|
+
mem: "memory",
|
|
3294
|
+
metabolize: "memory",
|
|
3295
|
+
prune: "memory",
|
|
3296
|
+
mouse: "ui",
|
|
3297
|
+
color: "ui",
|
|
3298
|
+
colors: "ui",
|
|
3299
|
+
theme: "ui",
|
|
3300
|
+
neovim: "ui",
|
|
3301
|
+
nvim: "ui",
|
|
3302
|
+
vim: "ui",
|
|
3303
|
+
emojis: "ui",
|
|
3304
|
+
emoji: "ui",
|
|
3305
|
+
statusbar: "ui",
|
|
3306
|
+
sb: "ui",
|
|
3307
|
+
skin: "ui",
|
|
3308
|
+
prompt: "ui",
|
|
3309
|
+
personality: "ui",
|
|
3310
|
+
reasoning: "ui",
|
|
3311
|
+
selfmodify: "runtime",
|
|
3312
|
+
selfimprove: "runtime",
|
|
3313
|
+
"self-improve": "runtime",
|
|
3314
|
+
debug: "runtime",
|
|
3315
|
+
selfmod: "runtime",
|
|
3316
|
+
"self-modify": "runtime",
|
|
3317
|
+
stats: "session",
|
|
3318
|
+
metrics: "session"
|
|
3319
|
+
};
|
|
3320
|
+
var ALIASES = {
|
|
3321
|
+
help: ["h", "?"],
|
|
3322
|
+
quit: ["exit"],
|
|
3323
|
+
queue: ["q"],
|
|
3324
|
+
background: ["bg"],
|
|
3325
|
+
clear: ["cls"],
|
|
3326
|
+
verbose: ["v"],
|
|
3327
|
+
config: ["cfg"],
|
|
3328
|
+
color: ["colors", "theme"],
|
|
3329
|
+
setup: ["wizard"],
|
|
3330
|
+
mcp: ["mcps"],
|
|
3331
|
+
update: ["upgrade"],
|
|
3332
|
+
indicator: ["tray"],
|
|
3333
|
+
listen: ["mic"],
|
|
3334
|
+
pointcloud: ["points", "pcl", "point"],
|
|
3335
|
+
neovim: ["nvim"],
|
|
3336
|
+
apikey: ["key"],
|
|
3337
|
+
secrets: ["secret", "vault"],
|
|
3338
|
+
sessions: ["session"],
|
|
3339
|
+
cost: ["costs"],
|
|
3340
|
+
evaluate: ["eval"],
|
|
3341
|
+
score: ["inference"],
|
|
3342
|
+
"task-type": ["tasktype", "tt"],
|
|
3343
|
+
stats: ["dashboard"],
|
|
3344
|
+
memory: ["mem"],
|
|
3345
|
+
metabolize: ["metab"],
|
|
3346
|
+
files: ["workspace"],
|
|
3347
|
+
skills: ["skill"],
|
|
3348
|
+
commands: ["cmds"],
|
|
3349
|
+
selfmodify: ["selfmod", "self-modify"],
|
|
3350
|
+
selfimprove: ["self-improve"],
|
|
3351
|
+
codegraph: ["cg"],
|
|
3352
|
+
endpoint: ["ep"],
|
|
3353
|
+
telegram: ["tg"],
|
|
3354
|
+
bruteforce: ["brute"],
|
|
3355
|
+
deep: ["deep-context"],
|
|
3356
|
+
flow: ["flowstate"],
|
|
3357
|
+
emojis: ["emoji"],
|
|
3358
|
+
compact: ["gc"],
|
|
3359
|
+
network: ["k"],
|
|
3360
|
+
scheduler: [],
|
|
3361
|
+
reminder: ["remind", "reminders"],
|
|
3362
|
+
statusbar: ["sb"],
|
|
3363
|
+
platforms: ["gateway"]
|
|
3364
|
+
};
|
|
3365
|
+
var USER_ONLY = /* @__PURE__ */ new Set([
|
|
3366
|
+
"fallback",
|
|
3367
|
+
"quit",
|
|
3368
|
+
"exit",
|
|
3369
|
+
"clear",
|
|
3370
|
+
"cls",
|
|
3371
|
+
"q",
|
|
3372
|
+
"queue",
|
|
3373
|
+
"model",
|
|
3374
|
+
"endpoint",
|
|
3375
|
+
"parallel",
|
|
3376
|
+
"gpu",
|
|
3377
|
+
"memory",
|
|
3378
|
+
"broker",
|
|
3379
|
+
"commands",
|
|
3380
|
+
"cmds",
|
|
3381
|
+
"selfmodify",
|
|
3382
|
+
"selfmod",
|
|
3383
|
+
"self-modify",
|
|
3384
|
+
"selfimprove",
|
|
3385
|
+
"self-improve",
|
|
3386
|
+
"debug",
|
|
3387
|
+
"dbg",
|
|
3388
|
+
"hf",
|
|
3389
|
+
"models",
|
|
3390
|
+
"mcp",
|
|
3391
|
+
"mcps",
|
|
3392
|
+
"update",
|
|
3393
|
+
"upgrade",
|
|
3394
|
+
"setup",
|
|
3395
|
+
"wizard",
|
|
3396
|
+
"telegram",
|
|
3397
|
+
"call",
|
|
3398
|
+
"hangup",
|
|
3399
|
+
"listen",
|
|
3400
|
+
"mic",
|
|
3401
|
+
"paste",
|
|
3402
|
+
"image",
|
|
3403
|
+
"video",
|
|
3404
|
+
"pointcloud",
|
|
3405
|
+
"points",
|
|
3406
|
+
"pcl",
|
|
3407
|
+
"ingest",
|
|
3408
|
+
"transcribe",
|
|
3409
|
+
"expose",
|
|
3410
|
+
"p2p",
|
|
3411
|
+
"ipfs",
|
|
3412
|
+
"storage",
|
|
3413
|
+
"files",
|
|
3414
|
+
"workspace",
|
|
3415
|
+
"secrets",
|
|
3416
|
+
"secret",
|
|
3417
|
+
"vault",
|
|
3418
|
+
"skillify",
|
|
3419
|
+
"dream",
|
|
3420
|
+
"bless",
|
|
3421
|
+
"full-send-bless",
|
|
3422
|
+
"pause",
|
|
3423
|
+
"stop",
|
|
3424
|
+
"resume",
|
|
3425
|
+
"new",
|
|
3426
|
+
"retry",
|
|
3427
|
+
"undo",
|
|
3428
|
+
"rollback",
|
|
3429
|
+
"background",
|
|
3430
|
+
"bg",
|
|
3431
|
+
"apikey",
|
|
3432
|
+
"key",
|
|
3433
|
+
"access",
|
|
3434
|
+
"host",
|
|
3435
|
+
"network",
|
|
3436
|
+
"k",
|
|
3437
|
+
"scheduler",
|
|
3438
|
+
"cron",
|
|
3439
|
+
"daemon",
|
|
3440
|
+
"indicator",
|
|
3441
|
+
"compacted",
|
|
3442
|
+
"fortemi",
|
|
3443
|
+
"mouse",
|
|
3444
|
+
"approve",
|
|
3445
|
+
"deny",
|
|
3446
|
+
"sethome"
|
|
3447
|
+
]);
|
|
3448
|
+
var DESTRUCTIVE = /* @__PURE__ */ new Set([
|
|
3449
|
+
"destroy",
|
|
3450
|
+
"stop",
|
|
3451
|
+
"rollback",
|
|
3452
|
+
"update",
|
|
3453
|
+
"upgrade",
|
|
3454
|
+
"deny",
|
|
3455
|
+
"daemon",
|
|
3456
|
+
"ollama"
|
|
3457
|
+
]);
|
|
3458
|
+
var NETWORKED = /* @__PURE__ */ new Set([
|
|
3459
|
+
"endpoint",
|
|
3460
|
+
"update",
|
|
3461
|
+
"upgrade",
|
|
3462
|
+
"telegram",
|
|
3463
|
+
"call",
|
|
3464
|
+
"listen",
|
|
3465
|
+
"mic",
|
|
3466
|
+
"image",
|
|
3467
|
+
"video",
|
|
3468
|
+
"pointcloud",
|
|
3469
|
+
"points",
|
|
3470
|
+
"pcl",
|
|
3471
|
+
"cohere",
|
|
3472
|
+
"nexus",
|
|
3473
|
+
"ipfs",
|
|
3474
|
+
"sponsor",
|
|
3475
|
+
"expose",
|
|
3476
|
+
"access",
|
|
3477
|
+
"host",
|
|
3478
|
+
"network",
|
|
3479
|
+
"k",
|
|
3480
|
+
"p2p",
|
|
3481
|
+
"daemon",
|
|
3482
|
+
"fortemi",
|
|
3483
|
+
"mcp",
|
|
3484
|
+
"mcps",
|
|
3485
|
+
"browser",
|
|
3486
|
+
"cron",
|
|
3487
|
+
"reload-mcp",
|
|
3488
|
+
"plugins",
|
|
3489
|
+
"platforms",
|
|
3490
|
+
"gateway"
|
|
3491
|
+
]);
|
|
3492
|
+
var SECRET_BEARING = /* @__PURE__ */ new Set([
|
|
3493
|
+
"endpoint",
|
|
3494
|
+
"apikey",
|
|
3495
|
+
"key",
|
|
3496
|
+
"telegram",
|
|
3497
|
+
"secrets",
|
|
3498
|
+
"secret",
|
|
3499
|
+
"vault",
|
|
3500
|
+
"hf"
|
|
3501
|
+
]);
|
|
3502
|
+
var PROFILE_GATED = /* @__PURE__ */ new Set([
|
|
3503
|
+
"destroy",
|
|
3504
|
+
"update",
|
|
3505
|
+
"upgrade",
|
|
3506
|
+
"telegram",
|
|
3507
|
+
"call",
|
|
3508
|
+
"listen",
|
|
3509
|
+
"mic",
|
|
3510
|
+
"expose",
|
|
3511
|
+
"access",
|
|
3512
|
+
"host",
|
|
3513
|
+
"network",
|
|
3514
|
+
"k",
|
|
3515
|
+
"p2p",
|
|
3516
|
+
"daemon",
|
|
3517
|
+
"fortemi",
|
|
3518
|
+
"mcp",
|
|
3519
|
+
"mcps",
|
|
3520
|
+
"ipfs",
|
|
3521
|
+
"approve",
|
|
3522
|
+
"deny",
|
|
3523
|
+
"sethome"
|
|
3524
|
+
]);
|
|
3525
|
+
var REST_BLOCKED = /* @__PURE__ */ new Set([
|
|
3526
|
+
"pause",
|
|
3527
|
+
"resume",
|
|
3528
|
+
"new",
|
|
3529
|
+
"retry",
|
|
3530
|
+
"undo",
|
|
3531
|
+
"rollback",
|
|
3532
|
+
"background",
|
|
3533
|
+
"bg",
|
|
3534
|
+
"paste",
|
|
3535
|
+
"indicator"
|
|
3536
|
+
]);
|
|
3537
|
+
var LOCAL_TUI_ONLY = /* @__PURE__ */ new Set(["indicator", "compacted", "gpu", "memory"]);
|
|
3538
|
+
var CANONICAL_BY_ALIAS = /* @__PURE__ */ new Map();
|
|
3539
|
+
for (const [canonical, aliases2] of Object.entries(ALIASES)) {
|
|
3540
|
+
for (const alias of aliases2) CANONICAL_BY_ALIAS.set(alias, canonical);
|
|
3541
|
+
}
|
|
3542
|
+
var DYNAMIC_COMMANDS = [];
|
|
3543
|
+
function getSlashHelpEntries(opts = {}) {
|
|
3544
|
+
const includePlanned = opts.includePlanned === true;
|
|
3545
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3546
|
+
const out = [];
|
|
3547
|
+
for (const item of [
|
|
3548
|
+
...DYNAMIC_COMMANDS,
|
|
3549
|
+
...COMMAND_SIGNATURES,
|
|
3550
|
+
...PLANNED_SIGNATURES
|
|
3551
|
+
]) {
|
|
3552
|
+
if (!includePlanned && item.implementationStatus === "planned") continue;
|
|
3553
|
+
if (seen.has(item.signature)) continue;
|
|
3554
|
+
seen.add(item.signature);
|
|
3555
|
+
out.push([item.signature, item.description]);
|
|
3556
|
+
}
|
|
3557
|
+
return out;
|
|
3558
|
+
}
|
|
3559
|
+
var SLASH_COMMANDS = getSlashHelpEntries();
|
|
3560
|
+
function listCommandRegistry(opts = {}) {
|
|
3561
|
+
const includePlanned = opts.includePlanned !== false;
|
|
3562
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3563
|
+
for (const sig of [
|
|
3564
|
+
...COMMAND_SIGNATURES,
|
|
3565
|
+
...PLANNED_SIGNATURES,
|
|
3566
|
+
...DYNAMIC_COMMANDS
|
|
3567
|
+
]) {
|
|
3568
|
+
if (!includePlanned && sig.implementationStatus === "planned") continue;
|
|
3569
|
+
const name = commandNameFromSignature(sig.signature);
|
|
3570
|
+
if (!name || name.startsWith("<")) continue;
|
|
3571
|
+
if (!grouped.has(name)) grouped.set(name, []);
|
|
3572
|
+
grouped.get(name).push(sig);
|
|
3573
|
+
}
|
|
3574
|
+
return Array.from(grouped.entries()).map(
|
|
3575
|
+
([name, signatures]) => buildCommandDef(name, signatures)
|
|
3576
|
+
);
|
|
3577
|
+
}
|
|
3578
|
+
function findCommand(name) {
|
|
3579
|
+
const normalized = normalizeCommandName(name);
|
|
3580
|
+
const all = listCommandRegistry({ includePlanned: true });
|
|
3581
|
+
return all.find(
|
|
3582
|
+
(cmd) => cmd.name === normalized || cmd.aliases.includes(normalized)
|
|
3583
|
+
);
|
|
3584
|
+
}
|
|
3585
|
+
function buildCommandDef(name, signatures) {
|
|
3586
|
+
const status = signatures.every(
|
|
3587
|
+
(sig) => sig.implementationStatus === "planned"
|
|
3588
|
+
) ? "planned" : "implemented";
|
|
3589
|
+
const safety = {
|
|
3590
|
+
userOnly: USER_ONLY.has(name),
|
|
3591
|
+
destructive: DESTRUCTIVE.has(name),
|
|
3592
|
+
networked: NETWORKED.has(name),
|
|
3593
|
+
secretBearing: SECRET_BEARING.has(name),
|
|
3594
|
+
profileGated: PROFILE_GATED.has(name)
|
|
3595
|
+
};
|
|
3596
|
+
const aliases2 = Array.from(new Set(ALIASES[name] ?? []));
|
|
3597
|
+
const category = CATEGORY_OVERRIDES[name] ?? (status === "planned" ? "planned" : "core");
|
|
3598
|
+
const subcommands = Array.from(
|
|
3599
|
+
new Set(
|
|
3600
|
+
signatures.map((sig) => subcommandFromSignature(sig.signature)).filter((sub) => !!sub)
|
|
3601
|
+
)
|
|
3602
|
+
);
|
|
3603
|
+
const argsHint = inferArgsHint(signatures[0]?.signature ?? `/${name}`);
|
|
3604
|
+
const surfaces = {
|
|
3605
|
+
tui: true,
|
|
3606
|
+
rest: status === "implemented" && !["quit", "exit"].includes(name) && !safety.destructive && !safety.secretBearing && !REST_BLOCKED.has(name) && !LOCAL_TUI_ONLY.has(name),
|
|
3607
|
+
gateway: status === "implemented" && !safety.destructive && !safety.secretBearing && !LOCAL_TUI_ONLY.has(name),
|
|
3608
|
+
agentTool: status === "implemented" && !safety.userOnly && !safety.destructive && !safety.secretBearing && !safety.profileGated && !LOCAL_TUI_ONLY.has(name)
|
|
3609
|
+
};
|
|
3610
|
+
return {
|
|
3611
|
+
name,
|
|
3612
|
+
aliases: aliases2,
|
|
3613
|
+
description: signatures[0]?.description ?? "",
|
|
3614
|
+
category,
|
|
3615
|
+
argsHint,
|
|
3616
|
+
subcommands,
|
|
3617
|
+
signatures,
|
|
3618
|
+
surfaces,
|
|
3619
|
+
safety,
|
|
3620
|
+
implementationStatus: status
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
function commandNameFromSignature(signature) {
|
|
3624
|
+
const raw = normalizeCommandName(
|
|
3625
|
+
(signature.trim().split(/\s+/)[0] ?? "").replace(/^\//, "")
|
|
3626
|
+
);
|
|
3627
|
+
return CANONICAL_BY_ALIAS.get(raw) ?? raw;
|
|
3628
|
+
}
|
|
3629
|
+
function subcommandFromSignature(signature) {
|
|
3630
|
+
const parts = signature.trim().split(/\s+/);
|
|
3631
|
+
if (parts.length < 2) return null;
|
|
3632
|
+
const sub = parts[1] ?? "";
|
|
3633
|
+
if (!sub || sub.startsWith("<") || sub.startsWith("[") || sub.includes("|"))
|
|
3634
|
+
return null;
|
|
3635
|
+
return sub.replace(/[|].*$/, "");
|
|
3636
|
+
}
|
|
3637
|
+
function inferArgsHint(signature) {
|
|
3638
|
+
return signature.trim().split(/\s+/).slice(1).join(" ");
|
|
3639
|
+
}
|
|
3640
|
+
function normalizeCommandName(name) {
|
|
3641
|
+
return name.replace(/^\//, "").trim().toLowerCase();
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
// packages/cli/src/tui/native/components/vim-composer.ts
|
|
3645
|
+
var VimComposer = class {
|
|
3646
|
+
constructor(editor, changed) {
|
|
3647
|
+
this.editor = editor;
|
|
3648
|
+
this.changed = changed;
|
|
3649
|
+
}
|
|
3650
|
+
editor;
|
|
3651
|
+
changed;
|
|
3652
|
+
enabled = false;
|
|
3653
|
+
mode = "INSERT";
|
|
3654
|
+
count = "";
|
|
3655
|
+
operator = "";
|
|
3656
|
+
operatorCount = 1;
|
|
3657
|
+
anchor = 0;
|
|
3658
|
+
register = "";
|
|
3659
|
+
linewise = false;
|
|
3660
|
+
find = "";
|
|
3661
|
+
search;
|
|
3662
|
+
lastSearch;
|
|
3663
|
+
setEnabled(enabled) {
|
|
3664
|
+
if (enabled === this.enabled) return;
|
|
3665
|
+
this.enabled = enabled;
|
|
3666
|
+
this.mode = "INSERT";
|
|
3667
|
+
this.reset();
|
|
3668
|
+
this.editor.clearSelection();
|
|
3669
|
+
this.changed();
|
|
3670
|
+
}
|
|
3671
|
+
get label() {
|
|
3672
|
+
return this.search ? `${this.search.backward ? "?" : "/"}${this.search.value}` : this.mode;
|
|
3673
|
+
}
|
|
3674
|
+
reset() {
|
|
3675
|
+
this.count = "";
|
|
3676
|
+
this.operator = "";
|
|
3677
|
+
this.operatorCount = 1;
|
|
3678
|
+
this.find = "";
|
|
3679
|
+
this.search = void 0;
|
|
3680
|
+
}
|
|
3681
|
+
normal() {
|
|
3682
|
+
this.mode = "NORMAL";
|
|
3683
|
+
this.editor.clearSelection();
|
|
3684
|
+
this.reset();
|
|
3685
|
+
this.changed();
|
|
3686
|
+
}
|
|
3687
|
+
end() {
|
|
3688
|
+
return this.editor.editBuffer.measureText(this.editor.plainText);
|
|
3689
|
+
}
|
|
3690
|
+
takeCount() {
|
|
3691
|
+
const n = Math.max(1, Math.min(1e4, Number(this.count) || 1));
|
|
3692
|
+
this.count = "";
|
|
3693
|
+
return n;
|
|
3694
|
+
}
|
|
3695
|
+
select(start, end) {
|
|
3696
|
+
this.editor.setSelection(Math.min(start, end), Math.max(start, end));
|
|
3697
|
+
}
|
|
3698
|
+
operate(start, end, operation, linewise = false) {
|
|
3699
|
+
this.select(start, end);
|
|
3700
|
+
this.register = this.editor.getSelectedText();
|
|
3701
|
+
this.linewise = linewise;
|
|
3702
|
+
if (linewise && this.register && !this.register.endsWith("\n")) this.register += "\n";
|
|
3703
|
+
if (operation !== "y") this.editor.deleteSelection();
|
|
3704
|
+
else {
|
|
3705
|
+
this.editor.clearSelection();
|
|
3706
|
+
this.editor.cursorOffset = Math.min(start, end);
|
|
3707
|
+
}
|
|
3708
|
+
this.reset();
|
|
3709
|
+
this.mode = operation === "c" ? "INSERT" : "NORMAL";
|
|
3710
|
+
this.changed();
|
|
3711
|
+
}
|
|
3712
|
+
lineRange(count) {
|
|
3713
|
+
const row = this.editor.logicalCursor.row;
|
|
3714
|
+
const start = this.editor.editBuffer.getLineStartOffset(row);
|
|
3715
|
+
const next = row + count;
|
|
3716
|
+
let end = next < this.editor.lineCount ? this.editor.editBuffer.getLineStartOffset(next) : this.end();
|
|
3717
|
+
if (end === start && row > 0) return [start - 1, end];
|
|
3718
|
+
return [start, end];
|
|
3719
|
+
}
|
|
3720
|
+
motion(key, count) {
|
|
3721
|
+
const editor = this.editor;
|
|
3722
|
+
if (key === "0") editor.gotoLineHome();
|
|
3723
|
+
else if (key === "$") {
|
|
3724
|
+
if (count > 1) editor.gotoLine(Math.min(editor.lineCount - 1, editor.logicalCursor.row + count - 1));
|
|
3725
|
+
editor.gotoLineEnd();
|
|
3726
|
+
} else if (key === "^") {
|
|
3727
|
+
editor.gotoLineHome();
|
|
3728
|
+
const line = editor.getTextRange(editor.cursorOffset, this.end()).split("\n")[0];
|
|
3729
|
+
for (const char of line) {
|
|
3730
|
+
if (!/\s/.test(char)) break;
|
|
3731
|
+
editor.moveCursorRight();
|
|
3732
|
+
}
|
|
3733
|
+
} else if (key === "G") {
|
|
3734
|
+
editor.gotoLine(count > 1 ? Math.min(editor.lineCount - 1, count - 1) : editor.lineCount - 1);
|
|
3735
|
+
editor.gotoLineHome();
|
|
3736
|
+
} else if (key === "gg") {
|
|
3737
|
+
editor.gotoLine(Math.max(0, count - 1));
|
|
3738
|
+
editor.gotoLineHome();
|
|
3739
|
+
} else if (key === "e") {
|
|
3740
|
+
for (let i = 0; i < count; i++) {
|
|
3741
|
+
const start = editor.cursorOffset;
|
|
3742
|
+
const endWord = () => {
|
|
3743
|
+
editor.moveWordForward();
|
|
3744
|
+
const nextWord2 = editor.cursorOffset;
|
|
3745
|
+
editor.moveCursorLeft();
|
|
3746
|
+
while (editor.cursorOffset > start && /\s/u.test(editor.getTextRange(editor.cursorOffset, nextWord2).slice(0, 1))) editor.moveCursorLeft();
|
|
3747
|
+
return nextWord2;
|
|
3748
|
+
};
|
|
3749
|
+
const nextWord = endWord();
|
|
3750
|
+
if (editor.cursorOffset <= start && nextWord < this.end()) {
|
|
3751
|
+
editor.cursorOffset = nextWord;
|
|
3752
|
+
endWord();
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
} else if (["h", "l", "j", "k", "w", "b", "left", "right", "up", "down"].includes(key)) {
|
|
3756
|
+
for (let i = 0; i < count; i++) {
|
|
3757
|
+
if (key === "h" || key === "left") editor.moveCursorLeft();
|
|
3758
|
+
else if (key === "l" || key === "right") editor.moveCursorRight();
|
|
3759
|
+
else if (key === "j" || key === "down") editor.moveCursorDown();
|
|
3760
|
+
else if (key === "k" || key === "up") editor.moveCursorUp();
|
|
3761
|
+
else if (key === "w") editor.moveWordForward();
|
|
3762
|
+
else editor.moveWordBackward();
|
|
3763
|
+
}
|
|
3764
|
+
} else return false;
|
|
3765
|
+
return true;
|
|
3766
|
+
}
|
|
3767
|
+
searchFor(value, backward) {
|
|
3768
|
+
if (!value) return;
|
|
3769
|
+
const text = this.editor.plainText;
|
|
3770
|
+
const character = this.editor.getTextRange(0, this.editor.cursorOffset).length;
|
|
3771
|
+
const index = backward ? character > 0 ? text.lastIndexOf(value, character - 1) : -1 : text.indexOf(value, character + 1);
|
|
3772
|
+
const found = index >= 0 ? index : backward ? text.lastIndexOf(value) : text.indexOf(value);
|
|
3773
|
+
if (found >= 0) this.editor.cursorOffset = this.editor.editBuffer.measureText(text.slice(0, found));
|
|
3774
|
+
}
|
|
3775
|
+
handle(key) {
|
|
3776
|
+
if (!this.enabled || key.eventType === "release") return false;
|
|
3777
|
+
const consume = () => {
|
|
3778
|
+
key.preventDefault();
|
|
3779
|
+
key.stopPropagation();
|
|
3780
|
+
return true;
|
|
3781
|
+
};
|
|
3782
|
+
if (this.mode === "INSERT") {
|
|
3783
|
+
if (key.name !== "escape") return false;
|
|
3784
|
+
if (this.editor.logicalCursor.col > 0) this.editor.moveCursorLeft();
|
|
3785
|
+
this.normal();
|
|
3786
|
+
return consume();
|
|
3787
|
+
}
|
|
3788
|
+
if (key.name === "escape") {
|
|
3789
|
+
this.normal();
|
|
3790
|
+
return consume();
|
|
3791
|
+
}
|
|
3792
|
+
if (key.ctrl && key.name === "r") {
|
|
3793
|
+
this.editor.redo();
|
|
3794
|
+
return consume();
|
|
3795
|
+
}
|
|
3796
|
+
if (key.ctrl || key.meta || key.name === "return" && !this.search || key.name === "tab") return false;
|
|
3797
|
+
const char = key.sequence.length === 1 ? key.sequence : key.name;
|
|
3798
|
+
if (this.search) {
|
|
3799
|
+
if (key.name === "return") {
|
|
3800
|
+
this.lastSearch = this.search;
|
|
3801
|
+
this.searchFor(this.search.value, this.search.backward);
|
|
3802
|
+
this.search = void 0;
|
|
3803
|
+
} else if (key.name === "backspace") this.search.value = [...this.search.value].slice(0, -1).join("");
|
|
3804
|
+
else if (key.sequence.length === 1) this.search.value += key.sequence;
|
|
3805
|
+
this.changed();
|
|
3806
|
+
return consume();
|
|
3807
|
+
}
|
|
3808
|
+
if (this.find) {
|
|
3809
|
+
if (this.find === "r") {
|
|
3810
|
+
const start2 = this.editor.cursorOffset;
|
|
3811
|
+
this.editor.moveCursorRight();
|
|
3812
|
+
if (this.editor.cursorOffset !== start2) {
|
|
3813
|
+
this.select(start2, this.editor.cursorOffset);
|
|
3814
|
+
this.editor.insertText(char);
|
|
3815
|
+
this.editor.cursorOffset = start2;
|
|
3816
|
+
}
|
|
3817
|
+
this.find = "";
|
|
3818
|
+
return consume();
|
|
3819
|
+
}
|
|
3820
|
+
const forward = this.find === "f" || this.find === "t", till = this.find === "t" || this.find === "T";
|
|
3821
|
+
const start = this.editor.cursorOffset, row = this.editor.logicalCursor.row;
|
|
3822
|
+
for (let i = 0; i < this.editor.plainText.length; i++) {
|
|
3823
|
+
const previous = this.editor.cursorOffset;
|
|
3824
|
+
if (forward) this.editor.moveCursorRight();
|
|
3825
|
+
else this.editor.moveCursorLeft();
|
|
3826
|
+
const current = this.editor.cursorOffset;
|
|
3827
|
+
if (current === previous || this.editor.logicalCursor.row !== row) {
|
|
3828
|
+
this.editor.cursorOffset = start;
|
|
3829
|
+
break;
|
|
3830
|
+
}
|
|
3831
|
+
const end = current;
|
|
3832
|
+
this.editor.moveCursorRight();
|
|
3833
|
+
const next = this.editor.cursorOffset;
|
|
3834
|
+
this.editor.cursorOffset = end;
|
|
3835
|
+
if (this.editor.getTextRange(end, next) === char) {
|
|
3836
|
+
if (till) {
|
|
3837
|
+
if (forward) this.editor.moveCursorLeft();
|
|
3838
|
+
else this.editor.moveCursorRight();
|
|
3839
|
+
}
|
|
3840
|
+
break;
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
this.find = "";
|
|
3844
|
+
this.changed();
|
|
3845
|
+
return consume();
|
|
3846
|
+
}
|
|
3847
|
+
if (/^[1-9]$/.test(char) || char === "0" && this.count) {
|
|
3848
|
+
this.count = (this.count + char).slice(0, 5);
|
|
3849
|
+
return consume();
|
|
3850
|
+
}
|
|
3851
|
+
const count = this.takeCount();
|
|
3852
|
+
if (this.operator === "g") {
|
|
3853
|
+
this.operator = "";
|
|
3854
|
+
if (char === "g") {
|
|
3855
|
+
this.motion("gg", this.operatorCount);
|
|
3856
|
+
return consume();
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
if (this.operator && this.operator !== "g") {
|
|
3860
|
+
const op = this.operator, repeat = count * this.operatorCount, start = this.editor.cursorOffset;
|
|
3861
|
+
if (char === op) {
|
|
3862
|
+
const range = this.lineRange(repeat);
|
|
3863
|
+
this.operate(...range, op, true);
|
|
3864
|
+
return consume();
|
|
3865
|
+
}
|
|
3866
|
+
if (this.motion(char, Math.min(1e4, repeat))) this.operate(start, this.editor.cursorOffset, op);
|
|
3867
|
+
else this.reset();
|
|
3868
|
+
return consume();
|
|
3869
|
+
}
|
|
3870
|
+
if (this.mode === "VISUAL" && ["d", "x", "c", "y"].includes(char)) {
|
|
3871
|
+
const selection = this.editor.getSelection();
|
|
3872
|
+
if (selection) this.operate(selection.start, selection.end, char === "x" ? "d" : char);
|
|
3873
|
+
return consume();
|
|
3874
|
+
}
|
|
3875
|
+
if (this.motion(char, count)) {
|
|
3876
|
+
if (this.mode === "VISUAL") this.select(this.anchor, this.editor.cursorOffset);
|
|
3877
|
+
return consume();
|
|
3878
|
+
}
|
|
3879
|
+
if (char === "g") {
|
|
3880
|
+
this.operator = "g";
|
|
3881
|
+
this.operatorCount = count;
|
|
3882
|
+
return consume();
|
|
3883
|
+
}
|
|
3884
|
+
if (["d", "c", "y"].includes(char)) {
|
|
3885
|
+
this.operator = char;
|
|
3886
|
+
this.operatorCount = count;
|
|
3887
|
+
return consume();
|
|
3888
|
+
}
|
|
3889
|
+
if (char === "i" || char === "a" || char === "I" || char === "A") {
|
|
3890
|
+
if (char === "a" && this.editor.cursorOffset < this.end()) this.editor.moveCursorRight();
|
|
3891
|
+
if (char === "I") this.motion("^", 1);
|
|
3892
|
+
if (char === "A") this.editor.gotoLineEnd();
|
|
3893
|
+
this.mode = "INSERT";
|
|
3894
|
+
this.reset();
|
|
3895
|
+
this.changed();
|
|
3896
|
+
return consume();
|
|
3897
|
+
}
|
|
3898
|
+
if (char === "o" || char === "O") {
|
|
3899
|
+
if (char === "o") {
|
|
3900
|
+
this.editor.gotoLineEnd();
|
|
3901
|
+
this.editor.newLine();
|
|
3902
|
+
} else {
|
|
3903
|
+
this.editor.gotoLineHome();
|
|
3904
|
+
this.editor.insertText("\n");
|
|
3905
|
+
this.editor.moveCursorLeft();
|
|
3906
|
+
}
|
|
3907
|
+
this.mode = "INSERT";
|
|
3908
|
+
this.changed();
|
|
3909
|
+
return consume();
|
|
3910
|
+
}
|
|
3911
|
+
if (char === "v") {
|
|
3912
|
+
if (this.mode === "VISUAL") this.normal();
|
|
3913
|
+
else {
|
|
3914
|
+
this.mode = "VISUAL";
|
|
3915
|
+
this.anchor = this.editor.cursorOffset;
|
|
3916
|
+
this.changed();
|
|
3917
|
+
}
|
|
3918
|
+
return consume();
|
|
3919
|
+
}
|
|
3920
|
+
if (char === "x" || char === "X") {
|
|
3921
|
+
const start = this.editor.cursorOffset;
|
|
3922
|
+
for (let i = 0; i < count; i++) char === "x" ? this.editor.moveCursorRight() : this.editor.moveCursorLeft();
|
|
3923
|
+
this.operate(start, this.editor.cursorOffset, "d");
|
|
3924
|
+
return consume();
|
|
3925
|
+
}
|
|
3926
|
+
if (char === "D" || char === "C") {
|
|
3927
|
+
const start = this.editor.cursorOffset;
|
|
3928
|
+
this.editor.gotoLineEnd();
|
|
3929
|
+
this.operate(start, this.editor.cursorOffset, char === "C" ? "c" : "d");
|
|
3930
|
+
return consume();
|
|
3931
|
+
}
|
|
3932
|
+
if (char === "p" || char === "P") {
|
|
3933
|
+
if (this.register) {
|
|
3934
|
+
if (this.linewise) {
|
|
3935
|
+
if (char === "P") this.editor.gotoLineHome();
|
|
3936
|
+
else {
|
|
3937
|
+
this.editor.gotoLineEnd();
|
|
3938
|
+
if (this.editor.cursorOffset < this.end()) this.editor.moveCursorRight();
|
|
3939
|
+
else this.editor.insertText("\n");
|
|
3940
|
+
}
|
|
3941
|
+
} else if (char === "p") this.editor.moveCursorRight();
|
|
3942
|
+
this.editor.insertText(this.register.repeat(Math.min(1e3, count)));
|
|
3943
|
+
}
|
|
3944
|
+
return consume();
|
|
3945
|
+
}
|
|
3946
|
+
if (char === "u") {
|
|
3947
|
+
for (let i = 0; i < count; i++) this.editor.undo();
|
|
3948
|
+
return consume();
|
|
3949
|
+
}
|
|
3950
|
+
if (["f", "F", "t", "T", "r"].includes(char)) {
|
|
3951
|
+
this.find = char;
|
|
3952
|
+
return consume();
|
|
3953
|
+
}
|
|
3954
|
+
if (char === "/" || char === "?") {
|
|
3955
|
+
this.search = { value: "", backward: char === "?" };
|
|
3956
|
+
this.changed();
|
|
3957
|
+
return consume();
|
|
3958
|
+
}
|
|
3959
|
+
if ((char === "n" || char === "N") && this.lastSearch) {
|
|
3960
|
+
this.searchFor(this.lastSearch.value, this.lastSearch.backward !== (char === "N"));
|
|
3961
|
+
return consume();
|
|
3962
|
+
}
|
|
3963
|
+
return consume();
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
|
|
3967
|
+
// packages/cli/src/tui/native/components/frontend.ts
|
|
3968
|
+
var initialTheme = { mode: "system", bg: -1, accent: -1, textPrimary: -1, textDim: -1, boxColor: -1 };
|
|
3969
|
+
var NativeFrontend = class {
|
|
3970
|
+
constructor(renderer2, emit) {
|
|
3971
|
+
this.renderer = renderer2;
|
|
3972
|
+
this.emit = emit;
|
|
3973
|
+
const colors = palette(initialTheme);
|
|
3974
|
+
this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map() };
|
|
3975
|
+
this.disposeSelectionContrast = installDefaultSelectionContrast(renderer2);
|
|
3976
|
+
this.clipboard = createClipboard({ host: createHostClipboard({ timeoutMs: 1500 }), terminal: createRendererClipboardAdapter(renderer2) });
|
|
3977
|
+
this.root = new BoxRenderable5(renderer2, {
|
|
3978
|
+
id: "omnius-native",
|
|
3979
|
+
width: "100%",
|
|
3980
|
+
height: "100%",
|
|
3981
|
+
flexDirection: "column",
|
|
3982
|
+
backgroundColor: colors.background
|
|
3983
|
+
});
|
|
3984
|
+
renderer2.root.add(this.root);
|
|
3985
|
+
this.header = new BoxRenderable5(renderer2, {
|
|
3986
|
+
id: "omnius-header",
|
|
3987
|
+
width: "100%",
|
|
3988
|
+
minWidth: 0,
|
|
3989
|
+
flexDirection: "column",
|
|
3990
|
+
flexShrink: 0,
|
|
3991
|
+
border: ["bottom"],
|
|
3992
|
+
borderStyle: "single",
|
|
3993
|
+
borderColor: colors.border
|
|
3994
|
+
});
|
|
3995
|
+
this.root.add(this.header);
|
|
3996
|
+
const headerRow = new BoxRenderable5(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
|
|
3997
|
+
this.header.add(headerRow);
|
|
3998
|
+
this.identity = new TextRenderable4(renderer2, {
|
|
3999
|
+
id: "omnius-identity",
|
|
4000
|
+
content: "Omnius",
|
|
4001
|
+
fg: colors.foreground,
|
|
4002
|
+
flexShrink: 0,
|
|
4003
|
+
flexGrow: 0,
|
|
4004
|
+
wrapMode: "none",
|
|
4005
|
+
truncate: true,
|
|
4006
|
+
minWidth: 18,
|
|
4007
|
+
selectable: false,
|
|
4008
|
+
attributes: TextAttributes6.BOLD,
|
|
4009
|
+
onMouseDown: (event) => {
|
|
4010
|
+
if (event.button === 0) {
|
|
4011
|
+
this.toggleHeaderPanel();
|
|
4012
|
+
event.stopPropagation();
|
|
4013
|
+
}
|
|
4014
|
+
},
|
|
4015
|
+
onKeyDown: (key) => {
|
|
4016
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
4017
|
+
this.toggleHeaderPanel();
|
|
4018
|
+
key.preventDefault();
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
});
|
|
4022
|
+
headerRow.add(this.identity);
|
|
4023
|
+
this.headerActions = new ScrollBoxRenderable4(renderer2, {
|
|
4024
|
+
id: "omnius-header-actions",
|
|
4025
|
+
flexGrow: 0,
|
|
4026
|
+
flexShrink: 1,
|
|
4027
|
+
minWidth: 0,
|
|
4028
|
+
minHeight: 1,
|
|
4029
|
+
scrollX: true,
|
|
4030
|
+
scrollY: false,
|
|
4031
|
+
viewportOptions: { height: 1, minHeight: 1, maxHeight: 1, flexGrow: 0, flexShrink: 0 },
|
|
4032
|
+
contentOptions: { flexDirection: "row", gap: 1, justifyContent: "flex-end" },
|
|
4033
|
+
verticalScrollbarOptions: { visible: false }
|
|
4034
|
+
});
|
|
4035
|
+
headerRow.add(this.headerActions);
|
|
4036
|
+
this.headerPanel = new TextRenderable4(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
|
|
4037
|
+
this.header.add(this.headerPanel);
|
|
4038
|
+
this.detailButton = new TextRenderable4(renderer2, {
|
|
4039
|
+
id: "omnius-details-toggle",
|
|
4040
|
+
content: "+ DETAILS",
|
|
4041
|
+
height: 1,
|
|
4042
|
+
width: 9,
|
|
4043
|
+
flexShrink: 0,
|
|
4044
|
+
fg: colors.foreground,
|
|
4045
|
+
attributes: TextAttributes6.DIM,
|
|
4046
|
+
selectable: false,
|
|
4047
|
+
onMouseDown: (event) => {
|
|
4048
|
+
if (event.button === 0) {
|
|
4049
|
+
this.toggleDetails();
|
|
4050
|
+
event.stopPropagation();
|
|
4051
|
+
}
|
|
4052
|
+
},
|
|
4053
|
+
onKeyDown: (key) => {
|
|
4054
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
4055
|
+
this.toggleDetails();
|
|
4056
|
+
key.preventDefault();
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
});
|
|
4060
|
+
this.detailButton.focusable = true;
|
|
4061
|
+
headerRow.add(this.detailButton, 1);
|
|
4062
|
+
for (const control of [this.identity, this.detailButton]) {
|
|
4063
|
+
const attributes = control.attributes;
|
|
4064
|
+
control.on("focused", () => {
|
|
4065
|
+
control.attributes = attributes | TextAttributes6.INVERSE;
|
|
4066
|
+
});
|
|
4067
|
+
control.on("blurred", () => {
|
|
4068
|
+
control.attributes = attributes;
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
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 });
|
|
4072
|
+
this.root.add(this.routeWarning);
|
|
4073
|
+
this.tabs = new TabSelectRenderable(renderer2, {
|
|
4074
|
+
id: "omnius-agent-tabs",
|
|
4075
|
+
width: "100%",
|
|
4076
|
+
height: 2,
|
|
1063
4077
|
showDescription: false,
|
|
1064
|
-
showUnderline:
|
|
1065
|
-
showScrollArrows:
|
|
4078
|
+
showUnderline: true,
|
|
4079
|
+
showScrollArrows: false,
|
|
1066
4080
|
tabWidth: 18,
|
|
1067
4081
|
wrapSelection: true,
|
|
1068
4082
|
visible: false,
|
|
@@ -1082,16 +4096,26 @@ var NativeFrontend = class {
|
|
|
1082
4096
|
this.tabs.on("selectionChanged", () => {
|
|
1083
4097
|
if (this.changing) return;
|
|
1084
4098
|
const selected = this.tabs.getSelectedOption();
|
|
1085
|
-
if (selected) this.
|
|
4099
|
+
if (selected) this.emitAction("view", String(selected.value));
|
|
1086
4100
|
this.editor.focus();
|
|
1087
4101
|
});
|
|
4102
|
+
this.tabs.renderAfter = (buffer) => {
|
|
4103
|
+
if (this.tabs.height < 2) return;
|
|
4104
|
+
const colors2 = this.context.colors;
|
|
4105
|
+
const tabWidth = Math.min(this.tabs.getTabWidth(), this.tabs.width);
|
|
4106
|
+
const count = Math.max(1, Math.floor(this.tabs.width / Math.max(1, tabWidth)));
|
|
4107
|
+
const selected = this.tabs.getSelectedIndex();
|
|
4108
|
+
const start = Math.max(0, Math.min(selected - Math.floor(count / 2), this.tabs.options.length - count));
|
|
4109
|
+
buffer.fillRect(0, 1, this.tabs.width, 1, colors2.background);
|
|
4110
|
+
buffer.drawText("─".repeat(Math.max(0, tabWidth)), (selected - start) * tabWidth, 1, colors2.foreground, colors2.background, TextAttributes6.DIM);
|
|
4111
|
+
};
|
|
1088
4112
|
this.root.add(this.tabs);
|
|
1089
|
-
this.taskPane = new
|
|
4113
|
+
this.taskPane = new BoxRenderable5(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
|
|
1090
4114
|
this.tasks = new NativeBlockList(this.context, this.taskPane);
|
|
1091
4115
|
this.root.add(this.taskPane);
|
|
1092
|
-
this.body = new
|
|
4116
|
+
this.body = new BoxRenderable5(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
|
|
1093
4117
|
this.root.add(this.body);
|
|
1094
|
-
this.scroll = new
|
|
4118
|
+
this.scroll = new ScrollBoxRenderable4(renderer2, {
|
|
1095
4119
|
id: "omnius-conversation",
|
|
1096
4120
|
flexGrow: 1,
|
|
1097
4121
|
flexShrink: 1,
|
|
@@ -1127,18 +4151,34 @@ var NativeFrontend = class {
|
|
|
1127
4151
|
});
|
|
1128
4152
|
this.body.add(this.scroll);
|
|
1129
4153
|
this.content = new NativeBlockList(this.context, this.scroll);
|
|
1130
|
-
this.
|
|
4154
|
+
this.instrumentDivider = new InstrumentDivider(renderer2, {
|
|
4155
|
+
resize: (column) => this.resizeInstruments(column),
|
|
4156
|
+
toggle: () => this.toggleDetails(),
|
|
4157
|
+
start: () => {
|
|
4158
|
+
this.sidebarDragWidth = this.sidebarWidth ?? Math.floor(this.renderer.terminalWidth * 0.38);
|
|
4159
|
+
},
|
|
4160
|
+
finish: () => {
|
|
4161
|
+
this.sidebarDragWidth = void 0;
|
|
4162
|
+
}
|
|
4163
|
+
});
|
|
4164
|
+
this.body.add(this.instrumentDivider);
|
|
4165
|
+
this.root.onMouseDrag = (event) => this.instrumentDivider.handleDrag(event);
|
|
4166
|
+
this.root.onMouseDragEnd = (event) => this.instrumentDivider.handleRelease(event);
|
|
4167
|
+
this.root.onMouseUp = (event) => this.instrumentDivider.handleRelease(event);
|
|
4168
|
+
this.instruments = new InstrumentRail(renderer2, { colors, onInspectAgent: (id) => this.emitAction("view", id), onAction: (command2) => this.emitAction(command2) });
|
|
4169
|
+
this.body.add(this.instruments);
|
|
4170
|
+
this.embeddedPane = new BoxRenderable5(renderer2, {
|
|
1131
4171
|
id: "omnius-editor-pane",
|
|
1132
4172
|
width: "50%",
|
|
1133
4173
|
flexShrink: 0,
|
|
1134
4174
|
flexDirection: "column",
|
|
1135
4175
|
border: true,
|
|
1136
|
-
borderStyle: "
|
|
4176
|
+
borderStyle: "single",
|
|
1137
4177
|
borderColor: colors.border,
|
|
1138
4178
|
visible: false
|
|
1139
4179
|
});
|
|
1140
4180
|
this.body.add(this.embeddedPane);
|
|
1141
|
-
this.liveButton = new
|
|
4181
|
+
this.liveButton = new TextRenderable4(renderer2, {
|
|
1142
4182
|
id: "omnius-live-button",
|
|
1143
4183
|
content: "↓ Live",
|
|
1144
4184
|
width: "100%",
|
|
@@ -1153,7 +4193,7 @@ var NativeFrontend = class {
|
|
|
1153
4193
|
}
|
|
1154
4194
|
},
|
|
1155
4195
|
onKeyDown: (key) => {
|
|
1156
|
-
if (key.name === "return") {
|
|
4196
|
+
if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
|
|
1157
4197
|
this.scrollToLive();
|
|
1158
4198
|
key.preventDefault();
|
|
1159
4199
|
}
|
|
@@ -1161,7 +4201,7 @@ var NativeFrontend = class {
|
|
|
1161
4201
|
});
|
|
1162
4202
|
this.liveButton.focusable = true;
|
|
1163
4203
|
this.root.add(this.liveButton);
|
|
1164
|
-
this.queue = new
|
|
4204
|
+
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
4205
|
this.root.add(this.queue);
|
|
1166
4206
|
this.suggestions = new NativeSelectRenderable(renderer2, {
|
|
1167
4207
|
id: "omnius-suggestions",
|
|
@@ -1180,20 +4220,21 @@ var NativeFrontend = class {
|
|
|
1180
4220
|
this.suggestions.onActivate = () => this.acceptSuggestion();
|
|
1181
4221
|
this.suggestions.on("itemSelected", () => this.acceptSuggestion());
|
|
1182
4222
|
this.root.add(this.suggestions);
|
|
1183
|
-
this.
|
|
4223
|
+
this.status = new BoxRenderable5(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
|
|
4224
|
+
this.root.add(this.status);
|
|
4225
|
+
this.footer = new BoxRenderable5(renderer2, {
|
|
1184
4226
|
id: "omnius-input-box",
|
|
1185
4227
|
width: "100%",
|
|
1186
4228
|
flexShrink: 0,
|
|
1187
|
-
border:
|
|
1188
|
-
borderStyle: "
|
|
4229
|
+
border: ["top"],
|
|
4230
|
+
borderStyle: "single",
|
|
1189
4231
|
borderColor: colors.border,
|
|
1190
|
-
flexDirection: "column"
|
|
1191
|
-
paddingX: 1
|
|
4232
|
+
flexDirection: "column"
|
|
1192
4233
|
});
|
|
1193
4234
|
this.root.add(this.footer);
|
|
1194
|
-
const inputRow = new
|
|
4235
|
+
const inputRow = new BoxRenderable5(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
|
|
1195
4236
|
this.footer.add(inputRow);
|
|
1196
|
-
this.prompt = new
|
|
4237
|
+
this.prompt = new TextRenderable4(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
|
|
1197
4238
|
inputRow.add(this.prompt);
|
|
1198
4239
|
this.editor = new TextareaRenderable2(renderer2, {
|
|
1199
4240
|
id: "omnius-input",
|
|
@@ -1206,40 +4247,59 @@ var NativeFrontend = class {
|
|
|
1206
4247
|
backgroundColor: colors.background,
|
|
1207
4248
|
focusedTextColor: colors.foreground,
|
|
1208
4249
|
focusedBackgroundColor: colors.background,
|
|
1209
|
-
|
|
1210
|
-
cursorColor: colors.accent,
|
|
4250
|
+
placeholderColor: colors.foreground,
|
|
1211
4251
|
cursorStyle: { style: "block", blinking: true },
|
|
1212
4252
|
keyBindings: [{ name: "return", action: "submit" }, { name: "return", shift: true, action: "newline" }],
|
|
1213
4253
|
onContentChange: () => this.editorChanged(),
|
|
1214
4254
|
onCursorChange: () => this.editorChanged(),
|
|
1215
4255
|
onSubmit: () => this.submit()
|
|
1216
4256
|
});
|
|
4257
|
+
this.vimMode = new TextRenderable4(renderer2, {
|
|
4258
|
+
id: "omnius-vim-mode",
|
|
4259
|
+
content: "INSERT",
|
|
4260
|
+
height: 1,
|
|
4261
|
+
maxWidth: 12,
|
|
4262
|
+
flexShrink: 0,
|
|
4263
|
+
fg: colors.foreground,
|
|
4264
|
+
attributes: TextAttributes6.DIM,
|
|
4265
|
+
wrapMode: "none",
|
|
4266
|
+
truncate: true,
|
|
4267
|
+
visible: false,
|
|
4268
|
+
selectable: false
|
|
4269
|
+
});
|
|
4270
|
+
inputRow.add(this.vimMode);
|
|
4271
|
+
this.vimComposer = new VimComposer(this.editor, () => {
|
|
4272
|
+
this.vimMode.visible = this.vimComposer.enabled;
|
|
4273
|
+
this.vimMode.content = this.vimComposer.label;
|
|
4274
|
+
this.editorChanged();
|
|
4275
|
+
this.renderer.requestRender();
|
|
4276
|
+
});
|
|
1217
4277
|
inputRow.add(this.editor);
|
|
1218
|
-
this.enhance = new
|
|
4278
|
+
this.enhance = new BoxRenderable5(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
|
|
1219
4279
|
inputRow.add(this.enhance);
|
|
1220
|
-
this.activity = new
|
|
1221
|
-
this.
|
|
1222
|
-
this.media = new
|
|
1223
|
-
this.mediaText = new
|
|
4280
|
+
this.activity = new TextRenderable4(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
|
|
4281
|
+
this.status.add(this.activity);
|
|
4282
|
+
this.media = new BoxRenderable5(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
|
|
4283
|
+
this.mediaText = new TextRenderable4(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
|
|
1224
4284
|
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);
|
|
4285
|
+
this.micTrack = new TextRenderable4(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
|
|
1228
4286
|
this.media.add(this.micTrack);
|
|
1229
|
-
this.
|
|
1230
|
-
this.metricsRow = new
|
|
1231
|
-
this.
|
|
4287
|
+
this.status.add(this.media);
|
|
4288
|
+
this.metricsRow = new BoxRenderable5(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
|
|
4289
|
+
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 });
|
|
4290
|
+
this.metricsRow.add(this.keyHints);
|
|
4291
|
+
this.metricsStartActions = new BoxRenderable5(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
1232
4292
|
this.metricsRow.add(this.metricsStartActions);
|
|
1233
|
-
this.metrics = new
|
|
4293
|
+
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
4294
|
this.metricsRow.add(this.metrics);
|
|
1235
|
-
this.metricsEndActions = new
|
|
4295
|
+
this.metricsEndActions = new BoxRenderable5(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
1236
4296
|
this.metricsRow.add(this.metricsEndActions);
|
|
1237
4297
|
this.root.add(this.metricsRow);
|
|
1238
4298
|
this.modals = new NativeModalStack(this.context, () => this.restoreFocus());
|
|
1239
4299
|
renderer2.keyInput.on("keypress", this.onKey);
|
|
1240
4300
|
renderer2.keyInput.on("paste", this.onPaste);
|
|
1241
|
-
renderer2.on(
|
|
1242
|
-
renderer2.on(
|
|
4301
|
+
renderer2.on(CliRenderEvents3.FRAME, this.onFrame);
|
|
4302
|
+
renderer2.on(CliRenderEvents3.RESIZE, this.onResize);
|
|
1243
4303
|
this.applyTheme(initialTheme);
|
|
1244
4304
|
this.editor.focus();
|
|
1245
4305
|
}
|
|
@@ -1248,6 +4308,7 @@ var NativeFrontend = class {
|
|
|
1248
4308
|
root;
|
|
1249
4309
|
scroll;
|
|
1250
4310
|
editor;
|
|
4311
|
+
vimComposer;
|
|
1251
4312
|
content;
|
|
1252
4313
|
modals;
|
|
1253
4314
|
context;
|
|
@@ -1260,22 +4321,27 @@ var NativeFrontend = class {
|
|
|
1260
4321
|
taskPane;
|
|
1261
4322
|
tasks;
|
|
1262
4323
|
body;
|
|
4324
|
+
instruments;
|
|
4325
|
+
instrumentDivider;
|
|
4326
|
+
detailButton;
|
|
1263
4327
|
embeddedPane;
|
|
1264
4328
|
liveButton;
|
|
1265
4329
|
queue;
|
|
1266
4330
|
suggestions;
|
|
1267
4331
|
footer;
|
|
1268
4332
|
prompt;
|
|
4333
|
+
vimMode;
|
|
1269
4334
|
enhance;
|
|
1270
4335
|
metrics;
|
|
4336
|
+
keyHints;
|
|
1271
4337
|
metricsRow;
|
|
1272
4338
|
metricsStartActions;
|
|
1273
4339
|
metricsEndActions;
|
|
1274
4340
|
activity;
|
|
4341
|
+
status;
|
|
1275
4342
|
media;
|
|
1276
4343
|
mediaText;
|
|
1277
4344
|
micTrack;
|
|
1278
|
-
micLevel;
|
|
1279
4345
|
clipboard;
|
|
1280
4346
|
state;
|
|
1281
4347
|
themeSignature = "";
|
|
@@ -1291,8 +4357,13 @@ var NativeFrontend = class {
|
|
|
1291
4357
|
history = [];
|
|
1292
4358
|
historyIndex = -1;
|
|
1293
4359
|
savedInput = "";
|
|
1294
|
-
|
|
1295
|
-
|
|
4360
|
+
disposeSelectionContrast;
|
|
4361
|
+
actionSignatures = /* @__PURE__ */ new WeakMap();
|
|
4362
|
+
detailsOpen;
|
|
4363
|
+
narrowDetailsOpen = false;
|
|
4364
|
+
sidebarWidth;
|
|
4365
|
+
sidebarDragWidth;
|
|
4366
|
+
headerPanelCollapsed = false;
|
|
1296
4367
|
inputEventPending = false;
|
|
1297
4368
|
viewportSignature = "";
|
|
1298
4369
|
newEntries = 0;
|
|
@@ -1319,20 +4390,21 @@ var NativeFrontend = class {
|
|
|
1319
4390
|
if (headerSignature !== this.headerSignature) {
|
|
1320
4391
|
this.headerSignature = headerSignature;
|
|
1321
4392
|
this.identity.content = styledText([
|
|
1322
|
-
{ text: state.header.
|
|
4393
|
+
{ text: `${plainText(state.header.panel) ? this.headerPanelCollapsed ? "▸" : "▾" : "·"} ${state.header.title}` },
|
|
1323
4394
|
...state.header.detail ? [{ text: " " }, ...textSpans(state.header.detail)] : []
|
|
1324
4395
|
]);
|
|
1325
4396
|
this.headerPanel.content = styledText(state.header.panel);
|
|
1326
|
-
this.headerPanel.visible = plainText(state.header.panel).length > 0;
|
|
4397
|
+
this.headerPanel.visible = plainText(state.header.panel).length > 0 && !this.headerPanelCollapsed;
|
|
4398
|
+
this.identity.focusable = plainText(state.header.panel).length > 0;
|
|
1327
4399
|
this.replaceActions(this.headerActions, state.header.actions, "header");
|
|
1328
4400
|
this.headerActions.visible = state.header.actions.length > 0;
|
|
1329
4401
|
}
|
|
1330
|
-
this.routeWarning.content = state.routeWarning
|
|
4402
|
+
this.routeWarning.content = state.routeWarning ? `! ${state.routeWarning}` : "";
|
|
1331
4403
|
this.routeWarning.visible = Boolean(state.routeWarning);
|
|
1332
4404
|
const tabsSignature = JSON.stringify(state.tabs);
|
|
1333
4405
|
if (tabsSignature !== this.tabsSignature) {
|
|
1334
4406
|
this.tabsSignature = tabsSignature;
|
|
1335
|
-
this.tabs.setOptions(state.tabs.map((tab) => ({ name: plainText(tab.label), description: "", value: tab.id })));
|
|
4407
|
+
this.tabs.setOptions(state.tabs.map((tab) => ({ name: fitInstrumentText(plainText(tab.label), this.tabs.getTabWidth() - 2), description: "", value: tab.id })));
|
|
1336
4408
|
}
|
|
1337
4409
|
this.tabs.visible = state.tabs.length > 1;
|
|
1338
4410
|
const activeTab = state.tabs.findIndex((tab) => tab.id === state.activeViewId);
|
|
@@ -1364,7 +4436,9 @@ var NativeFrontend = class {
|
|
|
1364
4436
|
this.editor.focusable = state.input.enabled && !this.paused;
|
|
1365
4437
|
if (state.input.enabled && !this.paused && !this.modals.active && (inputWasEnabled === false || !this.renderer.currentFocusedRenderable)) this.editor.focus();
|
|
1366
4438
|
this.metrics.content = styledText(state.metrics);
|
|
1367
|
-
this.
|
|
4439
|
+
this.metrics.visible = state.metricsVisible;
|
|
4440
|
+
this.instruments.update(state);
|
|
4441
|
+
this.layoutInstruments();
|
|
1368
4442
|
const metricsActions = JSON.stringify(state.metricsActions ?? []);
|
|
1369
4443
|
if (metricsActions !== this.metricsActionsSignature) {
|
|
1370
4444
|
this.metricsActionsSignature = metricsActions;
|
|
@@ -1374,7 +4448,6 @@ var NativeFrontend = class {
|
|
|
1374
4448
|
this.renderEnhance();
|
|
1375
4449
|
this.renderActivity();
|
|
1376
4450
|
this.renderMedia();
|
|
1377
|
-
this.updateAnimation();
|
|
1378
4451
|
} finally {
|
|
1379
4452
|
this.changing = false;
|
|
1380
4453
|
}
|
|
@@ -1382,43 +4455,46 @@ var NativeFrontend = class {
|
|
|
1382
4455
|
this.renderer.requestRender();
|
|
1383
4456
|
}
|
|
1384
4457
|
replaceActions(parent, actions, prefix) {
|
|
4458
|
+
const signature = JSON.stringify(actions);
|
|
4459
|
+
if (this.actionSignatures.get(parent) === signature) return;
|
|
4460
|
+
this.actionSignatures.set(parent, signature);
|
|
1385
4461
|
for (const child of [...parent.getChildren()]) child.destroy();
|
|
1386
4462
|
for (const action of actions) {
|
|
1387
4463
|
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, {
|
|
4464
|
+
const attributes = action.disabled ? TextAttributes6.DIM : action.active ? TextAttributes6.BOLD | TextAttributes6.UNDERLINE : 0;
|
|
4465
|
+
const button = new TextRenderable4(this.renderer, {
|
|
1394
4466
|
id: `${prefix}:${action.id}`,
|
|
1395
|
-
content: styledText(
|
|
4467
|
+
content: styledText(action.label),
|
|
1396
4468
|
flexShrink: 0,
|
|
1397
4469
|
wrapMode: "none",
|
|
1398
4470
|
selectable: false,
|
|
1399
|
-
fg:
|
|
1400
|
-
bg:
|
|
1401
|
-
attributes
|
|
4471
|
+
fg: colors.foreground,
|
|
4472
|
+
bg: colors.background,
|
|
4473
|
+
attributes,
|
|
1402
4474
|
onMouseDown: (event) => {
|
|
1403
4475
|
if (event.button !== 0 || action.disabled) return;
|
|
1404
|
-
this.
|
|
4476
|
+
this.emitAction(action.command ?? action.id, action.id);
|
|
1405
4477
|
event.stopPropagation();
|
|
1406
4478
|
},
|
|
1407
4479
|
onKeyDown: (key) => {
|
|
1408
|
-
if (key.name !== "return" || action.disabled) return;
|
|
1409
|
-
this.
|
|
4480
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
4481
|
+
this.emitAction(action.command ?? action.id, action.id);
|
|
1410
4482
|
key.preventDefault();
|
|
1411
4483
|
}
|
|
1412
4484
|
});
|
|
1413
4485
|
button.focusable = !action.disabled;
|
|
1414
4486
|
if (!action.disabled) {
|
|
4487
|
+
button.on("focused", () => {
|
|
4488
|
+
button.attributes = attributes | TextAttributes6.INVERSE;
|
|
4489
|
+
});
|
|
4490
|
+
button.on("blurred", () => {
|
|
4491
|
+
button.attributes = attributes;
|
|
4492
|
+
});
|
|
1415
4493
|
button.onMouseOver = () => {
|
|
1416
|
-
button.
|
|
1417
|
-
button.fg = colors.foreground;
|
|
4494
|
+
button.attributes = attributes | TextAttributes6.UNDERLINE | (button.focused ? TextAttributes6.INVERSE : 0);
|
|
1418
4495
|
};
|
|
1419
4496
|
button.onMouseOut = () => {
|
|
1420
|
-
button.
|
|
1421
|
-
button.fg = foreground;
|
|
4497
|
+
button.attributes = attributes | (button.focused ? TextAttributes6.INVERSE : 0);
|
|
1422
4498
|
};
|
|
1423
4499
|
}
|
|
1424
4500
|
parent.add(button);
|
|
@@ -1426,23 +4502,26 @@ var NativeFrontend = class {
|
|
|
1426
4502
|
}
|
|
1427
4503
|
renderEnhance() {
|
|
1428
4504
|
const state = this.state?.enhance;
|
|
1429
|
-
this.enhance.visible = Boolean(state);
|
|
4505
|
+
this.enhance.visible = Boolean(state && (state.state !== "idle" || this.editor.plainText.trim()));
|
|
1430
4506
|
if (!state) return;
|
|
1431
|
-
const actions = state.state === "confirm" ? [{ id: "enhance-accept", label: "✓" }, { id: "enhance-reject", label: "×" }] : [{ id: "enhance", label: state.state === "busy" ?
|
|
4507
|
+
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
4508
|
this.replaceActions(this.enhance, actions, "enhance");
|
|
1433
4509
|
}
|
|
1434
4510
|
renderActivity() {
|
|
1435
4511
|
const activity = this.state?.activity;
|
|
1436
4512
|
const stage = activity?.stage === "idle" ? void 0 : activity?.stage;
|
|
1437
|
-
|
|
1438
|
-
this.activity.
|
|
4513
|
+
const reading = activity?.label || stage || this.state?.instruments?.runtimeStatus || "";
|
|
4514
|
+
this.activity.visible = Boolean(activity?.processing || reading);
|
|
4515
|
+
this.activity.content = `${activity?.processing ? "RUN · " : ""}${reading}`;
|
|
4516
|
+
this.status.visible = this.activity.visible || this.media.visible;
|
|
1439
4517
|
}
|
|
1440
4518
|
renderMedia() {
|
|
1441
4519
|
const media = this.state?.media;
|
|
1442
4520
|
this.media.visible = Boolean(media && (media.audio || media.video || media.voiceEnabled || media.recording || media.countdown > 0 || media.label));
|
|
4521
|
+
this.status.visible = this.activity.visible || this.media.visible;
|
|
1443
4522
|
if (!media) return;
|
|
1444
4523
|
this.mediaText.content = [
|
|
1445
|
-
media.recording ?
|
|
4524
|
+
media.recording ? "● REC" : void 0,
|
|
1446
4525
|
media.speech ? "speech" : void 0,
|
|
1447
4526
|
media.audio ? "audio" : void 0,
|
|
1448
4527
|
media.video ? "video" : void 0,
|
|
@@ -1453,23 +4532,8 @@ var NativeFrontend = class {
|
|
|
1453
4532
|
].filter(Boolean).join(" · ");
|
|
1454
4533
|
this.mediaText.fg = media.recording ? this.context.colors.warning : this.context.colors.accent;
|
|
1455
4534
|
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
|
-
}
|
|
4535
|
+
const level = Math.round(Math.max(0, Math.min(1, ((media.levelDb ?? -60) + 60) / 60)) * 8);
|
|
4536
|
+
this.micTrack.content = `[${"●".repeat(level)}${"·".repeat(8 - level)}]`;
|
|
1473
4537
|
}
|
|
1474
4538
|
applyTheme(theme) {
|
|
1475
4539
|
const signature = JSON.stringify(theme);
|
|
@@ -1478,24 +4542,7 @@ var NativeFrontend = class {
|
|
|
1478
4542
|
this.headerSignature = "";
|
|
1479
4543
|
this.metricsActionsSignature = "";
|
|
1480
4544
|
const colors = this.context.colors = palette(theme);
|
|
1481
|
-
|
|
1482
|
-
default: { fg: colors.foreground },
|
|
1483
|
-
"markup.heading": { fg: colors.accent, bold: true },
|
|
1484
|
-
"markup.strong": { fg: colors.foreground, bold: true },
|
|
1485
|
-
"markup.italic": { fg: colors.foreground, italic: true },
|
|
1486
|
-
"markup.link": { fg: colors.accent, underline: true },
|
|
1487
|
-
"markup.link.url": { fg: colors.dim },
|
|
1488
|
-
"markup.raw": { fg: colors.accent },
|
|
1489
|
-
"markup.list": { fg: colors.accent },
|
|
1490
|
-
"markup.quote": { fg: colors.dim },
|
|
1491
|
-
comment: { fg: colors.dim, italic: true },
|
|
1492
|
-
keyword: { fg: colors.accent, bold: true },
|
|
1493
|
-
string: { fg: colors.success },
|
|
1494
|
-
number: { fg: colors.warning },
|
|
1495
|
-
function: { fg: colors.foreground },
|
|
1496
|
-
type: { fg: colors.accent },
|
|
1497
|
-
punctuation: { fg: colors.dim }
|
|
1498
|
-
})) this.context.syntax.registerStyle(name, style);
|
|
4545
|
+
installNativeSyntax(this.context.syntax, colors);
|
|
1499
4546
|
this.root.backgroundColor = colors.background;
|
|
1500
4547
|
this.header.backgroundColor = this.footer.backgroundColor = colors.background;
|
|
1501
4548
|
this.header.borderColor = this.footer.borderColor = this.embeddedPane.borderColor = colors.border;
|
|
@@ -1503,13 +4550,12 @@ var NativeFrontend = class {
|
|
|
1503
4550
|
this.headerPanel.fg = colors.foreground;
|
|
1504
4551
|
this.routeWarning.fg = colors.warning;
|
|
1505
4552
|
this.prompt.fg = this.activity.fg = this.liveButton.fg = colors.accent;
|
|
1506
|
-
this.
|
|
1507
|
-
this.
|
|
4553
|
+
this.vimMode.fg = colors.foreground;
|
|
4554
|
+
this.micTrack.fg = colors.foreground;
|
|
1508
4555
|
this.queue.fg = this.metrics.fg = colors.dim;
|
|
1509
4556
|
this.editor.backgroundColor = this.editor.focusedBackgroundColor = colors.background;
|
|
1510
4557
|
this.editor.textColor = this.editor.focusedTextColor = colors.foreground;
|
|
1511
|
-
this.editor.
|
|
1512
|
-
this.editor.selectionBg = colors.selection;
|
|
4558
|
+
this.editor.editorView.setSelectionColors(void 0, void 0);
|
|
1513
4559
|
this.tabs.backgroundColor = this.tabs.focusedBackgroundColor = colors.background;
|
|
1514
4560
|
this.tabs.textColor = colors.dim;
|
|
1515
4561
|
this.tabs.selectedTextColor = colors.foreground;
|
|
@@ -1518,10 +4564,87 @@ var NativeFrontend = class {
|
|
|
1518
4564
|
this.suggestions.textColor = colors.dim;
|
|
1519
4565
|
this.suggestions.selectedTextColor = colors.foreground;
|
|
1520
4566
|
this.suggestions.selectedBackgroundColor = colors.selection;
|
|
4567
|
+
styleNativeScrollbars(this.headerActions, colors);
|
|
4568
|
+
styleNativeScrollbars(this.scroll, colors);
|
|
4569
|
+
this.instruments.applyTheme(colors);
|
|
4570
|
+
this.instrumentDivider.fg = colors.foreground;
|
|
4571
|
+
this.instrumentDivider.bg = colors.background;
|
|
1521
4572
|
this.content.applyTheme();
|
|
1522
4573
|
this.tasks.applyTheme();
|
|
1523
4574
|
this.modals?.applyTheme();
|
|
1524
4575
|
}
|
|
4576
|
+
toggleHeaderPanel() {
|
|
4577
|
+
if (!this.state || !plainText(this.state.header.panel)) return;
|
|
4578
|
+
this.headerPanelCollapsed = !this.headerPanelCollapsed;
|
|
4579
|
+
this.headerSignature = "";
|
|
4580
|
+
this.update(this.state);
|
|
4581
|
+
}
|
|
4582
|
+
toggleDetails() {
|
|
4583
|
+
if (this.renderer.terminalWidth >= 110) this.detailsOpen = !(this.detailsOpen ?? true);
|
|
4584
|
+
else this.narrowDetailsOpen = !this.narrowDetailsOpen;
|
|
4585
|
+
this.layoutInstruments();
|
|
4586
|
+
this.renderer.requestRender();
|
|
4587
|
+
}
|
|
4588
|
+
resizeInstruments(column) {
|
|
4589
|
+
if (this.renderer.terminalWidth < 110 || this.embeddedPane.visible) return;
|
|
4590
|
+
const width = this.renderer.terminalWidth;
|
|
4591
|
+
const requested = Math.max(0, this.body.x + width - Math.round(column) - 1);
|
|
4592
|
+
if (requested <= 4) {
|
|
4593
|
+
this.detailsOpen = false;
|
|
4594
|
+
if (this.sidebarDragWidth !== void 0) this.sidebarWidth = this.sidebarDragWidth;
|
|
4595
|
+
} else {
|
|
4596
|
+
this.detailsOpen = true;
|
|
4597
|
+
this.sidebarWidth = Math.max(24, Math.min(width - 41, requested));
|
|
4598
|
+
}
|
|
4599
|
+
this.layoutInstruments();
|
|
4600
|
+
this.renderer.requestRender();
|
|
4601
|
+
}
|
|
4602
|
+
layoutInstruments() {
|
|
4603
|
+
const width = this.renderer.terminalWidth;
|
|
4604
|
+
const focusedBeforeLayout = this.renderer.currentFocusedRenderable;
|
|
4605
|
+
const wide = width >= 110;
|
|
4606
|
+
const shown = !this.embeddedPane.visible && (wide ? this.detailsOpen ?? true : this.narrowDetailsOpen);
|
|
4607
|
+
const horizontal = wide || this.embeddedPane.visible && width >= 80;
|
|
4608
|
+
this.body.flexDirection = horizontal ? "row" : "column";
|
|
4609
|
+
this.scroll.height = horizontal ? "100%" : "auto";
|
|
4610
|
+
this.scroll.flexBasis = horizontal ? "auto" : 0;
|
|
4611
|
+
this.instruments.visible = shown;
|
|
4612
|
+
this.instrumentDivider.visible = wide && !this.embeddedPane.visible;
|
|
4613
|
+
this.instrumentDivider.setCollapsed(!shown);
|
|
4614
|
+
const railWidth = Math.max(24, Math.min(width - 41, this.sidebarWidth ?? Math.floor(width * 0.38)));
|
|
4615
|
+
this.instruments.width = wide ? railWidth : "100%";
|
|
4616
|
+
this.scroll.width = wide && this.instrumentDivider.visible ? width - (shown ? railWidth : 0) - 1 : this.embeddedPane.visible && width >= 80 ? "50%" : "100%";
|
|
4617
|
+
const inspectorHeight = Math.max(4, Math.min(9, Math.floor(this.renderer.terminalHeight / 3)));
|
|
4618
|
+
this.instruments.height = wide ? "100%" : inspectorHeight;
|
|
4619
|
+
this.instruments.maxHeight = wide ? "100%" : inspectorHeight;
|
|
4620
|
+
this.instruments.minHeight = wide ? 1 : inspectorHeight;
|
|
4621
|
+
this.instruments.flexGrow = 0;
|
|
4622
|
+
this.instruments.flexShrink = 0;
|
|
4623
|
+
this.instruments.border = wide ? [] : ["top"];
|
|
4624
|
+
this.instruments.borderStyle = "single";
|
|
4625
|
+
this.instruments.borderColor = this.context.colors.border;
|
|
4626
|
+
this.instruments.paddingLeft = wide ? 1 : 0;
|
|
4627
|
+
this.embeddedPane.width = width >= 80 ? "50%" : "100%";
|
|
4628
|
+
this.embeddedPane.maxHeight = width >= 80 ? "100%" : "50%";
|
|
4629
|
+
this.headerActions.visible = width >= 60 && Boolean(this.state?.header.actions.length);
|
|
4630
|
+
const actions = this.state?.header.actions ?? [];
|
|
4631
|
+
const naturalWidth = actions.reduce((size, action) => size + terminalCellWidth(plainText(action.label)), 0) + Math.max(0, actions.length - 1);
|
|
4632
|
+
const detailWidth = width < 40 ? 1 : 9;
|
|
4633
|
+
const gapCount = this.headerActions.visible ? 2 : 1;
|
|
4634
|
+
const controlWidth = Math.min(naturalWidth, Math.max(1, width - 18 - detailWidth - gapCount));
|
|
4635
|
+
this.headerActions.width = controlWidth;
|
|
4636
|
+
this.identity.minWidth = Math.max(0, Math.min(18, width - detailWidth - gapCount));
|
|
4637
|
+
this.identity.width = Math.max(0, width - (this.headerActions.visible ? controlWidth : 0) - detailWidth - gapCount);
|
|
4638
|
+
this.detailButton.width = detailWidth;
|
|
4639
|
+
this.keyHints.content = width < 60 ? "F1 HELP" : "F1 HELP TAB FOCUS";
|
|
4640
|
+
this.detailButton.content = `${shown ? "-" : "+"}${detailWidth > 1 ? " DETAILS" : ""}`;
|
|
4641
|
+
for (let focused = focusedBeforeLayout; focused; focused = focused.parent) {
|
|
4642
|
+
if (!shown && focused === this.instruments || !this.instrumentDivider.visible && focused === this.instrumentDivider || !this.headerActions.visible && focused === this.headerActions) {
|
|
4643
|
+
this.restoreFocus();
|
|
4644
|
+
break;
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
1525
4648
|
get offset() {
|
|
1526
4649
|
return Math.max(0, this.scroll.scrollHeight - this.scroll.viewport.height - this.scroll.scrollTop);
|
|
1527
4650
|
}
|
|
@@ -1559,6 +4682,7 @@ var NativeFrontend = class {
|
|
|
1559
4682
|
}
|
|
1560
4683
|
};
|
|
1561
4684
|
onResize = () => {
|
|
4685
|
+
this.layoutInstruments();
|
|
1562
4686
|
this.emit({ type: "resize", columns: this.renderer.terminalWidth, rows: this.renderer.terminalHeight });
|
|
1563
4687
|
};
|
|
1564
4688
|
editorChanged() {
|
|
@@ -1578,12 +4702,16 @@ var NativeFrontend = class {
|
|
|
1578
4702
|
if (text === this.lastInput && cursor === this.lastCursor) return;
|
|
1579
4703
|
this.lastInput = text;
|
|
1580
4704
|
this.lastCursor = cursor;
|
|
4705
|
+
this.renderEnhance();
|
|
1581
4706
|
this.emit({ type: "input", text, cursor });
|
|
1582
4707
|
}
|
|
1583
4708
|
submit() {
|
|
1584
4709
|
if (this.paused || this.state?.input.enabled === false) return;
|
|
1585
|
-
if (this.suggestions.visible && this.editor.plainText.startsWith("/") && this.acceptSuggestion()) return;
|
|
1586
4710
|
const text = this.editor.plainText;
|
|
4711
|
+
const command2 = /^\/([^\s]+)/.exec(text.trimStart())?.[1];
|
|
4712
|
+
const suggestion = String(this.suggestions.getSelectedOption()?.value ?? "");
|
|
4713
|
+
if (this.suggestions.visible && command2 && !findCommand(command2) && suggestion.startsWith(text.trimEnd()) && this.acceptSuggestion()) return;
|
|
4714
|
+
this.suggestions.visible = false;
|
|
1587
4715
|
if (text.trim() && this.history[0] !== text) {
|
|
1588
4716
|
this.history.unshift(text);
|
|
1589
4717
|
this.history.length = Math.min(this.history.length, 1e3);
|
|
@@ -1608,7 +4736,7 @@ var NativeFrontend = class {
|
|
|
1608
4736
|
const text = String(selected.value);
|
|
1609
4737
|
this.setInput(text + (text.endsWith(" ") ? "" : " "));
|
|
1610
4738
|
this.suggestions.visible = false;
|
|
1611
|
-
this.
|
|
4739
|
+
this.emitAction("completion", text);
|
|
1612
4740
|
return true;
|
|
1613
4741
|
}
|
|
1614
4742
|
setInput(text, cursor = text.length) {
|
|
@@ -1623,6 +4751,10 @@ var NativeFrontend = class {
|
|
|
1623
4751
|
key.preventDefault();
|
|
1624
4752
|
key.stopPropagation();
|
|
1625
4753
|
}
|
|
4754
|
+
emitAction(action, id) {
|
|
4755
|
+
this.flushLatestInput();
|
|
4756
|
+
this.emit({ type: "action", action, ...id === void 0 ? {} : { id } });
|
|
4757
|
+
}
|
|
1626
4758
|
onKey = (key) => {
|
|
1627
4759
|
if (this.modals.active) {
|
|
1628
4760
|
this.modals.active.handleKey(key);
|
|
@@ -1634,10 +4766,22 @@ var NativeFrontend = class {
|
|
|
1634
4766
|
return;
|
|
1635
4767
|
}
|
|
1636
4768
|
if (this.renderer.currentFocusedRenderable instanceof EmbeddedTerminalRenderable2) return;
|
|
4769
|
+
if (key.name === "f1") {
|
|
4770
|
+
this.emitAction("/help");
|
|
4771
|
+
key.preventDefault();
|
|
4772
|
+
key.stopPropagation();
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
if (key.ctrl && key.shift && key.name.toLowerCase() === "i") {
|
|
4776
|
+
this.toggleDetails();
|
|
4777
|
+
key.preventDefault();
|
|
4778
|
+
key.stopPropagation();
|
|
4779
|
+
return;
|
|
4780
|
+
}
|
|
1637
4781
|
const headerButtons = (this.state?.header.actions ?? []).filter((action) => !action.disabled).map((action) => this.getRenderable(`header:${action.id}`)).filter((button) => Boolean(button));
|
|
1638
4782
|
const headerIndex = headerButtons.indexOf(this.renderer.currentFocusedRenderable);
|
|
1639
4783
|
if (headerIndex >= 0) {
|
|
1640
|
-
const direction = key.name === "left"
|
|
4784
|
+
const direction = key.name === "left" ? -1 : key.name === "right" ? 1 : 0;
|
|
1641
4785
|
if (direction || key.name === "home" || key.name === "end") {
|
|
1642
4786
|
const index = key.name === "home" ? 0 : key.name === "end" ? headerButtons.length - 1 : (headerIndex + direction + headerButtons.length) % headerButtons.length;
|
|
1643
4787
|
const button = headerButtons[index];
|
|
@@ -1688,19 +4832,30 @@ var NativeFrontend = class {
|
|
|
1688
4832
|
this.shortcut("ctrl-u", key);
|
|
1689
4833
|
return;
|
|
1690
4834
|
}
|
|
4835
|
+
if (this.renderer.currentFocusedRenderable === this.editor && !this.paused && this.state?.input.enabled !== false && this.vimComposer.handle(key)) return;
|
|
1691
4836
|
if (key.name === "escape") {
|
|
4837
|
+
if (this.renderer.currentFocusedRenderable !== this.editor) {
|
|
4838
|
+
this.restoreFocus();
|
|
4839
|
+
key.preventDefault();
|
|
4840
|
+
key.stopPropagation();
|
|
4841
|
+
return;
|
|
4842
|
+
}
|
|
1692
4843
|
if (this.suggestions.visible) this.suggestions.visible = false;
|
|
1693
4844
|
this.shortcut("escape", key);
|
|
1694
4845
|
return;
|
|
1695
4846
|
}
|
|
1696
4847
|
if (key.name === "tab") {
|
|
1697
4848
|
if (key.ctrl) this.shortcut("ctrl-tab", key);
|
|
1698
|
-
else if (this.suggestions.visible) {
|
|
4849
|
+
else if (!key.shift && this.renderer.currentFocusedRenderable === this.editor && this.suggestions.visible) {
|
|
1699
4850
|
this.acceptSuggestion();
|
|
1700
4851
|
key.preventDefault();
|
|
1701
|
-
} else this.shortcut("tab", key);
|
|
4852
|
+
} else if (!key.shift && this.renderer.currentFocusedRenderable === this.editor && this.editor.plainText) this.shortcut("tab", key);
|
|
4853
|
+
else this.focusNext(key.shift ? -1 : 1);
|
|
4854
|
+
key.preventDefault();
|
|
4855
|
+
key.stopPropagation();
|
|
1702
4856
|
return;
|
|
1703
4857
|
}
|
|
4858
|
+
if (this.renderer.currentFocusedRenderable !== this.editor && this.renderer.currentFocusedRenderable !== this.scroll && !key.ctrl) return;
|
|
1704
4859
|
if (key.ctrl && (key.name === "left" || key.name === "right")) {
|
|
1705
4860
|
this.shortcut(`ctrl-${key.name}`, key);
|
|
1706
4861
|
return;
|
|
@@ -1749,6 +4904,28 @@ var NativeFrontend = class {
|
|
|
1749
4904
|
return;
|
|
1750
4905
|
}
|
|
1751
4906
|
};
|
|
4907
|
+
focusNext(direction) {
|
|
4908
|
+
const targets = [];
|
|
4909
|
+
const visit = (node) => {
|
|
4910
|
+
if (!node.visible) return;
|
|
4911
|
+
if (node.focusable && node !== this.scroll && !(node instanceof ScrollBoxRenderable4) && node.height > 0 && node.width > 0) targets.push(node);
|
|
4912
|
+
for (const child of node.getChildren()) visit(child);
|
|
4913
|
+
};
|
|
4914
|
+
visit(this.root);
|
|
4915
|
+
if (this.instruments.visible) {
|
|
4916
|
+
for (const target2 of this.instruments.focusTargets()) if (!targets.includes(target2)) targets.push(target2);
|
|
4917
|
+
}
|
|
4918
|
+
if (!targets.length) return;
|
|
4919
|
+
const current = targets.indexOf(this.renderer.currentFocusedRenderable);
|
|
4920
|
+
const target = targets[(current + direction + targets.length) % targets.length];
|
|
4921
|
+
target.focus();
|
|
4922
|
+
if (target.id.startsWith("header:")) this.headerActions.scrollChildIntoView(target.id);
|
|
4923
|
+
else if (target.id.startsWith("block-title:") || target.id.startsWith("block-action:")) {
|
|
4924
|
+
let block = target;
|
|
4925
|
+
while (block && !block.id.startsWith("block:")) block = block.parent;
|
|
4926
|
+
if (block) this.scroll.scrollChildIntoView(block.id);
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
1752
4929
|
onPaste = (event) => {
|
|
1753
4930
|
if (this.modals.active) {
|
|
1754
4931
|
this.modals.active.handlePaste(event);
|
|
@@ -1797,6 +4974,10 @@ var NativeFrontend = class {
|
|
|
1797
4974
|
case "input-action":
|
|
1798
4975
|
this.inputAction(command2.action, command2.value);
|
|
1799
4976
|
break;
|
|
4977
|
+
case "composer-vim":
|
|
4978
|
+
this.vimComposer.setEnabled(command2.enabled);
|
|
4979
|
+
this.restoreFocus();
|
|
4980
|
+
break;
|
|
1800
4981
|
case "mouse":
|
|
1801
4982
|
this.renderer.useMouse = command2.enabled;
|
|
1802
4983
|
break;
|
|
@@ -1875,6 +5056,7 @@ var NativeFrontend = class {
|
|
|
1875
5056
|
this.embeddedPane.add(terminal);
|
|
1876
5057
|
}
|
|
1877
5058
|
terminal.focus();
|
|
5059
|
+
this.layoutInstruments();
|
|
1878
5060
|
}
|
|
1879
5061
|
closeEmbedded(id) {
|
|
1880
5062
|
this.context.terminals.get(id)?.destroy();
|
|
@@ -1882,17 +5064,18 @@ var NativeFrontend = class {
|
|
|
1882
5064
|
if (this.embeddedId === id) {
|
|
1883
5065
|
this.embeddedId = void 0;
|
|
1884
5066
|
this.embeddedPane.visible = false;
|
|
5067
|
+
this.layoutInstruments();
|
|
1885
5068
|
this.restoreFocus();
|
|
1886
5069
|
}
|
|
1887
5070
|
}
|
|
1888
5071
|
async dispose() {
|
|
1889
5072
|
if (this.destroyed) return;
|
|
1890
5073
|
this.destroyed = true;
|
|
1891
|
-
|
|
5074
|
+
this.disposeSelectionContrast();
|
|
1892
5075
|
this.renderer.keyInput.off("keypress", this.onKey);
|
|
1893
5076
|
this.renderer.keyInput.off("paste", this.onPaste);
|
|
1894
|
-
this.renderer.off(
|
|
1895
|
-
this.renderer.off(
|
|
5077
|
+
this.renderer.off(CliRenderEvents3.FRAME, this.onFrame);
|
|
5078
|
+
this.renderer.off(CliRenderEvents3.RESIZE, this.onResize);
|
|
1896
5079
|
this.modals.destroy();
|
|
1897
5080
|
this.content.destroy();
|
|
1898
5081
|
this.tasks.destroy();
|
|
@@ -1902,21 +5085,86 @@ var NativeFrontend = class {
|
|
|
1902
5085
|
}
|
|
1903
5086
|
};
|
|
1904
5087
|
|
|
5088
|
+
// packages/cli/src/tui/native/components/cursor.ts
|
|
5089
|
+
import { CliRenderEvents as CliRenderEvents4 } from "@opentui/core";
|
|
5090
|
+
function installTerminalCursorDefaults(renderer2, session) {
|
|
5091
|
+
const reset = new TextEncoder().encode("\x1B]112\x07");
|
|
5092
|
+
let disposed = false;
|
|
5093
|
+
let requested = 0;
|
|
5094
|
+
let restored = 0;
|
|
5095
|
+
let pending;
|
|
5096
|
+
let observedFrame;
|
|
5097
|
+
const restore = () => {
|
|
5098
|
+
if (disposed || session.disposed) return Promise.resolve();
|
|
5099
|
+
requested++;
|
|
5100
|
+
if (pending) return pending;
|
|
5101
|
+
let resolve;
|
|
5102
|
+
let reject;
|
|
5103
|
+
const completion = new Promise((done, fail2) => {
|
|
5104
|
+
resolve = done;
|
|
5105
|
+
reject = fail2;
|
|
5106
|
+
});
|
|
5107
|
+
pending = completion;
|
|
5108
|
+
void (async () => {
|
|
5109
|
+
try {
|
|
5110
|
+
while (!disposed && !session.disposed && restored < requested) {
|
|
5111
|
+
const generation = requested;
|
|
5112
|
+
while (!session.write(reset)) await session.idle();
|
|
5113
|
+
await session.idle();
|
|
5114
|
+
restored = generation;
|
|
5115
|
+
}
|
|
5116
|
+
pending = void 0;
|
|
5117
|
+
resolve();
|
|
5118
|
+
} catch (error) {
|
|
5119
|
+
pending = void 0;
|
|
5120
|
+
reject(error);
|
|
5121
|
+
}
|
|
5122
|
+
})();
|
|
5123
|
+
return completion;
|
|
5124
|
+
};
|
|
5125
|
+
const afterFrame = () => {
|
|
5126
|
+
const current = restore();
|
|
5127
|
+
if (observedFrame === current) return;
|
|
5128
|
+
observedFrame = current;
|
|
5129
|
+
void current.then(() => {
|
|
5130
|
+
if (observedFrame === current) observedFrame = void 0;
|
|
5131
|
+
}, (error) => {
|
|
5132
|
+
if (observedFrame === current) observedFrame = void 0;
|
|
5133
|
+
if (!disposed && !renderer2.isDestroyed) renderer2.emit("error", error);
|
|
5134
|
+
});
|
|
5135
|
+
};
|
|
5136
|
+
const dispose = () => {
|
|
5137
|
+
if (disposed) return;
|
|
5138
|
+
disposed = true;
|
|
5139
|
+
renderer2.off(CliRenderEvents4.FRAME, afterFrame);
|
|
5140
|
+
renderer2.off(CliRenderEvents4.DESTROY, dispose);
|
|
5141
|
+
};
|
|
5142
|
+
renderer2.on(CliRenderEvents4.FRAME, afterFrame);
|
|
5143
|
+
renderer2.once(CliRenderEvents4.DESTROY, dispose);
|
|
5144
|
+
return { restore, dispose };
|
|
5145
|
+
}
|
|
5146
|
+
|
|
1905
5147
|
// packages/cli/src/tui/native/worker.ts
|
|
1906
5148
|
var replies = createWriteStream("", { fd: 3, autoClose: false });
|
|
1907
5149
|
var reply = (message) => replies.write(JSON.stringify(message) + "\n");
|
|
1908
5150
|
var terminalInput = new PassThrough();
|
|
1909
5151
|
var renderer;
|
|
1910
5152
|
var frontend;
|
|
5153
|
+
var cursorDefaults;
|
|
1911
5154
|
var stopping = false;
|
|
1912
5155
|
var failed = false;
|
|
1913
5156
|
async function close() {
|
|
1914
5157
|
if (stopping) return;
|
|
1915
5158
|
stopping = true;
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
5159
|
+
try {
|
|
5160
|
+
await frontend?.dispose();
|
|
5161
|
+
await cursorDefaults?.restore();
|
|
5162
|
+
} finally {
|
|
5163
|
+
cursorDefaults?.dispose();
|
|
5164
|
+
if (renderer) {
|
|
5165
|
+
renderer.destroy();
|
|
5166
|
+
await renderer.closed;
|
|
5167
|
+
}
|
|
1920
5168
|
}
|
|
1921
5169
|
}
|
|
1922
5170
|
async function fail(error) {
|
|
@@ -1936,18 +5184,22 @@ async function command(message) {
|
|
|
1936
5184
|
frontend.update(message.snapshot, message.content);
|
|
1937
5185
|
renderer.requestRender();
|
|
1938
5186
|
await renderer.idle();
|
|
1939
|
-
} else if (message.type === "suspend")
|
|
1940
|
-
|
|
5187
|
+
} else if (message.type === "suspend") {
|
|
5188
|
+
await cursorDefaults.restore();
|
|
5189
|
+
await renderer.suspend();
|
|
5190
|
+
} else if (message.type === "resume") {
|
|
1941
5191
|
await renderer.resume();
|
|
1942
5192
|
renderer.requestRender();
|
|
1943
5193
|
await renderer.idle();
|
|
5194
|
+
await cursorDefaults.restore();
|
|
1944
5195
|
} else if (message.type === "stop") await close();
|
|
1945
5196
|
reply({ type: "ack", id: message.id });
|
|
1946
5197
|
if (message.type === "stop") replies.end(() => process.exit(0));
|
|
1947
5198
|
}
|
|
1948
5199
|
try {
|
|
5200
|
+
const session = new NativeSession(process.stdout);
|
|
1949
5201
|
renderer = await createCliRenderer({
|
|
1950
|
-
nativeSession:
|
|
5202
|
+
nativeSession: session,
|
|
1951
5203
|
stdin: terminalInput,
|
|
1952
5204
|
stdout: process.stdout,
|
|
1953
5205
|
exitOnCtrlC: false,
|
|
@@ -1958,21 +5210,22 @@ try {
|
|
|
1958
5210
|
screenMode: "alternate-screen",
|
|
1959
5211
|
consoleMode: "disabled",
|
|
1960
5212
|
externalOutputMode: "passthrough",
|
|
1961
|
-
backgroundColor:
|
|
5213
|
+
backgroundColor: RGBA3.defaultBackground(),
|
|
1962
5214
|
openConsoleOnError: false,
|
|
1963
5215
|
targetFps: 60,
|
|
1964
5216
|
maxFps: 60,
|
|
1965
5217
|
clearOnShutdown: false
|
|
1966
5218
|
});
|
|
1967
|
-
renderer.on(
|
|
5219
|
+
renderer.on(CliRenderEvents5.RENDER_ERROR, (event) => {
|
|
1968
5220
|
void fail(event.error);
|
|
1969
5221
|
});
|
|
1970
|
-
renderer.on(
|
|
5222
|
+
renderer.on(CliRenderEvents5.HANDLER_ERROR, (event) => {
|
|
1971
5223
|
void fail(event.error);
|
|
1972
5224
|
});
|
|
1973
5225
|
renderer.on("error", (event) => {
|
|
1974
5226
|
void fail(event);
|
|
1975
5227
|
});
|
|
5228
|
+
cursorDefaults = installTerminalCursorDefaults(renderer, session);
|
|
1976
5229
|
frontend = new NativeFrontend(renderer, (event) => reply({ type: "event", event }));
|
|
1977
5230
|
const controls = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
1978
5231
|
let pending = Promise.resolve();
|
|
@@ -2004,6 +5257,7 @@ try {
|
|
|
2004
5257
|
});
|
|
2005
5258
|
renderer.requestRender();
|
|
2006
5259
|
await renderer.idle();
|
|
5260
|
+
await cursorDefaults.restore();
|
|
2007
5261
|
reply({ type: "ready" });
|
|
2008
5262
|
} catch (error) {
|
|
2009
5263
|
await fail(error);
|