omnius 1.0.867 → 1.0.868
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/py-embed.js +13 -0
- package/dist/index.js +2 -2
- package/dist/library.js +220 -194
- package/dist/tui/native/worker.js +857 -143
- package/dist/update-worker.js +128 -77
- package/docs/DISCOVERY.json +1069 -139
- package/docs/DISCOVERY.md +26 -7
- package/docs/design/media-unification-TODO.md +26 -6
- package/docs/design/media-unification-design.md +89 -0
- package/docs/design/update-operation-cancellation.md +40 -0
- package/docs/design/update-session-restoration-TODO.md +30 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-audio-playing.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-image-100x30.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-image-120x36.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-image-160x48.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-image-60x20.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-image-80x24.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-progress-collapsed.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-video-frame-a.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-video-frame-b.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/media-voice-autocomplete-junction.png +0 -0
- package/docs/work-orders/evidence/instrument-media-live-20261016/report.json +813 -0
- package/docs/work-orders/evidence/instrument-media-verification-20261016.json +59 -0
- package/docs/work-orders/evidence/native-interface-state-verification-20261016.json +59 -0
- package/docs/work-orders/evidence/project-media-unification-20261016.json +108 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -6,29 +6,70 @@ import { CliRenderEvents as CliRenderEvents7, NativeSession, RGBA as RGBA3, crea
|
|
|
6
6
|
|
|
7
7
|
// packages/cli/src/tui/native/components/frontend.ts
|
|
8
8
|
import {
|
|
9
|
-
BoxRenderable as
|
|
9
|
+
BoxRenderable as BoxRenderable8,
|
|
10
10
|
CliRenderEvents as CliRenderEvents5,
|
|
11
11
|
EmbeddedTerminalRenderable as EmbeddedTerminalRenderable2,
|
|
12
12
|
ScrollBoxRenderable as ScrollBoxRenderable5,
|
|
13
13
|
SyntaxStyle as SyntaxStyle2,
|
|
14
14
|
TextareaRenderable as TextareaRenderable2,
|
|
15
|
-
TextRenderable as
|
|
15
|
+
TextRenderable as TextRenderable7,
|
|
16
16
|
TextAttributes as TextAttributes8,
|
|
17
17
|
createClipboard,
|
|
18
18
|
createHostClipboard,
|
|
19
19
|
createRendererClipboardAdapter
|
|
20
20
|
} from "@opentui/core";
|
|
21
21
|
|
|
22
|
+
// packages/cli/src/tui/native/interface-state.ts
|
|
23
|
+
var record = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
24
|
+
var offset = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(1e7, Math.floor(value))) : 0;
|
|
25
|
+
function sanitizeNativeInterfaceState(value) {
|
|
26
|
+
if (!record(value) || value.version !== 1 || typeof value.activeViewId !== "string" || !record(value.input)) return void 0;
|
|
27
|
+
if (typeof value.input.text !== "string") return void 0;
|
|
28
|
+
const text = value.input.text.slice(0, 1e6), views = {};
|
|
29
|
+
if (record(value.views)) for (const [key, entry] of Object.entries(value.views).slice(0, 256)) {
|
|
30
|
+
if (key.length > 1024 || !record(entry)) continue;
|
|
31
|
+
const disclosures = {};
|
|
32
|
+
if (record(entry.disclosures)) for (const [id, collapsed] of Object.entries(entry.disclosures).slice(0, 1e4)) {
|
|
33
|
+
if (id.length <= 1024 && typeof collapsed === "boolean") Object.defineProperty(disclosures, id, { value: collapsed, enumerable: true, configurable: true, writable: true });
|
|
34
|
+
}
|
|
35
|
+
Object.defineProperty(views, key, { value: { top: offset(entry.top), live: entry.live === true, disclosures }, enumerable: true, configurable: true, writable: true });
|
|
36
|
+
}
|
|
37
|
+
const layout = record(value.layout) ? value.layout : {}, instruments = record(value.instruments) ? value.instruments : {}, tabs = record(value.tabs) ? value.tabs : {};
|
|
38
|
+
return {
|
|
39
|
+
version: 1,
|
|
40
|
+
...typeof value.sessionId === "string" ? { sessionId: value.sessionId.slice(0, 1024) } : {},
|
|
41
|
+
activeViewId: value.activeViewId.slice(0, 1024),
|
|
42
|
+
presentation: value.presentation === "tree" ? "tree" : "stack",
|
|
43
|
+
input: { text, cursor: Math.min(text.length, offset(value.input.cursor)) },
|
|
44
|
+
views,
|
|
45
|
+
layout: {
|
|
46
|
+
...typeof layout.sidebarWidth === "number" && Number.isFinite(layout.sidebarWidth) ? { sidebarWidth: Math.max(24, Math.min(1e4, Math.floor(layout.sidebarWidth))) } : {},
|
|
47
|
+
...typeof layout.detailsOpen === "boolean" ? { detailsOpen: layout.detailsOpen } : {},
|
|
48
|
+
narrowDetailsOpen: layout.narrowDetailsOpen === true,
|
|
49
|
+
headerPanelCollapsed: layout.headerPanelCollapsed === true
|
|
50
|
+
},
|
|
51
|
+
instruments: {
|
|
52
|
+
inference: instruments.inference !== false,
|
|
53
|
+
session: instruments.session === true,
|
|
54
|
+
context: instruments.context === true,
|
|
55
|
+
devices: instruments.devices === true,
|
|
56
|
+
scrollTop: offset(instruments.scrollTop),
|
|
57
|
+
deviceScrollTop: offset(instruments.deviceScrollTop)
|
|
58
|
+
},
|
|
59
|
+
tabs: { sessions: offset(tabs.sessions), agents: offset(tabs.agents) }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
22
63
|
// packages/cli/src/tui/native/components/blocks.ts
|
|
23
64
|
import {
|
|
24
|
-
BoxRenderable,
|
|
65
|
+
BoxRenderable as BoxRenderable2,
|
|
25
66
|
CodeRenderable,
|
|
26
67
|
EmbeddedTerminalRenderable,
|
|
27
|
-
ImageRenderable,
|
|
68
|
+
ImageRenderable as ImageRenderable2,
|
|
28
69
|
MarkdownRenderable,
|
|
29
70
|
RenderableEvents,
|
|
30
71
|
ScrollBoxRenderable,
|
|
31
|
-
TextRenderable
|
|
72
|
+
TextRenderable as TextRenderable2
|
|
32
73
|
} from "@opentui/core";
|
|
33
74
|
|
|
34
75
|
// packages/cli/src/tui/native/components/text.ts
|
|
@@ -170,6 +211,505 @@ function installDefaultSelectionContrast(renderer2) {
|
|
|
170
211
|
return dispose;
|
|
171
212
|
}
|
|
172
213
|
|
|
214
|
+
// packages/cli/src/tui/native/components/media.ts
|
|
215
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
216
|
+
import { BoxRenderable, ImageRenderable, NativeImagePool, TextRenderable } from "@opentui/core";
|
|
217
|
+
|
|
218
|
+
// packages/cli/src/tui/native/components/media-playback.ts
|
|
219
|
+
import { spawn, execFile } from "node:child_process";
|
|
220
|
+
import { realpath, lstat } from "node:fs/promises";
|
|
221
|
+
import { extname, relative, resolve } from "node:path";
|
|
222
|
+
import { Audio } from "@opentui/core";
|
|
223
|
+
async function validateMediaFile(path, root) {
|
|
224
|
+
const info = await lstat(path);
|
|
225
|
+
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Media must be a regular local file");
|
|
226
|
+
const canonical = await realpath(path), project = await realpath(root);
|
|
227
|
+
const local = relative(project, canonical);
|
|
228
|
+
if (local === ".." || local.startsWith("../") || resolve(project, local) !== canonical) throw new Error("Media is outside its project");
|
|
229
|
+
if (!/\.(png|jpe?g|webp|gif|mp4|webm|mov|mkv|wav|mp3|flac|ogg|m4a)$/i.test(extname(canonical))) throw new Error("Unsupported media file");
|
|
230
|
+
return canonical;
|
|
231
|
+
}
|
|
232
|
+
async function probeMedia(spec, signal) {
|
|
233
|
+
const path = await validateMediaFile(spec.path, spec.projectRoot);
|
|
234
|
+
const output = await new Promise((done, fail2) => execFile("ffprobe", ["-v", "error", "-show_entries", "stream=codec_type,width,height:format=duration", "-of", "json", path], { signal, timeout: 5e3, maxBuffer: 128e3 }, (error, stdout) => error ? fail2(error) : done(stdout)));
|
|
235
|
+
const data = JSON.parse(output), video = data.streams?.find((stream) => stream.codec_type === "video");
|
|
236
|
+
const durationSec = Number(data.format?.duration);
|
|
237
|
+
return { ...video?.width > 0 ? { width: video.width, height: video.height } : {}, ...Number.isFinite(durationSec) && durationSec > 0 ? { durationSec } : {}, hasAudio: data.streams?.some((stream) => stream.codec_type === "audio") };
|
|
238
|
+
}
|
|
239
|
+
var NativeMediaPlayback = class {
|
|
240
|
+
constructor(changed, silent = process.env["OMNIUS_MEDIA_TEST_SILENT"] === "1") {
|
|
241
|
+
this.changed = changed;
|
|
242
|
+
this.silent = silent;
|
|
243
|
+
}
|
|
244
|
+
changed;
|
|
245
|
+
silent;
|
|
246
|
+
positionSec = 0;
|
|
247
|
+
playing = false;
|
|
248
|
+
process;
|
|
249
|
+
controller;
|
|
250
|
+
audio;
|
|
251
|
+
stream;
|
|
252
|
+
ticker;
|
|
253
|
+
epoch = 0;
|
|
254
|
+
audioOffset = 0;
|
|
255
|
+
cleanup = Promise.resolve();
|
|
256
|
+
get closed() {
|
|
257
|
+
return this.cleanup;
|
|
258
|
+
}
|
|
259
|
+
async play(spec, size, frame) {
|
|
260
|
+
this.pause();
|
|
261
|
+
const epoch = ++this.epoch;
|
|
262
|
+
const controller = this.controller = new AbortController();
|
|
263
|
+
try {
|
|
264
|
+
await this.cleanup;
|
|
265
|
+
if (epoch !== this.epoch) return;
|
|
266
|
+
const path = await validateMediaFile(spec.path, spec.projectRoot);
|
|
267
|
+
if (epoch !== this.epoch) return;
|
|
268
|
+
const width = Math.max(2, Math.min(640, Math.floor(size.width / 2) * 2)), height = Math.max(2, Math.min(360, Math.floor(size.height / 2) * 2));
|
|
269
|
+
const info = spec.kind === "video" ? await probeMedia(spec, controller.signal) : {};
|
|
270
|
+
if (epoch !== this.epoch) return;
|
|
271
|
+
const input = ["-nostdin", "-loglevel", "error", "-hwaccel", "none", "-ss", String(this.positionSec), ...spec.kind === "video" ? ["-re"] : [], "-i", path];
|
|
272
|
+
const output = spec.kind === "video" ? ["-an", "-vf", `scale=${width}:${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:(ow-iw)/2:(oh-ih)/2,fps=8`, "-pix_fmt", "rgba", "-f", "rawvideo", "pipe:1"] : ["-vn", "-f", "f32le", "-ac", "2", "-ar", "48000", "pipe:1"];
|
|
273
|
+
const withAudio = spec.kind === "video" && info.hasAudio;
|
|
274
|
+
if (withAudio) output.push("-map", "0:a:0", "-vn", "-f", "f32le", "-ac", "2", "-ar", "48000", "pipe:3");
|
|
275
|
+
const child = this.process = spawn("ffmpeg", [...input, ...output], { stdio: withAudio ? ["pipe", "pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
276
|
+
child.stdin.end();
|
|
277
|
+
let diagnostic = "";
|
|
278
|
+
child.stderr.on("data", (chunk) => {
|
|
279
|
+
diagnostic = (diagnostic + chunk.toString()).slice(-2e3);
|
|
280
|
+
});
|
|
281
|
+
const closed = new Promise((done, fail2) => {
|
|
282
|
+
child.once("error", fail2);
|
|
283
|
+
child.once("close", done);
|
|
284
|
+
});
|
|
285
|
+
this.cleanup = closed.catch(() => {
|
|
286
|
+
});
|
|
287
|
+
this.playing = true;
|
|
288
|
+
this.changed(spec.kind === "video" && !withAudio ? "Playing · no audio track" : "Playing");
|
|
289
|
+
const audioWork = withAudio ? this.consumeAudio(child.stdio[3], controller, epoch) : void 0;
|
|
290
|
+
if (audioWork) void audioWork.catch((error) => {
|
|
291
|
+
if (epoch === this.epoch) {
|
|
292
|
+
this.pause();
|
|
293
|
+
this.changed(error.message);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
if (spec.kind === "video") {
|
|
297
|
+
const bytes = width * height * 4;
|
|
298
|
+
let pending = Buffer.alloc(0), frames = 0;
|
|
299
|
+
const offset2 = this.positionSec;
|
|
300
|
+
for await (const chunk of child.stdout) {
|
|
301
|
+
if (epoch !== this.epoch) break;
|
|
302
|
+
pending = Buffer.concat([pending, chunk]);
|
|
303
|
+
while (pending.length >= bytes && epoch === this.epoch) {
|
|
304
|
+
frame(pending.subarray(0, bytes), width, height);
|
|
305
|
+
pending = pending.subarray(bytes);
|
|
306
|
+
frames++;
|
|
307
|
+
if (!withAudio) this.positionSec = offset2 + frames / 8;
|
|
308
|
+
this.changed();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} else await this.consumeAudio(child.stdout, controller, epoch);
|
|
312
|
+
if (audioWork) await audioWork;
|
|
313
|
+
const code = await closed;
|
|
314
|
+
if (epoch !== this.epoch) return;
|
|
315
|
+
if (code !== 0) throw new Error(diagnostic.trim() || "Media decoder failed");
|
|
316
|
+
this.pause();
|
|
317
|
+
this.positionSec = 0;
|
|
318
|
+
this.changed("Playback complete");
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (epoch !== this.epoch) return;
|
|
321
|
+
this.pause();
|
|
322
|
+
this.changed(error instanceof Error ? error.message : String(error));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async consumeAudio(source, controller, epoch) {
|
|
326
|
+
const audio = this.audio = Audio.create({ autoStart: false, sampleRate: 48e3 });
|
|
327
|
+
audio.on("error", (error) => {
|
|
328
|
+
if (epoch === this.epoch) {
|
|
329
|
+
this.pause();
|
|
330
|
+
this.changed(error.message);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
if (!(this.silent ? audio.startMixer() : audio.start())) throw new Error("Audio output is unavailable");
|
|
334
|
+
this.audioOffset = this.positionSec;
|
|
335
|
+
const stream = this.stream = await audio.playStream(source, { format: "pcm", sampleFormat: "f32le", channels: 2, sampleRate: 48e3, buffer: { capacityMs: 500, startupMs: 50, resumeMs: 50 }, signal: controller.signal });
|
|
336
|
+
if (epoch !== this.epoch) {
|
|
337
|
+
stream.dispose();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
stream.on("error", (error) => {
|
|
341
|
+
if (epoch === this.epoch) {
|
|
342
|
+
this.pause();
|
|
343
|
+
this.changed(error.message);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
this.ticker = setInterval(() => {
|
|
347
|
+
if (this.silent) audio.mixFrames(480, 2);
|
|
348
|
+
const stats = stream.getStats();
|
|
349
|
+
this.positionSec = this.audioOffset + Number(stats.framesPlayed) / stats.sampleRate;
|
|
350
|
+
this.changed();
|
|
351
|
+
}, this.silent ? 10 : 100);
|
|
352
|
+
await stream.closed;
|
|
353
|
+
}
|
|
354
|
+
pause() {
|
|
355
|
+
++this.epoch;
|
|
356
|
+
if (this.stream) {
|
|
357
|
+
try {
|
|
358
|
+
const stats = this.stream.getStats();
|
|
359
|
+
this.positionSec = this.audioOffset + Number(stats.framesPlayed) / stats.sampleRate;
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
this.controller?.abort();
|
|
364
|
+
this.controller = void 0;
|
|
365
|
+
this.stream?.dispose();
|
|
366
|
+
this.stream = void 0;
|
|
367
|
+
this.audio?.dispose();
|
|
368
|
+
this.audio = void 0;
|
|
369
|
+
if (this.ticker) clearInterval(this.ticker);
|
|
370
|
+
this.ticker = void 0;
|
|
371
|
+
const child = this.process;
|
|
372
|
+
this.process = void 0;
|
|
373
|
+
if (child && child.exitCode === null) {
|
|
374
|
+
child.kill("SIGTERM");
|
|
375
|
+
const timer = setTimeout(() => {
|
|
376
|
+
if (child.exitCode === null) child.kill("SIGKILL");
|
|
377
|
+
}, 250);
|
|
378
|
+
timer.unref?.();
|
|
379
|
+
child.once("close", () => clearTimeout(timer));
|
|
380
|
+
}
|
|
381
|
+
const wasPlaying = this.playing;
|
|
382
|
+
this.playing = false;
|
|
383
|
+
if (wasPlaying) this.changed("Paused");
|
|
384
|
+
}
|
|
385
|
+
dispose() {
|
|
386
|
+
this.pause();
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// packages/cli/src/tui/native/components/media.ts
|
|
391
|
+
function mediaPreviewHeight(width, imageWidth, imageHeight, cellAspectRatio = 2) {
|
|
392
|
+
if (![width, imageWidth, imageHeight, cellAspectRatio].every((value) => Number.isFinite(value) && value > 0)) return 8;
|
|
393
|
+
return Math.max(1, Math.min(80, Math.round(width * imageHeight / imageWidth / cellAspectRatio)));
|
|
394
|
+
}
|
|
395
|
+
function audioWaveform(samples, width) {
|
|
396
|
+
const cells = Math.max(1, Math.min(240, Math.floor(width)));
|
|
397
|
+
const glyphs = "▁▂▃▄▅▆▇█";
|
|
398
|
+
return Array.from({ length: cells }, (_, column) => {
|
|
399
|
+
const begin = Math.floor(column * samples.length / cells), end = Math.max(begin + 1, Math.floor((column + 1) * samples.length / cells));
|
|
400
|
+
let peak = 0;
|
|
401
|
+
for (let index = begin; index < end && index < samples.length; index++) if (Number.isFinite(samples[index])) peak = Math.max(peak, Math.abs(samples[index]));
|
|
402
|
+
return glyphs[Math.min(7, Math.floor(Math.min(1, peak) * 7))];
|
|
403
|
+
}).join("");
|
|
404
|
+
}
|
|
405
|
+
var time = (seconds) => `${Math.floor(Math.max(0, seconds) / 60)}:${String(Math.floor(Math.max(0, seconds) % 60)).padStart(2, "0")}`;
|
|
406
|
+
var NativeMediaView = class extends BoxRenderable {
|
|
407
|
+
constructor(renderer2, mediaId, spec, colors, sendEvent) {
|
|
408
|
+
super(renderer2, { id: `media:${mediaId}`, width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
409
|
+
this.renderer = renderer2;
|
|
410
|
+
this.mediaId = mediaId;
|
|
411
|
+
this.colors = colors;
|
|
412
|
+
this.sendEvent = sendEvent;
|
|
413
|
+
this.spec = spec;
|
|
414
|
+
this.location = new TextRenderable(renderer2, { id: `media-location:${mediaId}`, content: spec.path, width: "100%", wrapMode: "word", selectable: true, fg: colors.dim });
|
|
415
|
+
this.status = new TextRenderable(renderer2, { id: `media-status:${mediaId}`, content: "", width: "100%", wrapMode: "word", selectable: true, fg: colors.dim });
|
|
416
|
+
this.wave = new TextRenderable(renderer2, { id: `media-waveform:${mediaId}`, content: "", width: "100%", height: 1, wrapMode: "none", fg: colors.foreground, visible: false });
|
|
417
|
+
this.playheadLabel = new TextRenderable(renderer2, { id: `media-position:${mediaId}`, content: "", width: "100%", height: 1, wrapMode: "none", fg: colors.dim, visible: false });
|
|
418
|
+
this.controls = new BoxRenderable(renderer2, { id: `media-controls:${mediaId}`, flexDirection: "row", gap: 2, flexShrink: 0 });
|
|
419
|
+
this.playControl = this.control("play", "Play", () => this.togglePlayback());
|
|
420
|
+
this.controls.add(this.playControl);
|
|
421
|
+
this.controls.add(this.control("open", "Open ↗", () => {
|
|
422
|
+
void this.open();
|
|
423
|
+
}));
|
|
424
|
+
this.playback = new NativeMediaPlayback((message) => {
|
|
425
|
+
if (this.isDestroyed) return;
|
|
426
|
+
if (message) this.currentStatus = message;
|
|
427
|
+
this.refreshStatus();
|
|
428
|
+
});
|
|
429
|
+
this.add(this.location);
|
|
430
|
+
this.add(this.wave);
|
|
431
|
+
this.add(this.playheadLabel);
|
|
432
|
+
this.add(this.status);
|
|
433
|
+
this.add(this.controls);
|
|
434
|
+
this.update(spec);
|
|
435
|
+
}
|
|
436
|
+
renderer;
|
|
437
|
+
mediaId;
|
|
438
|
+
colors;
|
|
439
|
+
sendEvent;
|
|
440
|
+
spec;
|
|
441
|
+
location;
|
|
442
|
+
status;
|
|
443
|
+
wave;
|
|
444
|
+
playheadLabel;
|
|
445
|
+
controls;
|
|
446
|
+
playControl;
|
|
447
|
+
image;
|
|
448
|
+
pool;
|
|
449
|
+
playback;
|
|
450
|
+
probeController;
|
|
451
|
+
samples;
|
|
452
|
+
waveformText = "";
|
|
453
|
+
sourceWidth = 16;
|
|
454
|
+
sourceHeight = 9;
|
|
455
|
+
duration = 0;
|
|
456
|
+
hovered = false;
|
|
457
|
+
lastWidth = 0;
|
|
458
|
+
currentStatus = "";
|
|
459
|
+
sourceEpoch = 0;
|
|
460
|
+
needsProbe = true;
|
|
461
|
+
get closed() {
|
|
462
|
+
return this.playback.closed;
|
|
463
|
+
}
|
|
464
|
+
pausePlayback() {
|
|
465
|
+
this.playback.pause();
|
|
466
|
+
}
|
|
467
|
+
control(action, label, operation) {
|
|
468
|
+
const control = new TextRenderable(this.renderer, {
|
|
469
|
+
id: `media-control:${this.mediaId}:${action}`,
|
|
470
|
+
content: label,
|
|
471
|
+
fg: this.colors.foreground,
|
|
472
|
+
selectable: false,
|
|
473
|
+
onMouseDown: (event) => {
|
|
474
|
+
if (event.button === 0) {
|
|
475
|
+
operation();
|
|
476
|
+
event.stopPropagation();
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
onKeyDown: (event) => {
|
|
480
|
+
if (event.eventType !== "release" && (event.name === "return" || event.sequence === " ")) {
|
|
481
|
+
operation();
|
|
482
|
+
event.preventDefault();
|
|
483
|
+
event.stopPropagation();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
control.focusable = true;
|
|
488
|
+
control.on("focused", () => {
|
|
489
|
+
control.attributes = 32;
|
|
490
|
+
});
|
|
491
|
+
control.on("blurred", () => {
|
|
492
|
+
control.attributes = 0;
|
|
493
|
+
});
|
|
494
|
+
return control;
|
|
495
|
+
}
|
|
496
|
+
update(spec) {
|
|
497
|
+
const changed = spec.path !== this.spec.path || spec.kind !== this.spec.kind;
|
|
498
|
+
this.spec = spec;
|
|
499
|
+
this.location.content = spec.path;
|
|
500
|
+
this.playControl.visible = spec.kind !== "image";
|
|
501
|
+
if (changed) {
|
|
502
|
+
this.playback.pause();
|
|
503
|
+
this.playback.positionSec = 0;
|
|
504
|
+
this.probeController?.abort();
|
|
505
|
+
this.probeController = void 0;
|
|
506
|
+
this.needsProbe = true;
|
|
507
|
+
this.pool?.dispose();
|
|
508
|
+
this.pool = void 0;
|
|
509
|
+
this.samples = void 0;
|
|
510
|
+
this.sourceEpoch++;
|
|
511
|
+
}
|
|
512
|
+
if ((spec.kind === "image" || spec.kind === "video") && !this.image) {
|
|
513
|
+
this.image = new ImageRenderable(this.renderer, {
|
|
514
|
+
id: `media-preview:${this.mediaId}`,
|
|
515
|
+
width: "100%",
|
|
516
|
+
height: 8,
|
|
517
|
+
fit: "fit",
|
|
518
|
+
protocol: "auto",
|
|
519
|
+
onLoad: (image) => {
|
|
520
|
+
if (this.spec.kind === "image") {
|
|
521
|
+
this.sourceWidth = image.width;
|
|
522
|
+
this.sourceHeight = image.height;
|
|
523
|
+
}
|
|
524
|
+
this.refreshGeometry();
|
|
525
|
+
},
|
|
526
|
+
onError: () => {
|
|
527
|
+
this.currentStatus = "Preview unavailable · use Open";
|
|
528
|
+
this.refreshStatus();
|
|
529
|
+
},
|
|
530
|
+
onMouseOver: () => {
|
|
531
|
+
this.hovered = true;
|
|
532
|
+
this.refreshStatus();
|
|
533
|
+
},
|
|
534
|
+
onMouseOut: () => {
|
|
535
|
+
this.hovered = false;
|
|
536
|
+
this.refreshStatus();
|
|
537
|
+
},
|
|
538
|
+
onMouseUp: (event) => {
|
|
539
|
+
if (event.button === 0 && !event.isDragging) {
|
|
540
|
+
void this.open();
|
|
541
|
+
event.stopPropagation();
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
onKeyDown: (event) => {
|
|
545
|
+
if (event.eventType !== "release" && event.name === "return") {
|
|
546
|
+
void this.open();
|
|
547
|
+
event.preventDefault();
|
|
548
|
+
event.stopPropagation();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
this.image.focusable = true;
|
|
553
|
+
this.add(this.image, 1);
|
|
554
|
+
}
|
|
555
|
+
if (this.image) {
|
|
556
|
+
this.image.visible = spec.kind === "image" || spec.kind === "video";
|
|
557
|
+
const source = spec.kind === "image" ? spec.path : spec.previewPath;
|
|
558
|
+
if (source && this.image.source !== source && !this.playback.playing) {
|
|
559
|
+
const epoch = this.sourceEpoch;
|
|
560
|
+
void validateMediaFile(source, spec.projectRoot).then((path) => {
|
|
561
|
+
if (!this.isDestroyed && epoch === this.sourceEpoch && !this.playback.playing && this.image && this.image.source !== path) this.image.source = path;
|
|
562
|
+
}).catch((error) => {
|
|
563
|
+
if (!this.isDestroyed) {
|
|
564
|
+
this.currentStatus = `Preview unavailable · ${error.message}`;
|
|
565
|
+
this.refreshStatus();
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
this.wave.visible = spec.kind === "audio" || spec.kind === "music";
|
|
571
|
+
this.playheadLabel.visible = spec.kind !== "image";
|
|
572
|
+
this.refreshStatus();
|
|
573
|
+
}
|
|
574
|
+
startProbe() {
|
|
575
|
+
if (this.needsProbe && !this.probeController) {
|
|
576
|
+
this.needsProbe = false;
|
|
577
|
+
const spec = this.spec;
|
|
578
|
+
const controller = this.probeController = new AbortController(), epoch = this.sourceEpoch;
|
|
579
|
+
if (spec.kind !== "image") void probeMedia(spec, controller.signal).then((info) => {
|
|
580
|
+
if (controller.signal.aborted || epoch !== this.sourceEpoch || this.isDestroyed) return;
|
|
581
|
+
this.sourceWidth = info.width || 16;
|
|
582
|
+
this.sourceHeight = info.height || 9;
|
|
583
|
+
this.duration = info.durationSec || 0;
|
|
584
|
+
this.refreshGeometry();
|
|
585
|
+
if (spec.kind === "audio" || spec.kind === "music") this.loadWaveform(controller, epoch);
|
|
586
|
+
}).catch((error) => {
|
|
587
|
+
if (!controller.signal.aborted && !this.isDestroyed) {
|
|
588
|
+
this.currentStatus = `Media metadata unavailable · ${error.message}`;
|
|
589
|
+
this.refreshStatus();
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
this.refreshStatus();
|
|
594
|
+
}
|
|
595
|
+
loadWaveform(controller, epoch) {
|
|
596
|
+
execFile2("ffmpeg", ["-nostdin", "-loglevel", "error", "-hwaccel", "none", "-i", this.spec.path, "-t", "600", "-vn", "-ac", "1", "-ar", "1000", "-f", "f32le", "pipe:1"], { encoding: "buffer", signal: controller.signal, timeout: 1e4, maxBuffer: 25e5 }, (error, buffer) => {
|
|
597
|
+
if (controller.signal.aborted || epoch !== this.sourceEpoch || this.isDestroyed) return;
|
|
598
|
+
if (error) {
|
|
599
|
+
this.currentStatus = "Waveform unavailable · playback and Open remain available";
|
|
600
|
+
this.refreshStatus();
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const samples = new Float32Array(Math.floor(buffer.length / 4));
|
|
604
|
+
for (let index = 0; index < samples.length; index++) samples[index] = buffer.readFloatLE(index * 4);
|
|
605
|
+
this.samples = samples;
|
|
606
|
+
this.lastWidth = 0;
|
|
607
|
+
this.refreshGeometry();
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
reconcile(viewport, active = true) {
|
|
611
|
+
if (this.isDestroyed) return;
|
|
612
|
+
let ancestor = this;
|
|
613
|
+
while (ancestor && active) {
|
|
614
|
+
active = ancestor.visible;
|
|
615
|
+
ancestor = ancestor.parent;
|
|
616
|
+
}
|
|
617
|
+
const visible = active && this.y + this.height > viewport.y && this.y < viewport.y + viewport.height;
|
|
618
|
+
if (!visible && this.playback.playing) this.playback.pause();
|
|
619
|
+
if (!visible && this.probeController && !this.samples) {
|
|
620
|
+
this.probeController.abort();
|
|
621
|
+
this.probeController = void 0;
|
|
622
|
+
this.needsProbe = true;
|
|
623
|
+
}
|
|
624
|
+
if (visible) this.startProbe();
|
|
625
|
+
this.refreshGeometry();
|
|
626
|
+
}
|
|
627
|
+
refreshGeometry() {
|
|
628
|
+
if (this.isDestroyed) return;
|
|
629
|
+
const width = Math.max(1, this.width);
|
|
630
|
+
if (this.image) {
|
|
631
|
+
const height = mediaPreviewHeight(width, this.sourceWidth, this.sourceHeight, this.image.cellAspectRatio);
|
|
632
|
+
if (this.image.height !== height) this.image.height = height;
|
|
633
|
+
}
|
|
634
|
+
if (width !== this.lastWidth) {
|
|
635
|
+
this.lastWidth = width;
|
|
636
|
+
if (this.samples) this.waveformText = audioWaveform(this.samples, width);
|
|
637
|
+
}
|
|
638
|
+
this.refreshStatus();
|
|
639
|
+
}
|
|
640
|
+
refreshStatus() {
|
|
641
|
+
if (this.isDestroyed) return;
|
|
642
|
+
const text = `${this.hovered ? "Open ↗ · " : ""}${this.currentStatus}`;
|
|
643
|
+
if (this.status.plainText !== text) this.status.content = text;
|
|
644
|
+
const label = this.playback?.playing ? "Pause" : this.playback?.positionSec ? "Resume" : "Play";
|
|
645
|
+
if (this.playControl.plainText !== label) this.playControl.content = label;
|
|
646
|
+
if (this.waveformText) {
|
|
647
|
+
const cursor = this.duration > 0 ? Math.min(this.waveformText.length - 1, Math.floor(this.playback.positionSec / this.duration * this.waveformText.length)) : 0;
|
|
648
|
+
const waveform = this.playback.positionSec > 0 ? this.waveformText.slice(0, cursor) + "│" + this.waveformText.slice(cursor + 1) : this.waveformText;
|
|
649
|
+
if (this.wave.plainText !== waveform) this.wave.content = waveform;
|
|
650
|
+
}
|
|
651
|
+
const progress = `${time(this.playback?.positionSec ?? 0)} / ${this.duration ? time(this.duration) : "--:--"}${this.duration > 600 && this.samples ? " · waveform shows first 10 min" : ""}`;
|
|
652
|
+
if (this.playheadLabel.plainText !== progress) this.playheadLabel.content = progress;
|
|
653
|
+
}
|
|
654
|
+
togglePlayback() {
|
|
655
|
+
if (this.playback.playing) {
|
|
656
|
+
this.playback.pause();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (this.spec.kind === "image") return;
|
|
660
|
+
void this.playback.play(this.spec, { width: Math.max(2, this.width * 4), height: Math.max(2, (this.image?.height ?? 24) * 8) }, (rgba, width, height) => {
|
|
661
|
+
if (!this.image || this.isDestroyed) return;
|
|
662
|
+
if (!this.pool || this.pool.width !== width || this.pool.height !== height) {
|
|
663
|
+
this.pool?.dispose();
|
|
664
|
+
this.pool = new NativeImagePool({ width, height, capacity: 3, owner: this.renderer.nativeScene.resourceContext });
|
|
665
|
+
}
|
|
666
|
+
const frame = this.pool.publishRgba(rgba);
|
|
667
|
+
if (frame) {
|
|
668
|
+
this.image.source = frame;
|
|
669
|
+
frame.dispose();
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
async open() {
|
|
674
|
+
try {
|
|
675
|
+
const path = await validateMediaFile(this.spec.path, this.spec.projectRoot);
|
|
676
|
+
if (this.isDestroyed) return;
|
|
677
|
+
this.sendEvent({ type: "action", action: "media-open", id: this.mediaId, text: path });
|
|
678
|
+
if (process.env["OMNIUS_MEDIA_TEST_NO_OPEN"] === "1") {
|
|
679
|
+
this.currentStatus = "Open requested (test fixture)";
|
|
680
|
+
this.refreshStatus();
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const command2 = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
684
|
+
execFile2(command2, [path], { timeout: 5e3 }, (error) => {
|
|
685
|
+
if (!this.isDestroyed) {
|
|
686
|
+
this.currentStatus = error ? `Open failed · ${error.message}` : "Opened in the system viewer";
|
|
687
|
+
this.refreshStatus();
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
} catch (error) {
|
|
691
|
+
if (!this.isDestroyed) {
|
|
692
|
+
this.currentStatus = `Open unavailable · ${error instanceof Error ? error.message : String(error)}`;
|
|
693
|
+
this.refreshStatus();
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
applyTheme(colors) {
|
|
698
|
+
this.colors = colors;
|
|
699
|
+
for (const view of [this.location, this.status, this.wave, this.playheadLabel, ...this.controls.getChildren()]) if (view instanceof TextRenderable) {
|
|
700
|
+
view.fg = view === this.wave ? colors.foreground : colors.dim;
|
|
701
|
+
view.bg = colors.background;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
destroy() {
|
|
705
|
+
this.playback.dispose();
|
|
706
|
+
this.probeController?.abort();
|
|
707
|
+
this.pool?.dispose();
|
|
708
|
+
this.pool = void 0;
|
|
709
|
+
super.destroy();
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
173
713
|
// packages/cli/src/tui/native/components/code.ts
|
|
174
714
|
var aliases = {
|
|
175
715
|
js: "javascript",
|
|
@@ -297,10 +837,10 @@ function nativeMarkdownSections(owner, source, streaming = false) {
|
|
|
297
837
|
}
|
|
298
838
|
];
|
|
299
839
|
const rows = source.split("\n"), offsets = [];
|
|
300
|
-
let
|
|
840
|
+
let offset2 = 0;
|
|
301
841
|
for (const row of rows) {
|
|
302
|
-
offsets.push(
|
|
303
|
-
|
|
842
|
+
offsets.push(offset2);
|
|
843
|
+
offset2 += row.length + 1;
|
|
304
844
|
}
|
|
305
845
|
const output = [], stack = [
|
|
306
846
|
{ level: 0, children: output }
|
|
@@ -400,7 +940,7 @@ var NativeBlockView = class {
|
|
|
400
940
|
this.inline = inline;
|
|
401
941
|
this.spec = spec;
|
|
402
942
|
this.pixel = inline && spec.kind === "text" && Boolean(spec.actions?.length) && spec.actions.every((action) => action.id.startsWith("pixel:"));
|
|
403
|
-
this.root = new
|
|
943
|
+
this.root = new BoxRenderable2(context.renderer, {
|
|
404
944
|
id: `block:${spec.id}`,
|
|
405
945
|
flexDirection: "column",
|
|
406
946
|
flexShrink: inline && !this.pixel ? 1 : 0,
|
|
@@ -424,6 +964,7 @@ var NativeBlockView = class {
|
|
|
424
964
|
actions;
|
|
425
965
|
progress;
|
|
426
966
|
image;
|
|
967
|
+
media;
|
|
427
968
|
terminal;
|
|
428
969
|
collapsed = false;
|
|
429
970
|
previousModelCollapsed;
|
|
@@ -443,7 +984,7 @@ var NativeBlockView = class {
|
|
|
443
984
|
this.root.marginBottom = 0;
|
|
444
985
|
this.root.flexDirection = spec.kind === "row" ? "row" : "column";
|
|
445
986
|
if (spec.title) {
|
|
446
|
-
this.title = new
|
|
987
|
+
this.title = new TextRenderable2(renderer2, {
|
|
447
988
|
id: `block-title:${spec.id}`,
|
|
448
989
|
content: "",
|
|
449
990
|
fg: colors.foreground,
|
|
@@ -467,7 +1008,7 @@ var NativeBlockView = class {
|
|
|
467
1008
|
nativeControlFocus(this.title, 1);
|
|
468
1009
|
this.root.add(this.title);
|
|
469
1010
|
}
|
|
470
|
-
this.body = new
|
|
1011
|
+
this.body = new BoxRenderable2(renderer2, {
|
|
471
1012
|
id: `block-body:${spec.id}`,
|
|
472
1013
|
flexDirection: spec.kind === "row" ? "row" : "column",
|
|
473
1014
|
width: this.pixel ? "auto" : "100%",
|
|
@@ -487,8 +1028,12 @@ var NativeBlockView = class {
|
|
|
487
1028
|
});
|
|
488
1029
|
this.context.terminals.set(spec.id, this.terminal);
|
|
489
1030
|
this.body.add(this.terminal);
|
|
1031
|
+
} else if (spec.kind === "media" && spec.media) {
|
|
1032
|
+
this.media = new NativeMediaView(renderer2, spec.id, spec.media, colors, this.context.emit);
|
|
1033
|
+
this.context.mediaViews?.add(this.media);
|
|
1034
|
+
this.body.add(this.media);
|
|
490
1035
|
} else if (spec.kind === "image" && spec.imagePath) {
|
|
491
|
-
this.image = new
|
|
1036
|
+
this.image = new ImageRenderable2(renderer2, {
|
|
492
1037
|
id: `image:${spec.id}`,
|
|
493
1038
|
source: spec.imagePath,
|
|
494
1039
|
width: "100%",
|
|
@@ -499,7 +1044,7 @@ var NativeBlockView = class {
|
|
|
499
1044
|
if (this.image) this.image.visible = false;
|
|
500
1045
|
if (this.text) {
|
|
501
1046
|
this.text.visible = true;
|
|
502
|
-
if (this.text instanceof
|
|
1047
|
+
if (this.text instanceof TextRenderable2) this.text.content = styledText(this.spec.text ?? "Image unavailable");
|
|
503
1048
|
}
|
|
504
1049
|
this.context.emit({ type: "action", action: "image-error", id: spec.id, text: String(error) });
|
|
505
1050
|
},
|
|
@@ -509,7 +1054,7 @@ var NativeBlockView = class {
|
|
|
509
1054
|
}
|
|
510
1055
|
});
|
|
511
1056
|
this.body.add(this.image);
|
|
512
|
-
this.text = new
|
|
1057
|
+
this.text = new TextRenderable2(renderer2, { id: `image-fallback:${spec.id}`, content: "", visible: false, width: "100%", wrapMode: "word", fg: colors.dim, selectable: true });
|
|
513
1058
|
this.body.add(this.text);
|
|
514
1059
|
} else if (spec.kind === "markdown") {
|
|
515
1060
|
this.text = new MarkdownRenderable(renderer2, {
|
|
@@ -561,7 +1106,7 @@ var NativeBlockView = class {
|
|
|
561
1106
|
});
|
|
562
1107
|
this.body.add(this.text);
|
|
563
1108
|
} else if (spec.text !== void 0) {
|
|
564
|
-
this.text = new
|
|
1109
|
+
this.text = new TextRenderable2(renderer2, {
|
|
565
1110
|
id: `text:${spec.id}`,
|
|
566
1111
|
content: "",
|
|
567
1112
|
width: this.pixel ? "auto" : "100%",
|
|
@@ -574,7 +1119,7 @@ var NativeBlockView = class {
|
|
|
574
1119
|
this.body.add(this.text);
|
|
575
1120
|
}
|
|
576
1121
|
if (spec.kind === "progress") {
|
|
577
|
-
this.progress = new
|
|
1122
|
+
this.progress = new TextRenderable2(renderer2, {
|
|
578
1123
|
id: `progress-track:${spec.id}`,
|
|
579
1124
|
width: "100%",
|
|
580
1125
|
height: 1,
|
|
@@ -588,7 +1133,7 @@ var NativeBlockView = class {
|
|
|
588
1133
|
this.body.add(this.progress);
|
|
589
1134
|
}
|
|
590
1135
|
this.children = new NativeBlockList(this.context, this.body);
|
|
591
|
-
this.actions = new
|
|
1136
|
+
this.actions = new BoxRenderable2(renderer2, {
|
|
592
1137
|
id: `block-actions:${spec.id}`,
|
|
593
1138
|
flexDirection: "row",
|
|
594
1139
|
gap: 1,
|
|
@@ -597,6 +1142,11 @@ var NativeBlockView = class {
|
|
|
597
1142
|
this.root.add(this.actions);
|
|
598
1143
|
}
|
|
599
1144
|
reset() {
|
|
1145
|
+
if (this.media) {
|
|
1146
|
+
this.context.mediaViews?.delete(this.media);
|
|
1147
|
+
this.media.destroy();
|
|
1148
|
+
this.media = void 0;
|
|
1149
|
+
}
|
|
600
1150
|
this.children?.destroy();
|
|
601
1151
|
this.markdownSections?.destroy();
|
|
602
1152
|
this.markdownSections = void 0;
|
|
@@ -632,13 +1182,14 @@ var NativeBlockView = class {
|
|
|
632
1182
|
this.markdownSections.destroy();
|
|
633
1183
|
this.markdownSections = void 0;
|
|
634
1184
|
}
|
|
635
|
-
if (this.text instanceof
|
|
1185
|
+
if (this.text instanceof TextRenderable2) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
|
|
636
1186
|
else if (this.text instanceof MarkdownRenderable || this.text instanceof CodeRenderable) {
|
|
637
1187
|
const code = this.text instanceof CodeRenderable ? nativeCodeContent(plainText(spec.text), spec.language) : void 0;
|
|
638
1188
|
this.text.content = sections ? "" : code?.content ?? plainText(spec.text);
|
|
639
1189
|
this.text.streaming = spec.streaming ?? false;
|
|
640
1190
|
if (this.text instanceof CodeRenderable) this.text.filetype = code?.language ?? "text";
|
|
641
1191
|
}
|
|
1192
|
+
if (this.media && spec.media) this.media.update(spec.media);
|
|
642
1193
|
if (this.image && spec.imagePath && this.image.source !== spec.imagePath) this.image.source = spec.imagePath;
|
|
643
1194
|
if (this.terminal) this.terminal.height = spec.height ?? 14;
|
|
644
1195
|
if (this.image) this.image.height = spec.height ?? 12;
|
|
@@ -649,7 +1200,7 @@ var NativeBlockView = class {
|
|
|
649
1200
|
this.actionsSignature = actionsSignature;
|
|
650
1201
|
for (const child of [...this.actions.getChildren()]) child.destroy();
|
|
651
1202
|
for (const action of spec.actions ?? []) {
|
|
652
|
-
const button = new
|
|
1203
|
+
const button = new TextRenderable2(this.context.renderer, {
|
|
653
1204
|
id: `block-action:${spec.id}:${action.id}`,
|
|
654
1205
|
content: plainText(action.label),
|
|
655
1206
|
fg: this.context.colors.foreground,
|
|
@@ -686,6 +1237,20 @@ var NativeBlockView = class {
|
|
|
686
1237
|
if (this.title) this.title.content = `${this.collapsed ? "▸ " : "· "}${plainText(this.spec.title)}`;
|
|
687
1238
|
this.context.emit({ type: "action", action: "toggle-block", id: this.spec.id });
|
|
688
1239
|
}
|
|
1240
|
+
captureDisclosures(result) {
|
|
1241
|
+
if (this.spec.collapsible) Object.defineProperty(result, this.spec.id, { value: this.collapsed, enumerable: true, configurable: true, writable: true });
|
|
1242
|
+
this.children?.captureDisclosures(result);
|
|
1243
|
+
this.markdownSections?.captureDisclosures(result);
|
|
1244
|
+
}
|
|
1245
|
+
restoreDisclosures(state) {
|
|
1246
|
+
if (this.spec.collapsible && Object.hasOwn(state, this.spec.id)) {
|
|
1247
|
+
this.collapsed = state[this.spec.id];
|
|
1248
|
+
if (this.body) this.body.visible = !this.collapsed;
|
|
1249
|
+
if (this.title) this.title.content = `${this.collapsed ? "▸ " : "· "}${plainText(this.spec.title)}`;
|
|
1250
|
+
}
|
|
1251
|
+
this.children?.restoreDisclosures(state);
|
|
1252
|
+
this.markdownSections?.restoreDisclosures(state);
|
|
1253
|
+
}
|
|
689
1254
|
applyTheme() {
|
|
690
1255
|
const colors = this.context.colors;
|
|
691
1256
|
this.root.backgroundColor = colors.background;
|
|
@@ -704,14 +1269,20 @@ var NativeBlockView = class {
|
|
|
704
1269
|
this.progress.fg = colors.foreground;
|
|
705
1270
|
this.progress.bg = colors.background;
|
|
706
1271
|
}
|
|
707
|
-
for (const action of this.actions?.getChildren() ?? []) if (action instanceof
|
|
1272
|
+
for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable2) {
|
|
708
1273
|
action.fg = colors.foreground;
|
|
709
1274
|
action.bg = colors.background;
|
|
710
1275
|
}
|
|
1276
|
+
this.media?.applyTheme(colors);
|
|
711
1277
|
this.children?.applyTheme();
|
|
712
1278
|
this.markdownSections?.applyTheme();
|
|
713
1279
|
}
|
|
714
1280
|
destroy() {
|
|
1281
|
+
if (this.media) {
|
|
1282
|
+
this.context.mediaViews?.delete(this.media);
|
|
1283
|
+
this.media.destroy();
|
|
1284
|
+
this.media = void 0;
|
|
1285
|
+
}
|
|
715
1286
|
this.children?.destroy();
|
|
716
1287
|
this.markdownSections?.destroy();
|
|
717
1288
|
if (this.terminal) this.context.terminals.delete(this.spec.id);
|
|
@@ -750,7 +1321,10 @@ var NativeBlockList = class {
|
|
|
750
1321
|
reorder(order) {
|
|
751
1322
|
const next = order.filter((id) => this.views.has(id));
|
|
752
1323
|
if (next.length === this.order.length && next.every((id, index) => this.order[index] === id)) return;
|
|
753
|
-
next.forEach((id, index) =>
|
|
1324
|
+
next.forEach((id, index) => {
|
|
1325
|
+
const view = this.views.get(id).root;
|
|
1326
|
+
if (this.parent.getChildren()[index] !== view) this.parent.add(view, index);
|
|
1327
|
+
});
|
|
754
1328
|
this.order = next;
|
|
755
1329
|
}
|
|
756
1330
|
remove(id) {
|
|
@@ -761,6 +1335,13 @@ var NativeBlockList = class {
|
|
|
761
1335
|
applyTheme() {
|
|
762
1336
|
for (const view of this.views.values()) view.applyTheme();
|
|
763
1337
|
}
|
|
1338
|
+
captureDisclosures(result = {}) {
|
|
1339
|
+
for (const view of this.views.values()) view.captureDisclosures(result);
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
restoreDisclosures(state) {
|
|
1343
|
+
for (const view of this.views.values()) view.restoreDisclosures(state);
|
|
1344
|
+
}
|
|
764
1345
|
clear() {
|
|
765
1346
|
for (const view of this.views.values()) view.destroy();
|
|
766
1347
|
this.views.clear();
|
|
@@ -773,12 +1354,12 @@ var NativeBlockList = class {
|
|
|
773
1354
|
|
|
774
1355
|
// packages/cli/src/tui/native/components/modals.ts
|
|
775
1356
|
import {
|
|
776
|
-
BoxRenderable as
|
|
1357
|
+
BoxRenderable as BoxRenderable4,
|
|
777
1358
|
InputRenderable,
|
|
778
1359
|
ScrollBoxRenderable as ScrollBoxRenderable2,
|
|
779
1360
|
TextareaRenderable,
|
|
780
1361
|
TextAttributes as TextAttributes4,
|
|
781
|
-
TextRenderable as
|
|
1362
|
+
TextRenderable as TextRenderable3
|
|
782
1363
|
} from "@opentui/core";
|
|
783
1364
|
|
|
784
1365
|
// packages/cli/src/tui/native/components/select.ts
|
|
@@ -820,8 +1401,8 @@ function styleNativeSlider(slider, colors) {
|
|
|
820
1401
|
const seek = (event) => {
|
|
821
1402
|
const horizontal = slider.orientation === "horizontal";
|
|
822
1403
|
const length = horizontal ? slider.width : slider.height;
|
|
823
|
-
const
|
|
824
|
-
const progress = length > 1 ? Math.max(0, Math.min(1,
|
|
1404
|
+
const offset2 = horizontal ? event.x - slider.x : event.y - slider.y;
|
|
1405
|
+
const progress = length > 1 ? Math.max(0, Math.min(1, offset2 / (length - 1))) : 0;
|
|
825
1406
|
slider.value = slider.min + progress * (slider.max - slider.min);
|
|
826
1407
|
event.preventDefault();
|
|
827
1408
|
event.stopPropagation();
|
|
@@ -854,8 +1435,8 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
854
1435
|
this.renderAfter = (buffer) => {
|
|
855
1436
|
const count = this.visibleCount();
|
|
856
1437
|
if (!this.showScrollIndicator || this.options.length <= count) return;
|
|
857
|
-
const
|
|
858
|
-
drawDotRail(buffer, this.width - 1, 0, this.height, false,
|
|
1438
|
+
const offset2 = Math.max(0, Math.min(this.getSelectedIndex() - Math.floor(count / 2), this.options.length - count));
|
|
1439
|
+
drawDotRail(buffer, this.width - 1, 0, this.height, false, offset2 / (this.options.length - count), colors);
|
|
859
1440
|
};
|
|
860
1441
|
}
|
|
861
1442
|
visibleCount() {
|
|
@@ -931,7 +1512,7 @@ var NativeSelectRenderable = class extends SelectRenderable {
|
|
|
931
1512
|
};
|
|
932
1513
|
|
|
933
1514
|
// packages/cli/src/tui/native/components/corner-frame.ts
|
|
934
|
-
import { BoxRenderable as
|
|
1515
|
+
import { BoxRenderable as BoxRenderable3, CliRenderEvents as CliRenderEvents2, RenderableEvents as RenderableEvents2, TextAttributes as TextAttributes3 } from "@opentui/core";
|
|
935
1516
|
function drawCornerFrame(buffer, panel, colors, progress = 1) {
|
|
936
1517
|
const { x, y, width, height } = panel;
|
|
937
1518
|
if (width <= 0 || height <= 0) return;
|
|
@@ -970,7 +1551,7 @@ var NativeCornerFrame = class {
|
|
|
970
1551
|
this.colors = colors;
|
|
971
1552
|
this.renderer = renderer2;
|
|
972
1553
|
this.container = container;
|
|
973
|
-
this.chrome = new
|
|
1554
|
+
this.chrome = new BoxRenderable3(renderer2, {
|
|
974
1555
|
id: `${panel.id}:corners`,
|
|
975
1556
|
position: "absolute",
|
|
976
1557
|
left: 0,
|
|
@@ -1100,7 +1681,7 @@ var NativeModalView = class {
|
|
|
1100
1681
|
this.close = close2;
|
|
1101
1682
|
this.spec = spec;
|
|
1102
1683
|
const { renderer: renderer2, colors } = context;
|
|
1103
|
-
this.root = new
|
|
1684
|
+
this.root = new BoxRenderable4(renderer2, {
|
|
1104
1685
|
id: `modal:${spec.id}`,
|
|
1105
1686
|
position: "absolute",
|
|
1106
1687
|
top: 0,
|
|
@@ -1122,7 +1703,7 @@ var NativeModalView = class {
|
|
|
1122
1703
|
}
|
|
1123
1704
|
}
|
|
1124
1705
|
});
|
|
1125
|
-
this.panel = new
|
|
1706
|
+
this.panel = new BoxRenderable4(renderer2, {
|
|
1126
1707
|
id: `modal-panel:${spec.id}`,
|
|
1127
1708
|
flexDirection: "column",
|
|
1128
1709
|
width: "94%",
|
|
@@ -1138,7 +1719,12 @@ var NativeModalView = class {
|
|
|
1138
1719
|
this.root.add(this.panel);
|
|
1139
1720
|
this.frame = new NativeCornerFrame(this.panel, colors, renderer2, this.root);
|
|
1140
1721
|
this.breadcrumb = this.addText(`modal-breadcrumb:${spec.id}`);
|
|
1141
|
-
|
|
1722
|
+
const header = new BoxRenderable4(renderer2, { id: `modal-heading:${spec.id}`, width: "100%", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
1723
|
+
this.heading = new TextRenderable3(renderer2, { id: `modal-title:${spec.id}`, content: "", width: 0, flexGrow: 1, flexShrink: 1, wrapMode: "word", fg: colors.foreground });
|
|
1724
|
+
this.headerActions = new BoxRenderable4(renderer2, { id: `modal-header-actions:${spec.id}`, flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
1725
|
+
header.add(this.heading);
|
|
1726
|
+
header.add(this.headerActions);
|
|
1727
|
+
this.panel.add(header);
|
|
1142
1728
|
this.description = this.addText(`modal-description:${spec.id}`);
|
|
1143
1729
|
if (spec.kind === "select") this.buildSelect();
|
|
1144
1730
|
else if (spec.kind === "input") this.buildInput();
|
|
@@ -1156,7 +1742,7 @@ var NativeModalView = class {
|
|
|
1156
1742
|
this.blocks = new NativeBlockList(this.modalContext(), this.document);
|
|
1157
1743
|
this.panel.add(this.document);
|
|
1158
1744
|
} else {
|
|
1159
|
-
this.progress = new
|
|
1745
|
+
this.progress = new TextRenderable3(renderer2, {
|
|
1160
1746
|
id: `modal-progress:${spec.id}`,
|
|
1161
1747
|
width: "100%",
|
|
1162
1748
|
height: 1,
|
|
@@ -1168,13 +1754,13 @@ var NativeModalView = class {
|
|
|
1168
1754
|
selectable: false
|
|
1169
1755
|
});
|
|
1170
1756
|
this.panel.add(this.progress);
|
|
1171
|
-
const progressBlocks = new
|
|
1757
|
+
const progressBlocks = new BoxRenderable4(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
|
|
1172
1758
|
this.panel.add(progressBlocks);
|
|
1173
1759
|
this.blocks = new NativeBlockList(this.modalContext(), progressBlocks);
|
|
1174
1760
|
}
|
|
1175
1761
|
this.error = this.addText(`modal-error:${spec.id}`);
|
|
1176
1762
|
this.hints = this.addText(`modal-hints:${spec.id}`);
|
|
1177
|
-
this.actions = new
|
|
1763
|
+
this.actions = new BoxRenderable4(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", gap: 1, width: "100%", marginTop: 0 });
|
|
1178
1764
|
this.panel.add(this.actions);
|
|
1179
1765
|
this.update(spec);
|
|
1180
1766
|
}
|
|
@@ -1190,6 +1776,7 @@ var NativeModalView = class {
|
|
|
1190
1776
|
error;
|
|
1191
1777
|
hints;
|
|
1192
1778
|
actions;
|
|
1779
|
+
headerActions;
|
|
1193
1780
|
filterInput;
|
|
1194
1781
|
editor;
|
|
1195
1782
|
secretMask;
|
|
@@ -1201,11 +1788,39 @@ var NativeModalView = class {
|
|
|
1201
1788
|
filter = "";
|
|
1202
1789
|
changing = false;
|
|
1203
1790
|
actionsSignature = "";
|
|
1791
|
+
headerActionsSignature = "";
|
|
1204
1792
|
addText(id) {
|
|
1205
|
-
const text = new
|
|
1793
|
+
const text = new TextRenderable3(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
|
|
1206
1794
|
this.panel.add(text);
|
|
1207
1795
|
return text;
|
|
1208
1796
|
}
|
|
1797
|
+
replaceActions(container, actions, prefix = "") {
|
|
1798
|
+
for (const child of [...container.getChildren()]) child.destroy();
|
|
1799
|
+
for (const action of actions) {
|
|
1800
|
+
const button = new TextRenderable3(this.context.renderer, {
|
|
1801
|
+
id: `modal-action:${this.spec.id}:${prefix}${action.id}`,
|
|
1802
|
+
content: plainText(action.label),
|
|
1803
|
+
fg: this.context.colors.foreground,
|
|
1804
|
+
bg: this.context.colors.background,
|
|
1805
|
+
attributes: action.disabled ? 2 : action.active ? 1 : 0,
|
|
1806
|
+
selectable: false,
|
|
1807
|
+
onMouseDown: (event) => {
|
|
1808
|
+
if (event.button !== 0 || action.disabled) return;
|
|
1809
|
+
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1810
|
+
event.stopPropagation();
|
|
1811
|
+
},
|
|
1812
|
+
onKeyDown: (key) => {
|
|
1813
|
+
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
1814
|
+
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1815
|
+
key.preventDefault();
|
|
1816
|
+
}
|
|
1817
|
+
});
|
|
1818
|
+
button.focusable = !action.disabled;
|
|
1819
|
+
nativeControlFocus(button, action.disabled ? 2 : action.active ? 1 : 0);
|
|
1820
|
+
container.add(button);
|
|
1821
|
+
}
|
|
1822
|
+
container.visible = actions.length > 0;
|
|
1823
|
+
}
|
|
1209
1824
|
buildSelect() {
|
|
1210
1825
|
const { renderer: renderer2, colors } = this.context;
|
|
1211
1826
|
this.filterInput = new InputRenderable(renderer2, {
|
|
@@ -1308,34 +1923,15 @@ var NativeModalView = class {
|
|
|
1308
1923
|
this.refreshOptions(patch.selectedIndex);
|
|
1309
1924
|
this.blocks?.replace(spec.blocks ?? []);
|
|
1310
1925
|
if (this.progress) this.progress.content = nativeProgressText(spec.progress, true);
|
|
1926
|
+
const headerActionsSignature = JSON.stringify(spec.headerActions ?? []);
|
|
1927
|
+
if (headerActionsSignature !== this.headerActionsSignature) {
|
|
1928
|
+
this.headerActionsSignature = headerActionsSignature;
|
|
1929
|
+
this.replaceActions(this.headerActions, spec.headerActions ?? [], "header:");
|
|
1930
|
+
}
|
|
1311
1931
|
const actionsSignature = JSON.stringify(spec.actions ?? []);
|
|
1312
1932
|
if (actionsSignature !== this.actionsSignature) {
|
|
1313
1933
|
this.actionsSignature = actionsSignature;
|
|
1314
|
-
|
|
1315
|
-
for (const action of spec.actions ?? []) {
|
|
1316
|
-
const button = new TextRenderable2(this.context.renderer, {
|
|
1317
|
-
id: `modal-action:${spec.id}:${action.id}`,
|
|
1318
|
-
content: plainText(action.label),
|
|
1319
|
-
fg: this.context.colors.foreground,
|
|
1320
|
-
bg: this.context.colors.background,
|
|
1321
|
-
attributes: action.disabled ? 2 : action.active ? 1 : 0,
|
|
1322
|
-
selectable: false,
|
|
1323
|
-
onMouseDown: (event) => {
|
|
1324
|
-
if (event.button !== 0 || action.disabled) return;
|
|
1325
|
-
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1326
|
-
event.stopPropagation();
|
|
1327
|
-
},
|
|
1328
|
-
onKeyDown: (key) => {
|
|
1329
|
-
if (key.eventType === "release" || key.name !== "return" && key.sequence !== " " || action.disabled) return;
|
|
1330
|
-
this.emitAction(action.command ?? action.id, this.editor?.plainText);
|
|
1331
|
-
key.preventDefault();
|
|
1332
|
-
}
|
|
1333
|
-
});
|
|
1334
|
-
button.focusable = !action.disabled;
|
|
1335
|
-
nativeControlFocus(button, action.disabled ? 2 : action.active ? 1 : 0);
|
|
1336
|
-
this.actions.add(button);
|
|
1337
|
-
}
|
|
1338
|
-
this.actions.visible = (spec.actions?.length ?? 0) > 0;
|
|
1934
|
+
this.replaceActions(this.actions, spec.actions ?? []);
|
|
1339
1935
|
}
|
|
1340
1936
|
} finally {
|
|
1341
1937
|
this.changing = false;
|
|
@@ -1390,7 +1986,7 @@ var NativeModalView = class {
|
|
|
1390
1986
|
this.close(this.spec.id, { confirmed: true, key: current?.item.key ?? null, index: current?.index ?? -1, ...value === void 0 ? {} : { value } });
|
|
1391
1987
|
}
|
|
1392
1988
|
cancel(action = "cancel") {
|
|
1393
|
-
if (this.frame.closing) return;
|
|
1989
|
+
if (this.frame.closing || this.spec.nonDismissible) return;
|
|
1394
1990
|
if (this.spec.deferSelection) {
|
|
1395
1991
|
this.emitAction(action);
|
|
1396
1992
|
return;
|
|
@@ -1559,7 +2155,7 @@ var NativeModalView = class {
|
|
|
1559
2155
|
editor.placeholderColor = colors.foreground;
|
|
1560
2156
|
}
|
|
1561
2157
|
if (this.secretMask) this.secretMask.fg = colors.foreground;
|
|
1562
|
-
for (const action of this.actions.getChildren()) if (action instanceof
|
|
2158
|
+
for (const action of [...this.actions.getChildren(), ...this.headerActions.getChildren()]) if (action instanceof TextRenderable3) {
|
|
1563
2159
|
action.fg = colors.foreground;
|
|
1564
2160
|
action.bg = colors.background;
|
|
1565
2161
|
}
|
|
@@ -1643,12 +2239,12 @@ var NativeModalStack = class {
|
|
|
1643
2239
|
|
|
1644
2240
|
// packages/cli/src/tui/native/components/instruments.ts
|
|
1645
2241
|
import {
|
|
1646
|
-
BoxRenderable as
|
|
2242
|
+
BoxRenderable as BoxRenderable5,
|
|
1647
2243
|
RGBA as RGBA2,
|
|
1648
2244
|
RenderableEvents as RenderableEvents3,
|
|
1649
2245
|
ScrollBoxRenderable as ScrollBoxRenderable3,
|
|
1650
2246
|
TextAttributes as TextAttributes5,
|
|
1651
|
-
TextRenderable as
|
|
2247
|
+
TextRenderable as TextRenderable4
|
|
1652
2248
|
} from "@opentui/core";
|
|
1653
2249
|
|
|
1654
2250
|
// packages/cli/src/tui/native/components/device-history.ts
|
|
@@ -1848,7 +2444,7 @@ function instrumentStatus(value) {
|
|
|
1848
2444
|
// packages/cli/src/tui/native/components/instruments.ts
|
|
1849
2445
|
var foreground = RGBA2.defaultForeground();
|
|
1850
2446
|
var background = RGBA2.defaultBackground();
|
|
1851
|
-
var SectionBanner = class extends
|
|
2447
|
+
var SectionBanner = class extends TextRenderable4 {
|
|
1852
2448
|
constructor(renderer2, title, id, activate) {
|
|
1853
2449
|
super(renderer2, {
|
|
1854
2450
|
id,
|
|
@@ -1903,7 +2499,7 @@ var SectionBanner = class extends TextRenderable3 {
|
|
|
1903
2499
|
if (this.plainText !== next) this.content = next;
|
|
1904
2500
|
}
|
|
1905
2501
|
};
|
|
1906
|
-
var TelemetryRow = class extends
|
|
2502
|
+
var TelemetryRow = class extends TextRenderable4 {
|
|
1907
2503
|
constructor(renderer2, label, id, reserved = 12) {
|
|
1908
2504
|
super(renderer2, {
|
|
1909
2505
|
id,
|
|
@@ -1949,7 +2545,7 @@ var SignalMeter = class extends TelemetryRow {
|
|
|
1949
2545
|
this.update(formatSignalMeter(value, maximum));
|
|
1950
2546
|
}
|
|
1951
2547
|
};
|
|
1952
|
-
var InstrumentDivider = class extends
|
|
2548
|
+
var InstrumentDivider = class extends TextRenderable4 {
|
|
1953
2549
|
constructor(renderer2, actions) {
|
|
1954
2550
|
super(renderer2, {
|
|
1955
2551
|
id: "omnius-instrument-divider",
|
|
@@ -2046,17 +2642,17 @@ var InstrumentDivider = class extends TextRenderable3 {
|
|
|
2046
2642
|
this.attributes = this.focused ? TextAttributes5.INVERSE : TextAttributes5.DIM;
|
|
2047
2643
|
}
|
|
2048
2644
|
};
|
|
2049
|
-
var DeviceMonitor = class extends
|
|
2645
|
+
var DeviceMonitor = class extends BoxRenderable5 {
|
|
2050
2646
|
constructor(renderer2, action) {
|
|
2051
2647
|
super(renderer2, { id: "omnius-devices", width: "100%", height: 5, minHeight: 5, maxHeight: "60%", flexShrink: 1, flexDirection: "column", visible: false });
|
|
2052
2648
|
this.action = action;
|
|
2053
2649
|
this.heading = new SectionBanner(renderer2, "DEVICES", "omnius-devices:heading", () => this.toggle());
|
|
2054
2650
|
this.heading.update("DEVICES", false);
|
|
2055
2651
|
this.add(this.heading);
|
|
2056
|
-
this.summary = new
|
|
2652
|
+
this.summary = new TextRenderable4(renderer2, { id: "omnius-devices:summary", content: "--", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
|
|
2057
2653
|
this.add(this.summary);
|
|
2058
|
-
this.compactLoad = new
|
|
2059
|
-
this.compactMemory = new
|
|
2654
|
+
this.compactLoad = new TextRenderable4(renderer2, { id: "omnius-devices:load-history", content: "", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
|
|
2655
|
+
this.compactMemory = new TextRenderable4(renderer2, { id: "omnius-devices:memory-history", content: "", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
|
|
2060
2656
|
this.add(this.compactLoad);
|
|
2061
2657
|
this.add(this.compactMemory);
|
|
2062
2658
|
this.scroll = new ScrollBoxRenderable3(renderer2, {
|
|
@@ -2074,7 +2670,7 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2074
2670
|
contentOptions: { flexDirection: "column" }
|
|
2075
2671
|
});
|
|
2076
2672
|
this.add(this.scroll);
|
|
2077
|
-
this.notes = new
|
|
2673
|
+
this.notes = new TextRenderable4(renderer2, {
|
|
2078
2674
|
id: "omnius-devices:notes",
|
|
2079
2675
|
content: "",
|
|
2080
2676
|
width: "100%",
|
|
@@ -2086,7 +2682,7 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2086
2682
|
truncate: true
|
|
2087
2683
|
});
|
|
2088
2684
|
this.scroll.add(this.notes);
|
|
2089
|
-
const controls = new
|
|
2685
|
+
const controls = new BoxRenderable5(renderer2, { id: "omnius-devices:controls", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2 });
|
|
2090
2686
|
this.add(controls);
|
|
2091
2687
|
this.policy = this.button(renderer2, "omnius-devices:policy", "GPU POLICY", () => {
|
|
2092
2688
|
if (this.canSelect()) this.action?.("/gpu");
|
|
@@ -2110,7 +2706,7 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2110
2706
|
historySource = "";
|
|
2111
2707
|
rows = /* @__PURE__ */ new Map();
|
|
2112
2708
|
button(renderer2, id, label, activate) {
|
|
2113
|
-
const button = new
|
|
2709
|
+
const button = new TextRenderable4(renderer2, {
|
|
2114
2710
|
id,
|
|
2115
2711
|
content: label,
|
|
2116
2712
|
height: 1,
|
|
@@ -2173,7 +2769,7 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2173
2769
|
for (const device of snapshot?.devices ?? []) {
|
|
2174
2770
|
let row = this.rows.get(device.uuid);
|
|
2175
2771
|
if (!row) {
|
|
2176
|
-
const root = new
|
|
2772
|
+
const root = new BoxRenderable5(this._ctx, {
|
|
2177
2773
|
id: `omnius-device:${device.uuid}`,
|
|
2178
2774
|
width: "100%",
|
|
2179
2775
|
flexDirection: "column",
|
|
@@ -2192,9 +2788,9 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2192
2788
|
title.on(RenderableEvents3.FOCUSED, () => this.scroll.scrollChildIntoView(root.id));
|
|
2193
2789
|
const load = new TelemetryRow(this._ctx, "LOAD", `${root.id}:load`, 16);
|
|
2194
2790
|
const memory = new TelemetryRow(this._ctx, "VRAM", `${root.id}:memory`, 16);
|
|
2195
|
-
const plot = (name) => new
|
|
2791
|
+
const plot = (name) => new TextRenderable4(this._ctx, { id: `${root.id}:${name}-history`, content: "", width: "100%", height: 3, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true, selectable: false });
|
|
2196
2792
|
const loadGraph = plot("load"), memoryGraph = plot("memory"), history = new DeviceHistory();
|
|
2197
|
-
const reason = new
|
|
2793
|
+
const reason = new TextRenderable4(this._ctx, {
|
|
2198
2794
|
id: `${root.id}:reason`,
|
|
2199
2795
|
content: "",
|
|
2200
2796
|
width: "100%",
|
|
@@ -2241,6 +2837,12 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2241
2837
|
this.fitHeight(this.availableRows + 2);
|
|
2242
2838
|
this.refresh();
|
|
2243
2839
|
}
|
|
2840
|
+
get isExpanded() {
|
|
2841
|
+
return this.expanded;
|
|
2842
|
+
}
|
|
2843
|
+
setExpanded(expanded) {
|
|
2844
|
+
if (this.expanded !== expanded) this.toggle();
|
|
2845
|
+
}
|
|
2244
2846
|
fitHeight(railRows) {
|
|
2245
2847
|
this.availableRows = Math.max(0, railRows - 2);
|
|
2246
2848
|
this.visible = Boolean(this.snapshot) && this.availableRows >= 3;
|
|
@@ -2299,7 +2901,7 @@ var DeviceMonitor = class extends BoxRenderable4 {
|
|
|
2299
2901
|
];
|
|
2300
2902
|
}
|
|
2301
2903
|
};
|
|
2302
|
-
var ChannelMap = class extends
|
|
2904
|
+
var ChannelMap = class extends BoxRenderable5 {
|
|
2303
2905
|
constructor(renderer2, id, inspect, reveal) {
|
|
2304
2906
|
super(renderer2, {
|
|
2305
2907
|
id,
|
|
@@ -2331,7 +2933,7 @@ var ChannelMap = class extends BoxRenderable4 {
|
|
|
2331
2933
|
if (!this.channels.has(agent.id))
|
|
2332
2934
|
this.channels.set(agent.id, this.channels.size + 1);
|
|
2333
2935
|
if (!this.rows.has(agent.id)) {
|
|
2334
|
-
const row = new
|
|
2936
|
+
const row = new TextRenderable4(this._ctx, {
|
|
2335
2937
|
id: `${this.id}:channel:${agent.id}`,
|
|
2336
2938
|
content: "",
|
|
2337
2939
|
width: "100%",
|
|
@@ -2397,7 +2999,7 @@ var ChannelMap = class extends BoxRenderable4 {
|
|
|
2397
2999
|
}
|
|
2398
3000
|
}
|
|
2399
3001
|
};
|
|
2400
|
-
var InstrumentRail = class extends
|
|
3002
|
+
var InstrumentRail = class extends BoxRenderable5 {
|
|
2401
3003
|
scroll;
|
|
2402
3004
|
inferenceHeader;
|
|
2403
3005
|
sessionHeader;
|
|
@@ -2451,7 +3053,7 @@ var InstrumentRail = class extends BoxRenderable4 {
|
|
|
2451
3053
|
() => this.reveal(this.inferenceHeader)
|
|
2452
3054
|
);
|
|
2453
3055
|
this.scroll.add(this.inferenceHeader);
|
|
2454
|
-
this.inference = new
|
|
3056
|
+
this.inference = new BoxRenderable5(renderer2, {
|
|
2455
3057
|
id: `${id}:inference-body`,
|
|
2456
3058
|
width: "100%",
|
|
2457
3059
|
flexDirection: "column",
|
|
@@ -2466,7 +3068,7 @@ var InstrumentRail = class extends BoxRenderable4 {
|
|
|
2466
3068
|
};
|
|
2467
3069
|
addRow(this.inference, "route", "ROUTE");
|
|
2468
3070
|
addRow(this.inference, "backend", "BACKEND");
|
|
2469
|
-
this.model = new
|
|
3071
|
+
this.model = new TextRenderable4(renderer2, {
|
|
2470
3072
|
id: `${id}:model`,
|
|
2471
3073
|
content: "MODEL --",
|
|
2472
3074
|
width: "100%",
|
|
@@ -2495,7 +3097,7 @@ var InstrumentRail = class extends BoxRenderable4 {
|
|
|
2495
3097
|
});
|
|
2496
3098
|
this.contextHeader.on(RenderableEvents3.FOCUSED, () => this.reveal(this.contextHeader));
|
|
2497
3099
|
this.inference.add(this.contextHeader);
|
|
2498
|
-
this.contextDetails = new
|
|
3100
|
+
this.contextDetails = new BoxRenderable5(renderer2, { id: `${id}:context-detail-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
|
|
2499
3101
|
this.inference.add(this.contextDetails);
|
|
2500
3102
|
this.contextHeader.update("CONTEXT DETAILS", false);
|
|
2501
3103
|
addRow(this.contextDetails, "messages", "MESSAGES");
|
|
@@ -2514,7 +3116,7 @@ var InstrumentRail = class extends BoxRenderable4 {
|
|
|
2514
3116
|
() => this.reveal(this.sessionHeader)
|
|
2515
3117
|
);
|
|
2516
3118
|
this.scroll.add(this.sessionHeader);
|
|
2517
|
-
this.session = new
|
|
3119
|
+
this.session = new BoxRenderable5(renderer2, {
|
|
2518
3120
|
id: `${id}:session-body`,
|
|
2519
3121
|
width: "100%",
|
|
2520
3122
|
flexDirection: "column",
|
|
@@ -2597,7 +3199,7 @@ var InstrumentRail = class extends BoxRenderable4 {
|
|
|
2597
3199
|
applyTheme(colors) {
|
|
2598
3200
|
this.backgroundColor = colors.background;
|
|
2599
3201
|
const visit = (node) => {
|
|
2600
|
-
if (node instanceof
|
|
3202
|
+
if (node instanceof TextRenderable4) {
|
|
2601
3203
|
node.fg = colors.foreground;
|
|
2602
3204
|
node.bg = colors.background;
|
|
2603
3205
|
}
|
|
@@ -2616,6 +3218,25 @@ var InstrumentRail = class extends BoxRenderable4 {
|
|
|
2616
3218
|
body.visible
|
|
2617
3219
|
);
|
|
2618
3220
|
}
|
|
3221
|
+
capturePresentation() {
|
|
3222
|
+
return {
|
|
3223
|
+
inference: this.inference.visible,
|
|
3224
|
+
session: this.session.visible,
|
|
3225
|
+
context: this.contextHeader.expanded,
|
|
3226
|
+
devices: this.devices.isExpanded,
|
|
3227
|
+
scrollTop: this.scroll.scrollTop,
|
|
3228
|
+
deviceScrollTop: this.devices.scroll.scrollTop
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
restorePresentation(state) {
|
|
3232
|
+
if (this.inference.visible !== state.inference) this.toggleSection("inference");
|
|
3233
|
+
if (this.session.visible !== state.session) this.toggleSection("session");
|
|
3234
|
+
this.contextHeader.update("CONTEXT DETAILS", state.context);
|
|
3235
|
+
this.contextDetails.visible = this.contextHeader.visible && state.context;
|
|
3236
|
+
this.devices.setExpanded(state.devices);
|
|
3237
|
+
this.scroll.scrollTo(state.scrollTop);
|
|
3238
|
+
this.devices.scroll.scrollTo(state.deviceScrollTop);
|
|
3239
|
+
}
|
|
2619
3240
|
focusTargets() {
|
|
2620
3241
|
if (!this.visible) return [];
|
|
2621
3242
|
return [
|
|
@@ -3440,6 +4061,7 @@ var ALIASES = {
|
|
|
3440
4061
|
update: ["upgrade"],
|
|
3441
4062
|
indicator: ["tray"],
|
|
3442
4063
|
listen: ["mic"],
|
|
4064
|
+
sound: ["audio"],
|
|
3443
4065
|
pointcloud: ["points", "pcl", "point"],
|
|
3444
4066
|
neovim: ["nvim"],
|
|
3445
4067
|
apikey: ["key"],
|
|
@@ -4074,7 +4696,7 @@ var VimComposer = class {
|
|
|
4074
4696
|
};
|
|
4075
4697
|
|
|
4076
4698
|
// packages/cli/src/tui/native/components/selection-menu.ts
|
|
4077
|
-
import { BoxRenderable as
|
|
4699
|
+
import { BoxRenderable as BoxRenderable6, CliRenderEvents as CliRenderEvents3, TextAttributes as TextAttributes6, TextRenderable as TextRenderable5 } from "@opentui/core";
|
|
4078
4700
|
var NativeSelectionMenu = class {
|
|
4079
4701
|
constructor(renderer2, colors, copy, restoreFocus) {
|
|
4080
4702
|
this.renderer = renderer2;
|
|
@@ -4102,7 +4724,7 @@ var NativeSelectionMenu = class {
|
|
|
4102
4724
|
this.selectedText = text;
|
|
4103
4725
|
this.anchor = { x, y };
|
|
4104
4726
|
this.previousFocus = this.renderer.currentFocusedRenderable;
|
|
4105
|
-
this.overlay = new
|
|
4727
|
+
this.overlay = new BoxRenderable6(this.renderer, {
|
|
4106
4728
|
id: "omnius-selection-menu",
|
|
4107
4729
|
position: "absolute",
|
|
4108
4730
|
left: 0,
|
|
@@ -4118,7 +4740,7 @@ var NativeSelectionMenu = class {
|
|
|
4118
4740
|
event.stopPropagation();
|
|
4119
4741
|
}
|
|
4120
4742
|
});
|
|
4121
|
-
this.panel = new
|
|
4743
|
+
this.panel = new BoxRenderable6(this.renderer, {
|
|
4122
4744
|
id: "omnius-selection-menu-panel",
|
|
4123
4745
|
position: "absolute",
|
|
4124
4746
|
width: 14,
|
|
@@ -4132,7 +4754,7 @@ var NativeSelectionMenu = class {
|
|
|
4132
4754
|
event.stopPropagation();
|
|
4133
4755
|
}
|
|
4134
4756
|
});
|
|
4135
|
-
const action = new
|
|
4757
|
+
const action = new TextRenderable5(this.renderer, {
|
|
4136
4758
|
id: "omnius-selection-copy",
|
|
4137
4759
|
content: "COPY",
|
|
4138
4760
|
height: 1,
|
|
@@ -4181,7 +4803,7 @@ var NativeSelectionMenu = class {
|
|
|
4181
4803
|
if (!this.panel) return;
|
|
4182
4804
|
this.panel.backgroundColor = colors.background;
|
|
4183
4805
|
this.frame?.setColors(colors);
|
|
4184
|
-
for (const child of this.panel.getChildren()) if (child instanceof
|
|
4806
|
+
for (const child of this.panel.getChildren()) if (child instanceof TextRenderable5) {
|
|
4185
4807
|
child.fg = colors.foreground;
|
|
4186
4808
|
child.bg = colors.background;
|
|
4187
4809
|
}
|
|
@@ -4207,8 +4829,8 @@ var NativeSelectionMenu = class {
|
|
|
4207
4829
|
};
|
|
4208
4830
|
|
|
4209
4831
|
// packages/cli/src/tui/native/components/tab-strip.ts
|
|
4210
|
-
import { BoxRenderable as
|
|
4211
|
-
var NativeTabStrip = class extends
|
|
4832
|
+
import { BoxRenderable as BoxRenderable7, CliRenderEvents as CliRenderEvents4, ScrollBoxRenderable as ScrollBoxRenderable4, SliderRenderable, TextAttributes as TextAttributes7, TextRenderable as TextRenderable6 } from "@opentui/core";
|
|
4833
|
+
var NativeTabStrip = class extends BoxRenderable7 {
|
|
4212
4834
|
constructor(renderer2, options) {
|
|
4213
4835
|
super(renderer2, {
|
|
4214
4836
|
id: options.id,
|
|
@@ -4287,7 +4909,7 @@ var NativeTabStrip = class extends BoxRenderable6 {
|
|
|
4287
4909
|
for (const [index, action] of items.entries()) {
|
|
4288
4910
|
let entry = this.buttons.get(action.id);
|
|
4289
4911
|
if (!entry) {
|
|
4290
|
-
const button = new
|
|
4912
|
+
const button = new TextRenderable6(this.renderer, {
|
|
4291
4913
|
id: `${this.options.prefix}:${action.id}`,
|
|
4292
4914
|
content: "",
|
|
4293
4915
|
height: 1,
|
|
@@ -4374,6 +4996,10 @@ var NativeTabStrip = class extends BoxRenderable6 {
|
|
|
4374
4996
|
styleNativeScrollbars(this.scroll, colors);
|
|
4375
4997
|
styleNativeSlider(this.rail, colors);
|
|
4376
4998
|
}
|
|
4999
|
+
restoreOffset(offset2) {
|
|
5000
|
+
this.reveal = void 0;
|
|
5001
|
+
this.scroll.scrollLeft = offset2;
|
|
5002
|
+
}
|
|
4377
5003
|
destroySelf() {
|
|
4378
5004
|
this.renderer.off(CliRenderEvents4.FRAME, this.syncScroll);
|
|
4379
5005
|
super.destroySelf();
|
|
@@ -4387,13 +5013,13 @@ var NativeFrontend = class {
|
|
|
4387
5013
|
this.renderer = renderer2;
|
|
4388
5014
|
this.emit = emit;
|
|
4389
5015
|
const colors = palette(initialTheme);
|
|
4390
|
-
this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map() };
|
|
5016
|
+
this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map(), mediaViews: /* @__PURE__ */ new Set() };
|
|
4391
5017
|
this.disposeSelectionContrast = installDefaultSelectionContrast(renderer2);
|
|
4392
5018
|
this.clipboard = options.clipboard ?? createClipboard({ host: createHostClipboard({ timeoutMs: 1500 }), terminal: createRendererClipboardAdapter(renderer2) });
|
|
4393
5019
|
this.selectionMenu = new NativeSelectionMenu(renderer2, colors, (text) => {
|
|
4394
5020
|
void this.copy(text);
|
|
4395
5021
|
}, () => this.restoreFocus());
|
|
4396
|
-
this.root = new
|
|
5022
|
+
this.root = new BoxRenderable8(renderer2, {
|
|
4397
5023
|
id: "omnius-native",
|
|
4398
5024
|
width: "100%",
|
|
4399
5025
|
height: "100%",
|
|
@@ -4401,7 +5027,7 @@ var NativeFrontend = class {
|
|
|
4401
5027
|
backgroundColor: colors.background
|
|
4402
5028
|
});
|
|
4403
5029
|
renderer2.root.add(this.root);
|
|
4404
|
-
this.header = new
|
|
5030
|
+
this.header = new BoxRenderable8(renderer2, {
|
|
4405
5031
|
id: "omnius-header",
|
|
4406
5032
|
width: "100%",
|
|
4407
5033
|
minWidth: 0,
|
|
@@ -4425,9 +5051,9 @@ var NativeFrontend = class {
|
|
|
4425
5051
|
TextAttributes8.DIM
|
|
4426
5052
|
);
|
|
4427
5053
|
};
|
|
4428
|
-
const headerRow = new
|
|
5054
|
+
const headerRow = new BoxRenderable8(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
|
|
4429
5055
|
this.header.add(headerRow);
|
|
4430
|
-
this.identity = new
|
|
5056
|
+
this.identity = new TextRenderable7(renderer2, {
|
|
4431
5057
|
id: "omnius-identity",
|
|
4432
5058
|
content: "Omnius",
|
|
4433
5059
|
fg: colors.foreground,
|
|
@@ -4465,9 +5091,9 @@ var NativeFrontend = class {
|
|
|
4465
5091
|
verticalScrollbarOptions: { visible: false }
|
|
4466
5092
|
});
|
|
4467
5093
|
headerRow.add(this.headerActions);
|
|
4468
|
-
this.headerPanel = new
|
|
5094
|
+
this.headerPanel = new TextRenderable7(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
|
|
4469
5095
|
this.header.add(this.headerPanel);
|
|
4470
|
-
this.detailButton = new
|
|
5096
|
+
this.detailButton = new TextRenderable7(renderer2, {
|
|
4471
5097
|
id: "omnius-details-toggle",
|
|
4472
5098
|
content: "+ DETAILS",
|
|
4473
5099
|
height: 1,
|
|
@@ -4500,7 +5126,7 @@ var NativeFrontend = class {
|
|
|
4500
5126
|
control.attributes = attributes;
|
|
4501
5127
|
});
|
|
4502
5128
|
}
|
|
4503
|
-
this.sessionRow = new
|
|
5129
|
+
this.sessionRow = new BoxRenderable8(renderer2, {
|
|
4504
5130
|
id: "omnius-sessions-row",
|
|
4505
5131
|
width: "100%",
|
|
4506
5132
|
height: 1,
|
|
@@ -4518,7 +5144,7 @@ var NativeFrontend = class {
|
|
|
4518
5144
|
activate: (id) => this.emitAction("session", id)
|
|
4519
5145
|
});
|
|
4520
5146
|
this.sessionRow.add(this.sessions);
|
|
4521
|
-
this.newSession = new
|
|
5147
|
+
this.newSession = new TextRenderable7(renderer2, {
|
|
4522
5148
|
id: "omnius-session-new",
|
|
4523
5149
|
content: "+ NEW",
|
|
4524
5150
|
width: 5,
|
|
@@ -4547,13 +5173,15 @@ var NativeFrontend = class {
|
|
|
4547
5173
|
this.newSession.on("focused", styleNew);
|
|
4548
5174
|
this.newSession.on("blurred", styleNew);
|
|
4549
5175
|
this.sessionRow.add(this.newSession);
|
|
4550
|
-
this.routeWarning = new
|
|
5176
|
+
this.routeWarning = new TextRenderable7(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes8.BOLD | TextAttributes8.UNDERLINE, visible: false, flexShrink: 0 });
|
|
4551
5177
|
this.root.add(this.routeWarning);
|
|
4552
|
-
this.taskPane = new
|
|
5178
|
+
this.taskPane = new BoxRenderable8(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
|
|
4553
5179
|
this.tasks = new NativeBlockList(this.context, this.taskPane);
|
|
4554
5180
|
this.root.add(this.taskPane);
|
|
4555
|
-
this.body = new
|
|
5181
|
+
this.body = new BoxRenderable8(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
|
|
4556
5182
|
this.root.add(this.body);
|
|
5183
|
+
this.transcriptPane = new BoxRenderable8(renderer2, { id: "omnius-transcript-pane", flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, minHeight: 1 });
|
|
5184
|
+
this.body.add(this.transcriptPane);
|
|
4557
5185
|
this.scroll = new ScrollBoxRenderable5(renderer2, {
|
|
4558
5186
|
id: "omnius-conversation",
|
|
4559
5187
|
flexGrow: 1,
|
|
@@ -4609,7 +5237,7 @@ var NativeFrontend = class {
|
|
|
4609
5237
|
}
|
|
4610
5238
|
} }
|
|
4611
5239
|
});
|
|
4612
|
-
this.
|
|
5240
|
+
this.transcriptPane.add(this.scroll);
|
|
4613
5241
|
this.content = new NativeBlockList(this.context, this.scroll);
|
|
4614
5242
|
this.instrumentDivider = new InstrumentDivider(renderer2, {
|
|
4615
5243
|
resize: (column) => this.resizeInstruments(column),
|
|
@@ -4627,7 +5255,7 @@ var NativeFrontend = class {
|
|
|
4627
5255
|
this.root.onMouseUp = (event) => this.instrumentDivider.handleRelease(event);
|
|
4628
5256
|
this.instruments = new InstrumentRail(renderer2, { colors, onInspectAgent: (id) => this.emitAction("view", id), onAction: (command2) => this.emitAction(command2) });
|
|
4629
5257
|
this.body.add(this.instruments);
|
|
4630
|
-
this.embeddedPane = new
|
|
5258
|
+
this.embeddedPane = new BoxRenderable8(renderer2, {
|
|
4631
5259
|
id: "omnius-editor-pane",
|
|
4632
5260
|
width: "50%",
|
|
4633
5261
|
flexShrink: 0,
|
|
@@ -4638,7 +5266,7 @@ var NativeFrontend = class {
|
|
|
4638
5266
|
visible: false
|
|
4639
5267
|
});
|
|
4640
5268
|
this.body.add(this.embeddedPane);
|
|
4641
|
-
this.liveButton = new
|
|
5269
|
+
this.liveButton = new TextRenderable7(renderer2, {
|
|
4642
5270
|
id: "omnius-live-button",
|
|
4643
5271
|
content: "↓ Live",
|
|
4644
5272
|
width: "100%",
|
|
@@ -4660,9 +5288,9 @@ var NativeFrontend = class {
|
|
|
4660
5288
|
}
|
|
4661
5289
|
});
|
|
4662
5290
|
this.liveButton.focusable = true;
|
|
4663
|
-
this.
|
|
4664
|
-
this.queue = new
|
|
4665
|
-
this.
|
|
5291
|
+
this.transcriptPane.add(this.liveButton);
|
|
5292
|
+
this.queue = new TextRenderable7(renderer2, { id: "omnius-steering-queue", content: "", width: "100%", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, visible: false, flexShrink: 0 });
|
|
5293
|
+
this.transcriptPane.add(this.queue);
|
|
4666
5294
|
this.suggestions = new NativeSelectRenderable(renderer2, {
|
|
4667
5295
|
id: "omnius-suggestions",
|
|
4668
5296
|
width: "100%",
|
|
@@ -4679,10 +5307,10 @@ var NativeFrontend = class {
|
|
|
4679
5307
|
});
|
|
4680
5308
|
this.suggestions.onActivate = () => this.acceptSuggestion();
|
|
4681
5309
|
this.suggestions.on("itemSelected", () => this.acceptSuggestion());
|
|
4682
|
-
this.
|
|
4683
|
-
this.status = new
|
|
4684
|
-
this.
|
|
4685
|
-
this.footer = new
|
|
5310
|
+
this.transcriptPane.add(this.suggestions);
|
|
5311
|
+
this.status = new BoxRenderable8(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
|
|
5312
|
+
this.transcriptPane.add(this.status);
|
|
5313
|
+
this.footer = new BoxRenderable8(renderer2, {
|
|
4686
5314
|
id: "omnius-input-box",
|
|
4687
5315
|
width: "100%",
|
|
4688
5316
|
flexShrink: 0,
|
|
@@ -4691,7 +5319,7 @@ var NativeFrontend = class {
|
|
|
4691
5319
|
flexDirection: "column"
|
|
4692
5320
|
});
|
|
4693
5321
|
this.root.add(this.footer);
|
|
4694
|
-
this.footerRule = new
|
|
5322
|
+
this.footerRule = new BoxRenderable8(renderer2, {
|
|
4695
5323
|
id: "omnius-input-rule",
|
|
4696
5324
|
width: "100%",
|
|
4697
5325
|
height: 1,
|
|
@@ -4719,9 +5347,9 @@ var NativeFrontend = class {
|
|
|
4719
5347
|
sidebarColumn: this.instrumentDivider.visible ? this.instrumentDivider.x - this.footerRule.x : void 0
|
|
4720
5348
|
}), this.footerRule.x, this.footerRule.y, this.context.colors.foreground, this.context.colors.background, TextAttributes8.DIM);
|
|
4721
5349
|
};
|
|
4722
|
-
const inputRow = new
|
|
5350
|
+
const inputRow = new BoxRenderable8(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
|
|
4723
5351
|
this.footer.add(inputRow);
|
|
4724
|
-
this.prompt = new
|
|
5352
|
+
this.prompt = new TextRenderable7(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
|
|
4725
5353
|
inputRow.add(this.prompt);
|
|
4726
5354
|
this.editor = new TextareaRenderable2(renderer2, {
|
|
4727
5355
|
id: "omnius-input",
|
|
@@ -4741,7 +5369,7 @@ var NativeFrontend = class {
|
|
|
4741
5369
|
onCursorChange: () => this.editorChanged(),
|
|
4742
5370
|
onSubmit: () => this.submit()
|
|
4743
5371
|
});
|
|
4744
|
-
this.vimMode = new
|
|
5372
|
+
this.vimMode = new TextRenderable7(renderer2, {
|
|
4745
5373
|
id: "omnius-vim-mode",
|
|
4746
5374
|
content: "INSERT",
|
|
4747
5375
|
height: 1,
|
|
@@ -4762,17 +5390,17 @@ var NativeFrontend = class {
|
|
|
4762
5390
|
this.renderer.requestRender();
|
|
4763
5391
|
});
|
|
4764
5392
|
inputRow.add(this.editor);
|
|
4765
|
-
this.enhance = new
|
|
5393
|
+
this.enhance = new BoxRenderable8(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
|
|
4766
5394
|
inputRow.add(this.enhance);
|
|
4767
|
-
this.activity = new
|
|
5395
|
+
this.activity = new TextRenderable7(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
|
|
4768
5396
|
this.status.add(this.activity);
|
|
4769
|
-
this.media = new
|
|
4770
|
-
this.mediaText = new
|
|
5397
|
+
this.media = new BoxRenderable8(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
|
|
5398
|
+
this.mediaText = new TextRenderable7(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
|
|
4771
5399
|
this.media.add(this.mediaText);
|
|
4772
|
-
this.micTrack = new
|
|
5400
|
+
this.micTrack = new TextRenderable7(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
|
|
4773
5401
|
this.media.add(this.micTrack);
|
|
4774
5402
|
this.status.add(this.media);
|
|
4775
|
-
this.copyFeedback = new
|
|
5403
|
+
this.copyFeedback = new TextRenderable7(renderer2, {
|
|
4776
5404
|
id: "omnius-copy-feedback",
|
|
4777
5405
|
content: "",
|
|
4778
5406
|
height: 1,
|
|
@@ -4783,14 +5411,14 @@ var NativeFrontend = class {
|
|
|
4783
5411
|
visible: false
|
|
4784
5412
|
});
|
|
4785
5413
|
this.status.add(this.copyFeedback);
|
|
4786
|
-
this.metricsRow = new
|
|
4787
|
-
this.keyHints = new
|
|
5414
|
+
this.metricsRow = new BoxRenderable8(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
|
|
5415
|
+
this.keyHints = new TextRenderable7(renderer2, { id: "omnius-key-hints", content: "F1 HELP TAB FOCUS", height: 1, flexShrink: 0, attributes: TextAttributes8.DIM, fg: colors.foreground, selectable: false });
|
|
4788
5416
|
this.metricsRow.add(this.keyHints);
|
|
4789
|
-
this.metricsStartActions = new
|
|
5417
|
+
this.metricsStartActions = new BoxRenderable8(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
4790
5418
|
this.metricsRow.add(this.metricsStartActions);
|
|
4791
|
-
this.metrics = new
|
|
5419
|
+
this.metrics = new TextRenderable7(renderer2, { id: "omnius-metrics", content: "", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, flexShrink: 1, flexGrow: 1, minWidth: 0 });
|
|
4792
5420
|
this.metricsRow.add(this.metrics);
|
|
4793
|
-
this.metricsEndActions = new
|
|
5421
|
+
this.metricsEndActions = new BoxRenderable8(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
|
|
4794
5422
|
this.metricsRow.add(this.metricsEndActions);
|
|
4795
5423
|
this.root.add(this.metricsRow);
|
|
4796
5424
|
this.modals = new NativeModalStack(this.context, () => this.restoreFocus());
|
|
@@ -4823,6 +5451,7 @@ var NativeFrontend = class {
|
|
|
4823
5451
|
taskPane;
|
|
4824
5452
|
tasks;
|
|
4825
5453
|
body;
|
|
5454
|
+
transcriptPane;
|
|
4826
5455
|
instruments;
|
|
4827
5456
|
instrumentDivider;
|
|
4828
5457
|
detailButton;
|
|
@@ -4874,6 +5503,9 @@ var NativeFrontend = class {
|
|
|
4874
5503
|
destroyed = false;
|
|
4875
5504
|
pendingScrollRestore;
|
|
4876
5505
|
viewScroll = /* @__PURE__ */ new Map();
|
|
5506
|
+
viewDisclosures = /* @__PURE__ */ new Map();
|
|
5507
|
+
pendingDisclosures;
|
|
5508
|
+
pendingInterface;
|
|
4877
5509
|
embeddedId;
|
|
4878
5510
|
selectionArmed = false;
|
|
4879
5511
|
update(state, patch) {
|
|
@@ -4883,7 +5515,9 @@ var NativeFrontend = class {
|
|
|
4883
5515
|
const viewChanged = previousView !== void 0 && previousView !== currentView;
|
|
4884
5516
|
if (viewChanged) {
|
|
4885
5517
|
this.viewScroll.set(previousView, { top: this.scroll.scrollTop, live: this.offset === 0 });
|
|
5518
|
+
this.viewDisclosures.set(previousView, this.content.captureDisclosures(this.tasks.captureDisclosures()));
|
|
4886
5519
|
this.pendingScrollRestore = this.viewScroll.get(currentView) ?? { top: 0, live: true };
|
|
5520
|
+
this.pendingDisclosures = this.viewDisclosures.get(currentView);
|
|
4887
5521
|
this.newEntries = 0;
|
|
4888
5522
|
}
|
|
4889
5523
|
this.state = state;
|
|
@@ -4918,6 +5552,11 @@ var NativeFrontend = class {
|
|
|
4918
5552
|
if (this.offset > 0 && !viewChanged && !patch.reset) this.newEntries += patch.upsert.filter((block) => !this.content.views.has(block.id)).length;
|
|
4919
5553
|
this.content.patch(patch);
|
|
4920
5554
|
}
|
|
5555
|
+
if (this.pendingDisclosures) {
|
|
5556
|
+
this.content.restoreDisclosures(this.pendingDisclosures);
|
|
5557
|
+
this.tasks.restoreDisclosures(this.pendingDisclosures);
|
|
5558
|
+
this.pendingDisclosures = void 0;
|
|
5559
|
+
}
|
|
4921
5560
|
this.queue.content = state.queued.length ? `Queued (${state.queued.length}) · ${state.queued.join(" · ")}` : "";
|
|
4922
5561
|
this.queue.visible = state.queued.length > 0;
|
|
4923
5562
|
const suggestionSignature = JSON.stringify(state.suggestions);
|
|
@@ -4966,7 +5605,7 @@ var NativeFrontend = class {
|
|
|
4966
5605
|
for (const action of actions) {
|
|
4967
5606
|
const colors = this.context.colors;
|
|
4968
5607
|
const attributes = action.disabled ? TextAttributes8.DIM : action.active ? TextAttributes8.BOLD | TextAttributes8.UNDERLINE : 0;
|
|
4969
|
-
const button = new
|
|
5608
|
+
const button = new TextRenderable7(this.renderer, {
|
|
4970
5609
|
id: `${prefix}:${action.id}`,
|
|
4971
5610
|
content: styledText(action.label),
|
|
4972
5611
|
flexShrink: 0,
|
|
@@ -5112,14 +5751,17 @@ var NativeFrontend = class {
|
|
|
5112
5751
|
const shown = !this.embeddedPane.visible && (wide ? this.detailsOpen ?? true : this.narrowDetailsOpen);
|
|
5113
5752
|
const horizontal = wide || this.embeddedPane.visible && width >= 80;
|
|
5114
5753
|
this.body.flexDirection = horizontal ? "row" : "column";
|
|
5115
|
-
this.
|
|
5116
|
-
this.
|
|
5754
|
+
this.transcriptPane.height = horizontal ? "100%" : "auto";
|
|
5755
|
+
this.transcriptPane.flexBasis = horizontal ? "auto" : 0;
|
|
5756
|
+
this.scroll.height = "auto";
|
|
5757
|
+
this.scroll.flexBasis = 0;
|
|
5117
5758
|
this.instruments.visible = shown;
|
|
5118
5759
|
this.instrumentDivider.visible = wide && !this.embeddedPane.visible;
|
|
5119
5760
|
this.instrumentDivider.setCollapsed(!shown);
|
|
5120
5761
|
const railWidth = Math.max(24, Math.min(width - 41, this.sidebarWidth ?? Math.floor(width * 0.38)));
|
|
5121
5762
|
this.instruments.width = wide ? railWidth : "100%";
|
|
5122
|
-
this.
|
|
5763
|
+
this.transcriptPane.width = wide && this.instrumentDivider.visible ? width - (shown ? railWidth : 0) - 1 : this.embeddedPane.visible && width >= 80 ? "50%" : "100%";
|
|
5764
|
+
this.scroll.width = "100%";
|
|
5123
5765
|
const inspectorHeight = Math.max(4, Math.min(9, Math.floor(this.renderer.terminalHeight / 3)));
|
|
5124
5766
|
this.instruments.height = wide ? "100%" : inspectorHeight;
|
|
5125
5767
|
this.instruments.maxHeight = wide ? "100%" : inspectorHeight;
|
|
@@ -5167,6 +5809,13 @@ var NativeFrontend = class {
|
|
|
5167
5809
|
}
|
|
5168
5810
|
onFrame = () => {
|
|
5169
5811
|
if (this.destroyed) return;
|
|
5812
|
+
if (this.pendingInterface) {
|
|
5813
|
+
const state = this.pendingInterface;
|
|
5814
|
+
this.pendingInterface = void 0;
|
|
5815
|
+
this.instruments.restorePresentation(state.instruments);
|
|
5816
|
+
this.sessions.restoreOffset(state.tabs.sessions);
|
|
5817
|
+
this.tabs.restoreOffset(state.tabs.agents);
|
|
5818
|
+
}
|
|
5170
5819
|
if (this.pendingScrollRestore) {
|
|
5171
5820
|
const restore = this.pendingScrollRestore;
|
|
5172
5821
|
this.pendingScrollRestore = void 0;
|
|
@@ -5176,10 +5825,11 @@ var NativeFrontend = class {
|
|
|
5176
5825
|
const height = Math.max(1, Math.min(8, this.editor.editorView.getTotalVirtualLineCount(), this.renderer.terminalHeight - 8));
|
|
5177
5826
|
if (height !== this.editor.height) this.editor.height = height;
|
|
5178
5827
|
this.layoutTabStrips();
|
|
5179
|
-
const
|
|
5180
|
-
|
|
5181
|
-
this.
|
|
5182
|
-
|
|
5828
|
+
for (const media of this.context.mediaViews ?? []) media.reconcile({ x: this.scroll.viewport.x, y: this.scroll.viewport.y, width: this.scroll.viewport.width, height: this.scroll.viewport.height }, !this.paused);
|
|
5829
|
+
const offset2 = this.offset;
|
|
5830
|
+
if (offset2 === 0) this.newEntries = 0;
|
|
5831
|
+
this.liveButton.visible = offset2 > 0;
|
|
5832
|
+
const liveLabel = offset2 > 0 ? `↓ Live${this.newEntries ? ` · ${this.newEntries} new` : ""}` : "↓ Live";
|
|
5183
5833
|
if (this.liveButton.plainText !== liveLabel) this.liveButton.content = liveLabel;
|
|
5184
5834
|
const event = { type: "viewport", offset: this.scroll.scrollTop, height: this.scroll.viewport.height, width: this.scroll.viewport.width, total: this.scroll.scrollHeight };
|
|
5185
5835
|
const signature = JSON.stringify(event);
|
|
@@ -5446,7 +6096,7 @@ var NativeFrontend = class {
|
|
|
5446
6096
|
const target = targets[(current + direction + targets.length) % targets.length];
|
|
5447
6097
|
target.focus();
|
|
5448
6098
|
if (target.id.startsWith("header:")) this.headerActions.scrollChildIntoView(target.id);
|
|
5449
|
-
else if (target.id.startsWith("block-title:") || target.id.startsWith("block-action:")) {
|
|
6099
|
+
else if (target.id.startsWith("block-title:") || target.id.startsWith("block-action:") || target.id.startsWith("media-control:") || target.id.startsWith("media-preview:")) {
|
|
5450
6100
|
let block = target;
|
|
5451
6101
|
while (block && !block.id.startsWith("block:")) block = block.parent;
|
|
5452
6102
|
if (block) this.scroll.scrollChildIntoView(block.id);
|
|
@@ -5618,6 +6268,52 @@ var NativeFrontend = class {
|
|
|
5618
6268
|
this.restoreFocus();
|
|
5619
6269
|
}
|
|
5620
6270
|
}
|
|
6271
|
+
pauseMedia() {
|
|
6272
|
+
for (const media of this.context.mediaViews ?? []) media.pausePlayback();
|
|
6273
|
+
}
|
|
6274
|
+
captureInterfaceState() {
|
|
6275
|
+
this.flushLatestInput();
|
|
6276
|
+
const key = `${this.state?.sessions?.activeId ?? ""}:${this.state?.activeViewId ?? "main"}:${this.state?.presentation ?? "stack"}`;
|
|
6277
|
+
this.viewScroll.set(key, { top: this.scroll.scrollTop, live: this.offset === 0 });
|
|
6278
|
+
this.viewDisclosures.set(key, this.content.captureDisclosures(this.tasks.captureDisclosures()));
|
|
6279
|
+
return {
|
|
6280
|
+
version: 1,
|
|
6281
|
+
sessionId: this.state?.sessions?.activeId,
|
|
6282
|
+
activeViewId: this.state?.activeViewId ?? "main",
|
|
6283
|
+
presentation: this.state?.presentation ?? "stack",
|
|
6284
|
+
input: { text: this.editor.plainText, cursor: this.inputCursor() },
|
|
6285
|
+
views: Object.fromEntries([...this.viewScroll].slice(-256).map(([id, view]) => [id, { ...view, disclosures: this.viewDisclosures.get(id) ?? {} }])),
|
|
6286
|
+
layout: { sidebarWidth: this.sidebarWidth, detailsOpen: this.detailsOpen, narrowDetailsOpen: this.narrowDetailsOpen, headerPanelCollapsed: this.headerPanelCollapsed },
|
|
6287
|
+
instruments: this.instruments.capturePresentation(),
|
|
6288
|
+
tabs: { sessions: this.sessions.scroll.scrollLeft, agents: this.tabs.scroll.scrollLeft }
|
|
6289
|
+
};
|
|
6290
|
+
}
|
|
6291
|
+
restoreInterfaceState(value) {
|
|
6292
|
+
const state = sanitizeNativeInterfaceState(value);
|
|
6293
|
+
if (!state) return;
|
|
6294
|
+
for (const [key2, view2] of Object.entries(state.views)) {
|
|
6295
|
+
this.viewScroll.set(key2, { top: view2.top, live: view2.live });
|
|
6296
|
+
this.viewDisclosures.set(key2, view2.disclosures);
|
|
6297
|
+
}
|
|
6298
|
+
this.sidebarWidth = state.layout.sidebarWidth;
|
|
6299
|
+
this.detailsOpen = state.layout.detailsOpen;
|
|
6300
|
+
this.narrowDetailsOpen = state.layout.narrowDetailsOpen;
|
|
6301
|
+
this.headerPanelCollapsed = state.layout.headerPanelCollapsed;
|
|
6302
|
+
this.headerSignature = "";
|
|
6303
|
+
this.setInput(state.input.text, state.input.cursor);
|
|
6304
|
+
const key = `${this.state?.sessions?.activeId ?? state.sessionId ?? ""}:${this.state?.activeViewId ?? state.activeViewId}:${this.state?.presentation ?? state.presentation}`;
|
|
6305
|
+
const view = this.viewScroll.get(key);
|
|
6306
|
+
if (view) {
|
|
6307
|
+
this.pendingScrollRestore = view;
|
|
6308
|
+
const disclosures = this.viewDisclosures.get(key) ?? {};
|
|
6309
|
+
this.content.restoreDisclosures(disclosures);
|
|
6310
|
+
this.tasks.restoreDisclosures(disclosures);
|
|
6311
|
+
}
|
|
6312
|
+
this.pendingInterface = state;
|
|
6313
|
+
if (this.state) this.update(this.state);
|
|
6314
|
+
this.layoutInstruments();
|
|
6315
|
+
this.renderer.requestRender();
|
|
6316
|
+
}
|
|
5621
6317
|
async dispose() {
|
|
5622
6318
|
if (this.destroyed) return;
|
|
5623
6319
|
this.destroyed = true;
|
|
@@ -5628,9 +6324,14 @@ var NativeFrontend = class {
|
|
|
5628
6324
|
this.renderer.keyInput.off("paste", this.onPaste);
|
|
5629
6325
|
this.renderer.off(CliRenderEvents5.FRAME, this.onFrame);
|
|
5630
6326
|
this.renderer.off(CliRenderEvents5.RESIZE, this.onResize);
|
|
6327
|
+
const mediaClosed = [...this.context.mediaViews ?? []].map((media) => {
|
|
6328
|
+
media.pausePlayback();
|
|
6329
|
+
return media.closed;
|
|
6330
|
+
});
|
|
5631
6331
|
this.modals.destroy();
|
|
5632
6332
|
this.content.destroy();
|
|
5633
6333
|
this.tasks.destroy();
|
|
6334
|
+
await Promise.allSettled(mediaClosed);
|
|
5634
6335
|
this.root.destroyRecursively();
|
|
5635
6336
|
this.context.syntax.destroy();
|
|
5636
6337
|
await this.clipboard.dispose();
|
|
@@ -5650,10 +6351,10 @@ function installTerminalCursorDefaults(renderer2, session) {
|
|
|
5650
6351
|
if (disposed || session.disposed) return Promise.resolve();
|
|
5651
6352
|
requested++;
|
|
5652
6353
|
if (pending) return pending;
|
|
5653
|
-
let
|
|
6354
|
+
let resolve2;
|
|
5654
6355
|
let reject;
|
|
5655
6356
|
const completion = new Promise((done, fail2) => {
|
|
5656
|
-
|
|
6357
|
+
resolve2 = done;
|
|
5657
6358
|
reject = fail2;
|
|
5658
6359
|
});
|
|
5659
6360
|
pending = completion;
|
|
@@ -5666,7 +6367,7 @@ function installTerminalCursorDefaults(renderer2, session) {
|
|
|
5666
6367
|
restored = generation;
|
|
5667
6368
|
}
|
|
5668
6369
|
pending = void 0;
|
|
5669
|
-
|
|
6370
|
+
resolve2();
|
|
5670
6371
|
} catch (error) {
|
|
5671
6372
|
pending = void 0;
|
|
5672
6373
|
reject(error);
|
|
@@ -5732,11 +6433,24 @@ async function command(message) {
|
|
|
5732
6433
|
frontend.command(message.command);
|
|
5733
6434
|
return;
|
|
5734
6435
|
}
|
|
6436
|
+
if (message.type === "interface-capture") {
|
|
6437
|
+
await renderer.idle();
|
|
6438
|
+
reply({ type: "interface-state", id: message.id, state: frontend.captureInterfaceState() });
|
|
6439
|
+
return;
|
|
6440
|
+
}
|
|
6441
|
+
if (message.type === "interface-restore") {
|
|
6442
|
+
frontend.restoreInterfaceState(message.state);
|
|
6443
|
+
renderer.requestRender();
|
|
6444
|
+
await renderer.idle();
|
|
6445
|
+
reply({ type: "ack", id: message.id });
|
|
6446
|
+
return;
|
|
6447
|
+
}
|
|
5735
6448
|
if (message.type === "state") {
|
|
5736
6449
|
frontend.update(message.snapshot, message.content);
|
|
5737
6450
|
renderer.requestRender();
|
|
5738
6451
|
await renderer.idle();
|
|
5739
6452
|
} else if (message.type === "suspend") {
|
|
6453
|
+
frontend.pauseMedia();
|
|
5740
6454
|
await cursorDefaults.restore();
|
|
5741
6455
|
await renderer.suspend();
|
|
5742
6456
|
} else if (message.type === "resume") {
|