omnius 1.0.881 → 1.0.883

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.
@@ -2,17 +2,17 @@
2
2
  import { createWriteStream } from "node:fs";
3
3
  import { createInterface } from "node:readline";
4
4
  import { PassThrough } from "node:stream";
5
- import { CliRenderEvents as CliRenderEvents10, NativeSession, RGBA as RGBA3, createCliRenderer } from "@opentui/core";
5
+ import { CliRenderEvents as CliRenderEvents10, NativeSession, RGBA as RGBA4, createCliRenderer } from "@opentui/core";
6
6
 
7
7
  // packages/cli/src/tui/native/components/frontend.ts
8
8
  import {
9
- BoxRenderable as BoxRenderable11,
9
+ BoxRenderable as BoxRenderable12,
10
10
  CliRenderEvents as CliRenderEvents8,
11
11
  EmbeddedTerminalRenderable as EmbeddedTerminalRenderable2,
12
12
  ScrollBoxRenderable as ScrollBoxRenderable8,
13
13
  SyntaxStyle as SyntaxStyle2,
14
14
  TextareaRenderable as TextareaRenderable2,
15
- TextRenderable as TextRenderable10,
15
+ TextRenderable as TextRenderable11,
16
16
  TextAttributes as TextAttributes11,
17
17
  createClipboard,
18
18
  createHostClipboard,
@@ -42,12 +42,12 @@ function sanitizeNativeInterfaceState(value) {
42
42
  const workQueue = record(instruments.workQueue) ? instruments.workQueue : void 0;
43
43
  const workspaceFiles = record(instruments.workspaceFiles) ? instruments.workspaceFiles : void 0;
44
44
  const workDisclosures = {};
45
- if (workQueue && record(workQueue.disclosures)) for (const [id, open] of Object.entries(workQueue.disclosures).slice(0, 2048)) {
46
- if (id.length <= 1024 && typeof open === "boolean") Object.defineProperty(workDisclosures, id, { value: open, enumerable: true, configurable: true, writable: true });
45
+ if (workQueue && record(workQueue.disclosures)) for (const [id, open2] of Object.entries(workQueue.disclosures).slice(0, 2048)) {
46
+ if (id.length <= 1024 && typeof open2 === "boolean") Object.defineProperty(workDisclosures, id, { value: open2, enumerable: true, configurable: true, writable: true });
47
47
  }
48
48
  const taskDisclosures = {};
49
- if (taskNotes && record(taskNotes.disclosures)) for (const [id, open] of Object.entries(taskNotes.disclosures).slice(0, 2048)) {
50
- if (id.length <= 1024 && typeof open === "boolean") Object.defineProperty(taskDisclosures, id, { value: open, enumerable: true, configurable: true, writable: true });
49
+ if (taskNotes && record(taskNotes.disclosures)) for (const [id, open2] of Object.entries(taskNotes.disclosures).slice(0, 2048)) {
50
+ if (id.length <= 1024 && typeof open2 === "boolean") Object.defineProperty(taskDisclosures, id, { value: open2, enumerable: true, configurable: true, writable: true });
51
51
  }
52
52
  return {
53
53
  version: 1,
@@ -90,14 +90,14 @@ function sanitizeNativeInterfaceState(value) {
90
90
 
91
91
  // packages/cli/src/tui/native/components/blocks.ts
92
92
  import {
93
- BoxRenderable as BoxRenderable2,
93
+ BoxRenderable as BoxRenderable3,
94
94
  CodeRenderable,
95
95
  EmbeddedTerminalRenderable,
96
96
  ImageRenderable as ImageRenderable2,
97
97
  MarkdownRenderable,
98
98
  RenderableEvents,
99
99
  ScrollBoxRenderable,
100
- TextRenderable as TextRenderable2
100
+ TextRenderable as TextRenderable3
101
101
  } from "@opentui/core";
102
102
 
103
103
  // packages/cli/src/tui/native/components/text.ts
@@ -248,18 +248,18 @@ import { spawn, execFile } from "node:child_process";
248
248
  import { realpath, lstat } from "node:fs/promises";
249
249
  import { extname, relative, resolve } from "node:path";
250
250
  import { Audio } from "@opentui/core";
251
- async function validateMediaFile(path, root) {
251
+ async function validateMediaFile(path, root, allowSvg = false) {
252
252
  const info = await lstat(path);
253
253
  if (!info.isFile() || info.isSymbolicLink()) throw new Error("Media must be a regular local file");
254
254
  const canonical = await realpath(path), project = await realpath(root);
255
255
  const local = relative(project, canonical);
256
256
  if (local === ".." || local.startsWith("../") || resolve(project, local) !== canonical) throw new Error("Media is outside its project");
257
- 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");
257
+ if (!/\.(png|jpe?g|webp|gif|mp4|webm|mov|mkv|wav|mp3|flac|ogg|opus|m4a|aiff?)$/i.test(extname(canonical)) && !(allowSvg && extname(canonical).toLowerCase() === ".svg")) throw new Error("Unsupported media file");
258
258
  return canonical;
259
259
  }
260
260
  async function probeMedia(spec, signal) {
261
261
  const path = await validateMediaFile(spec.path, spec.projectRoot);
262
- 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)));
262
+ const output = await new Promise((done, fail2) => execFile("ffprobe", ["-v", "error", "-protocol_whitelist", "file,pipe", "-show_entries", "stream=codec_type,width,height:format=duration", "-of", "json", path], { signal, timeout: 5e3, maxBuffer: 128e3 }, (error, stdout) => error ? fail2(error) : done(stdout)));
263
263
  const data = JSON.parse(output), video = data.streams?.find((stream) => stream.codec_type === "video");
264
264
  const durationSec = Number(data.format?.duration);
265
265
  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") };
@@ -296,7 +296,7 @@ var NativeMediaPlayback = class {
296
296
  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));
297
297
  const info = spec.kind === "video" ? await probeMedia(spec, controller.signal) : {};
298
298
  if (epoch !== this.epoch) return;
299
- const input = ["-nostdin", "-loglevel", "error", "-hwaccel", "none", "-ss", String(this.positionSec), ...spec.kind === "video" ? ["-re"] : [], "-i", path];
299
+ const input = ["-nostdin", "-loglevel", "error", "-hwaccel", "none", "-protocol_whitelist", "file,pipe", "-ss", String(this.positionSec), ...spec.kind === "video" ? ["-re"] : [], "-i", path];
300
300
  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"];
301
301
  const withAudio = spec.kind === "video" && info.hasAudio;
302
302
  if (withAudio) output.push("-map", "0:a:0", "-vn", "-f", "f32le", "-ac", "2", "-ar", "48000", "pipe:3");
@@ -423,11 +423,14 @@ function mediaPreviewHeight(width, imageWidth, imageHeight, cellAspectRatio = 2)
423
423
  function audioWaveform(samples, width) {
424
424
  const cells = Math.max(1, Math.min(240, Math.floor(width)));
425
425
  const glyphs = "▁▂▃▄▅▆▇█";
426
+ let maximum = 0;
427
+ for (const sample of samples) if (Number.isFinite(sample)) maximum = Math.max(maximum, Math.abs(sample));
428
+ const scale = maximum > 1e-5 ? maximum : 0;
426
429
  return Array.from({ length: cells }, (_, column3) => {
427
430
  const begin = Math.floor(column3 * samples.length / cells), end = Math.max(begin + 1, Math.floor((column3 + 1) * samples.length / cells));
428
431
  let peak = 0;
429
432
  for (let index = begin; index < end && index < samples.length; index++) if (Number.isFinite(samples[index])) peak = Math.max(peak, Math.abs(samples[index]));
430
- return glyphs[Math.min(7, Math.floor(Math.min(1, peak) * 7))];
433
+ return glyphs[scale ? Math.min(7, Math.floor(Math.min(1, peak / scale) * 7)) : 0];
431
434
  }).join("");
432
435
  }
433
436
  var time = (seconds) => `${Math.floor(Math.max(0, seconds) / 60)}:${String(Math.floor(Math.max(0, seconds) % 60)).padStart(2, "0")}`;
@@ -439,7 +442,7 @@ var NativeMediaView = class extends BoxRenderable {
439
442
  this.colors = colors;
440
443
  this.sendEvent = sendEvent;
441
444
  this.spec = spec;
442
- this.location = new TextRenderable(renderer2, { id: `media-location:${mediaId}`, content: spec.path, width: "100%", wrapMode: "word", selectable: true, fg: colors.dim });
445
+ this.location = new TextRenderable(renderer2, { id: `media-location:${mediaId}`, content: spec.displayName ?? spec.path, width: "100%", wrapMode: "word", selectable: true, fg: colors.dim });
443
446
  this.status = new TextRenderable(renderer2, { id: `media-status:${mediaId}`, content: "", width: "100%", wrapMode: "word", selectable: true, fg: colors.dim });
444
447
  this.wave = new TextRenderable(renderer2, { id: `media-waveform:${mediaId}`, content: "", width: "100%", height: 1, wrapMode: "none", fg: colors.foreground, visible: false });
445
448
  this.playheadLabel = new TextRenderable(renderer2, { id: `media-position:${mediaId}`, content: "", width: "100%", height: 1, wrapMode: "none", fg: colors.dim, visible: false });
@@ -524,7 +527,7 @@ var NativeMediaView = class extends BoxRenderable {
524
527
  update(spec) {
525
528
  const changed = spec.path !== this.spec.path || spec.kind !== this.spec.kind;
526
529
  this.spec = spec;
527
- this.location.content = spec.path;
530
+ this.location.content = spec.displayName ?? spec.path;
528
531
  this.playControl.visible = spec.kind !== "image";
529
532
  if (changed) {
530
533
  this.playback.pause();
@@ -582,7 +585,7 @@ var NativeMediaView = class extends BoxRenderable {
582
585
  }
583
586
  if (this.image) {
584
587
  this.image.visible = spec.kind === "image" || spec.kind === "video";
585
- const source = spec.kind === "image" ? spec.path : spec.previewPath;
588
+ const source = spec.kind === "image" ? spec.previewPath ?? spec.path : spec.previewPath;
586
589
  if (source && this.image.source !== source && !this.playback.playing) {
587
590
  const epoch = this.sourceEpoch;
588
591
  void validateMediaFile(source, spec.projectRoot).then((path) => {
@@ -621,7 +624,7 @@ var NativeMediaView = class extends BoxRenderable {
621
624
  this.refreshStatus();
622
625
  }
623
626
  loadWaveform(controller, epoch) {
624
- 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) => {
627
+ execFile2("ffmpeg", ["-nostdin", "-loglevel", "error", "-hwaccel", "none", "-protocol_whitelist", "file,pipe", "-i", this.spec.path, "-t", "600", "-vn", "-af", "aformat=channel_layouts=mono,aeval=abs(val(0))", "-ac", "1", "-ar", "1000", "-f", "f32le", "pipe:1"], { encoding: "buffer", signal: controller.signal, timeout: 1e4, maxBuffer: 25e5 }, (error, buffer) => {
625
628
  if (controller.signal.aborted || epoch !== this.sourceEpoch || this.isDestroyed) return;
626
629
  if (error) {
627
630
  this.currentStatus = "Waveform unavailable · playback and Open remain available";
@@ -700,7 +703,7 @@ var NativeMediaView = class extends BoxRenderable {
700
703
  }
701
704
  async open() {
702
705
  try {
703
- const path = await validateMediaFile(this.spec.path, this.spec.projectRoot);
706
+ const path = await validateMediaFile(this.spec.path, this.spec.projectRoot, this.spec.kind === "image" && !!this.spec.previewPath);
704
707
  if (this.isDestroyed) return;
705
708
  this.sendEvent({ type: "action", action: "media-open", id: this.mediaId, text: path });
706
709
  if (process.env["OMNIUS_MEDIA_TEST_NO_OPEN"] === "1") {
@@ -738,6 +741,649 @@ var NativeMediaView = class extends BoxRenderable {
738
741
  }
739
742
  };
740
743
 
744
+ // packages/cli/src/tui/native/components/model3d.ts
745
+ import { constants } from "node:fs";
746
+ import { open } from "node:fs/promises";
747
+ import { BoxRenderable as BoxRenderable2, FrameBufferRenderable, OptimizedBuffer, RGBA as RGBA2, TextRenderable as TextRenderable2 } from "@opentui/core";
748
+
749
+ // packages/cli/src/tui/native/model3d.ts
750
+ var MODEL3D_MAX_BYTES = 16 * 1024 * 1024;
751
+ var MODEL3D_MAX_VERTICES = 75e4;
752
+ var MODEL3D_MAX_TRIANGLES = 25e4;
753
+ var MODEL3D_MAX_WIREFRAME_EDGES = 6e3;
754
+ var initialModel3DCamera = () => ({ yaw: 0.65, pitch: 0.35, distance: 3.5 });
755
+ var invalid = (message) => {
756
+ throw new Error(message);
757
+ };
758
+ function preflight(data, format) {
759
+ if (!data.byteLength || data.byteLength > MODEL3D_MAX_BYTES) invalid("3D preview supports nonempty files up to 16 MiB.");
760
+ if (format === "stl") {
761
+ if (data.byteLength < 84) invalid("STL data is incomplete.");
762
+ const faces = new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(80, true);
763
+ const binary2 = 84 + faces * 50 === data.byteLength;
764
+ if (binary2) {
765
+ if (!faces || faces > MODEL3D_MAX_TRIANGLES) invalid("STL exceeds the 250,000 triangle preview limit.");
766
+ return;
767
+ }
768
+ const text2 = new TextDecoder().decode(data);
769
+ if (!/^\uFEFF?\s*solid\b/i.test(text2) || !/endsolid\b/i.test(text2)) invalid("STL data is incomplete or unsupported.");
770
+ const count = text2.match(/\bfacet\s+normal\b/gi)?.length ?? 0;
771
+ if (!count || count > MODEL3D_MAX_TRIANGLES) invalid("STL exceeds the 250,000 triangle preview limit or has no faces.");
772
+ let asciiFaces = 0;
773
+ for (const match of text2.matchAll(/\bfacet\b([\s\S]*?)\bendfacet\b/gi)) {
774
+ const normals = [...match[1].matchAll(/\bnormal\s+([^\r\n]+)/gi)], vertices2 = [...match[1].matchAll(/\bvertex\s+([^\r\n]+)/gi)];
775
+ if (normals.length !== 1 || vertices2.length !== 3 || [...normals, ...vertices2].some((row) => {
776
+ const values = row[1].trim().split(/\s+/).map(Number);
777
+ return values.length !== 3 || values.some((value) => !Number.isFinite(value));
778
+ })) invalid("STL face data is incomplete or invalid.");
779
+ asciiFaces++;
780
+ }
781
+ if (asciiFaces !== count) invalid("STL face data is incomplete.");
782
+ return;
783
+ }
784
+ const text = new TextDecoder().decode(data);
785
+ if (format === "obj") {
786
+ let vertices2 = 0, triangles2 = 0;
787
+ for (const line of text.split(/\r?\n/)) {
788
+ if (line.length > 65536) invalid("OBJ line exceeds the preview limit.");
789
+ const record2 = line.trim().split(/\s+/, 1)[0];
790
+ if (record2 && !record2.startsWith("#") && !["v", "vn", "vt", "f", "l", "p", "o", "g", "s", "mtllib", "usemtl"].includes(record2)) invalid("OBJ contains an unsupported record; preview supports geometry and material names only.");
791
+ if (/^\s*v\s/.test(line)) {
792
+ const values = line.trim().split(/\s+/).slice(1, 4).map(Number);
793
+ if (values.length !== 3 || values.some((value) => !Number.isFinite(value) || Math.abs(value) > 1e30)) invalid("OBJ contains invalid coordinates.");
794
+ vertices2++;
795
+ }
796
+ if (/^\s*(f|l|p)\s/.test(line)) {
797
+ const values = line.split("#", 1)[0].trim().split(/\s+/);
798
+ for (const token of values.slice(1)) {
799
+ const index = Number(token.split("/")[0]);
800
+ if (!Number.isSafeInteger(index) || index === 0 || index > vertices2 || index < -vertices2) invalid("OBJ contains an invalid vertex index.");
801
+ }
802
+ if (values[0] === "f") triangles2 += Math.max(0, values.length - 3);
803
+ }
804
+ if (vertices2 > MODEL3D_MAX_VERTICES || triangles2 > MODEL3D_MAX_TRIANGLES) invalid("OBJ exceeds the bounded geometry preview limit.");
805
+ }
806
+ if (!vertices2) invalid("OBJ contains no vertices.");
807
+ return;
808
+ }
809
+ const end = text.indexOf("end_header");
810
+ if (!text.startsWith("ply") || end < 0 || end > 65536) invalid("PLY header is missing or too large.");
811
+ const header = text.slice(0, end);
812
+ if (!/^format (ascii|binary_little_endian|binary_big_endian) 1\.0\s*$/m.test(header)) invalid("PLY format is unsupported.");
813
+ let vertices = 0;
814
+ const elements = [];
815
+ for (const line of header.split(/\r?\n/)) {
816
+ const element = /^element\s+(\S+)\s+(\d+)\s*$/.exec(line);
817
+ if (!element) {
818
+ const property = /^property\s+(\S+)\s+(\S+)(?:\s+(\S+)\s+(\S+))?\s*$/.exec(line);
819
+ if (property) {
820
+ if (!elements.length) invalid("PLY property has no element.");
821
+ elements.at(-1).properties.push(property[1] === "list" ? { type: property[2], item: property[3] } : { type: property[1] });
822
+ }
823
+ continue;
824
+ }
825
+ const count = Number(element[2]);
826
+ if (!Number.isSafeInteger(count) || count > MODEL3D_MAX_VERTICES) invalid("PLY exceeds the bounded geometry preview limit.");
827
+ if (element[1] === "vertex") vertices = count;
828
+ if (element[1] === "face" && count > MODEL3D_MAX_TRIANGLES) invalid("PLY exceeds the 250,000 face preview limit.");
829
+ elements.push({ name: element[1], count, properties: [] });
830
+ }
831
+ if (!vertices) invalid("PLY contains no vertices.");
832
+ if (elements.reduce((count, element) => count + element.count, 0) > 1e6 || elements.some((element) => !element.properties.length || element.properties.length > 32)) invalid("PLY record layout exceeds the bounded preview limit.");
833
+ const sizes = { char: 1, uchar: 1, int8: 1, uint8: 1, short: 2, ushort: 2, int16: 2, uint16: 2, int: 4, uint: 4, int32: 4, uint32: 4, float: 4, float32: 4, double: 8, float64: 8 };
834
+ const binary = !/^format ascii /m.test(header), little = /^format binary_little_endian /m.test(header);
835
+ const headerBytes = new TextEncoder().encode(text.slice(0, end + "end_header".length)).byteLength;
836
+ let offset2 = headerBytes;
837
+ if (data[offset2] === 13) offset2++;
838
+ if (data[offset2] === 10) offset2++;
839
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
840
+ const ascii = binary ? "" : new TextDecoder().decode(data.subarray(offset2)), next = /\S+/g;
841
+ let triangles = 0;
842
+ const scalar = (type) => {
843
+ const size = sizes[type];
844
+ if (!size) invalid("PLY property type is unsupported.");
845
+ if (!binary) {
846
+ const token = next.exec(ascii);
847
+ const value2 = token ? Number(token[0]) : NaN;
848
+ if (!Number.isFinite(value2)) invalid("PLY data is incomplete or invalid.");
849
+ return value2;
850
+ }
851
+ if (offset2 + size > data.length) invalid("PLY data is incomplete.");
852
+ let value;
853
+ if (/^(uchar|uint8)$/.test(type)) value = view.getUint8(offset2);
854
+ else if (/^(char|int8)$/.test(type)) value = view.getInt8(offset2);
855
+ else if (/^(ushort|uint16)$/.test(type)) value = view.getUint16(offset2, little);
856
+ else if (/^(short|int16)$/.test(type)) value = view.getInt16(offset2, little);
857
+ else if (/^(uint|uint32)$/.test(type)) value = view.getUint32(offset2, little);
858
+ else if (/^(int|int32)$/.test(type)) value = view.getInt32(offset2, little);
859
+ else value = size === 4 ? view.getFloat32(offset2, little) : view.getFloat64(offset2, little);
860
+ offset2 += size;
861
+ if (!Number.isFinite(value)) invalid("PLY data contains invalid coordinates.");
862
+ return value;
863
+ };
864
+ for (const element of elements) for (let row = 0; row < element.count; row++) for (const property of element.properties) {
865
+ if (!property.item) {
866
+ scalar(property.type);
867
+ continue;
868
+ }
869
+ const count = scalar(property.type);
870
+ if (!Number.isInteger(count) || count < 0 || count > 4096) invalid("PLY list exceeds the bounded preview limit.");
871
+ if (element.name === "face") {
872
+ triangles += Math.max(0, count - 2);
873
+ if (triangles > MODEL3D_MAX_TRIANGLES) invalid("PLY exceeds the triangle preview limit.");
874
+ }
875
+ for (let item = 0; item < count; item++) {
876
+ const value = scalar(property.item);
877
+ if (element.name === "face" && (!Number.isInteger(value) || value < 0 || value >= vertices)) invalid("PLY contains an invalid vertex index.");
878
+ }
879
+ }
880
+ }
881
+ async function parseModel3D(data, format) {
882
+ if (!["stl", "obj", "ply"].includes(format)) invalid("Supported 3D preview formats: STL, OBJ and PLY.");
883
+ preflight(data, format);
884
+ const THREE = await import("three");
885
+ const bytes = Uint8Array.from(data).buffer;
886
+ let object;
887
+ if (format === "obj") {
888
+ const { OBJLoader } = await import("three/addons/loaders/OBJLoader.js");
889
+ object = new OBJLoader().parse(new TextDecoder().decode(data));
890
+ } else {
891
+ const geometry = format === "stl" ? new (await import("three/addons/loaders/STLLoader.js")).STLLoader().parse(bytes) : new (await import("three/addons/loaders/PLYLoader.js")).PLYLoader().parse(bytes);
892
+ object = format === "ply" && !geometry.index ? new THREE.Points(geometry) : new THREE.Mesh(geometry);
893
+ }
894
+ const positions = [], triangles = [], segments = [];
895
+ const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
896
+ try {
897
+ object.updateMatrixWorld(true);
898
+ object.traverse((node) => {
899
+ const drawable = node;
900
+ if (!drawable.geometry) return;
901
+ const geometry = drawable.geometry, attribute = geometry.getAttribute("position");
902
+ if (!attribute || !attribute.count) return;
903
+ const base = positions.length / 3;
904
+ if (base + attribute.count > MODEL3D_MAX_VERTICES) invalid("Decoded geometry exceeds the 750,000 vertex preview limit.");
905
+ const point = new THREE.Vector3();
906
+ for (let i = 0; i < attribute.count; i++) {
907
+ point.set(attribute.getX(i), attribute.getY(i), attribute.getZ(i)).applyMatrix4(node.matrixWorld);
908
+ const values = [point.x, point.y, point.z];
909
+ if (values.some((value) => !Number.isFinite(value) || Math.abs(value) > 1e30)) invalid("3D geometry contains invalid coordinates.");
910
+ for (let axis = 0; axis < 3; axis++) {
911
+ min[axis] = Math.min(min[axis], values[axis]);
912
+ max[axis] = Math.max(max[axis], values[axis]);
913
+ }
914
+ positions.push(...values);
915
+ }
916
+ const count = geometry.index?.count ?? attribute.count;
917
+ const at = (index) => {
918
+ const value = geometry.index ? geometry.index.getX(index) : index;
919
+ if (!Number.isInteger(value) || value < 0 || value >= attribute.count) invalid("3D geometry contains an invalid vertex index.");
920
+ return base + value;
921
+ };
922
+ if (drawable.isMesh) {
923
+ if (count % 3 !== 0 || triangles.length / 3 + count / 3 > MODEL3D_MAX_TRIANGLES) invalid("Decoded geometry exceeds the triangle preview limit or is incomplete.");
924
+ for (let i = 0; i < count; i++) triangles.push(at(i));
925
+ } else if (drawable.isLineSegments) {
926
+ for (let i = 0; i + 1 < count; i += 2) segments.push(at(i), at(i + 1));
927
+ }
928
+ });
929
+ } finally {
930
+ object.traverse((node) => {
931
+ const drawable = node;
932
+ drawable.geometry?.dispose();
933
+ for (const material of Array.isArray(drawable.material) ? drawable.material : drawable.material ? [drawable.material] : []) material.dispose();
934
+ });
935
+ }
936
+ if (!positions.length) invalid("No renderable geometry was found.");
937
+ const center = min.map((value, axis) => (value + max[axis]) / 2);
938
+ const radius = Math.hypot(...min.map((value, axis) => max[axis] - value)) / 2 || 1;
939
+ for (let i = 0; i < positions.length; i++) positions[i] = (positions[i] - center[i % 3]) / radius;
940
+ return {
941
+ format,
942
+ positions: Float32Array.from(positions),
943
+ triangles: Uint32Array.from(triangles),
944
+ segments: Uint32Array.from(segments),
945
+ bounds: { min, max },
946
+ vertexCount: positions.length / 3,
947
+ triangleCount: triangles.length / 3,
948
+ warnings: format === "obj" ? ["Geometry preview only; external materials and textures are not loaded."] : []
949
+ };
950
+ }
951
+ function orbitModel3D(camera, dx, dy) {
952
+ return { ...camera, yaw: camera.yaw + dx * 0.06, pitch: Math.max(-1.5, Math.min(1.5, camera.pitch + dy * 0.06)) };
953
+ }
954
+ function zoomModel3D(camera, direction) {
955
+ return { ...camera, distance: Math.max(1.15, Math.min(30, camera.distance * Math.exp(direction * 0.16))) };
956
+ }
957
+ function fitModel3D(camera, aspect) {
958
+ const halfFov = Math.atan(Math.tan(Math.PI / 8) * Math.min(1, Math.max(0.05, aspect)));
959
+ return { ...camera, distance: 1.1 / Math.sin(halfFov) };
960
+ }
961
+ function model3DCameraPosition(camera) {
962
+ return [Math.sin(camera.yaw) * Math.cos(camera.pitch) * camera.distance, Math.sin(camera.pitch) * camera.distance, Math.cos(camera.yaw) * Math.cos(camera.pitch) * camera.distance];
963
+ }
964
+ function model3DWireEdges(model) {
965
+ const edges = Array.from(model.segments), seen = /* @__PURE__ */ new Set();
966
+ for (let i = 0; i < model.triangles.length; i += 3) for (const [a, b] of [[model.triangles[i], model.triangles[i + 1]], [model.triangles[i + 1], model.triangles[i + 2]], [model.triangles[i + 2], model.triangles[i]]]) {
967
+ const key = a < b ? `${a}:${b}` : `${b}:${a}`;
968
+ if (!seen.has(key)) {
969
+ seen.add(key);
970
+ edges.push(a, b);
971
+ }
972
+ }
973
+ return Uint32Array.from(edges);
974
+ }
975
+ function projectModel3DWireframe(model, camera, width, height, edges = model3DWireEdges(model)) {
976
+ width = Math.max(1, Math.min(240, Math.floor(width)));
977
+ height = Math.max(1, Math.min(80, Math.floor(height)));
978
+ const cells = Array.from({ length: height }, () => Array(width).fill(" "));
979
+ const sy = Math.sin(camera.yaw), cy = Math.cos(camera.yaw), sp = Math.sin(camera.pitch), cp = Math.cos(camera.pitch);
980
+ const aspect = width / (height * 2), focal = 1 / Math.tan(Math.PI / 8);
981
+ const project = (index) => {
982
+ const x = model.positions[index * 3], y = model.positions[index * 3 + 1], z = model.positions[index * 3 + 2];
983
+ const rx = cy * x - sy * z, rz = sy * x + cy * z, ry = cp * y - sp * rz, depth = camera.distance - (sp * y + cp * rz);
984
+ if (depth <= 0.05) return;
985
+ return [(0.5 + rx / depth * focal / aspect / 2) * (width - 1), (0.5 - ry / depth * focal / 2) * (height - 1)];
986
+ };
987
+ const plot = (x, y) => {
988
+ if (x >= 0 && x < width && y >= 0 && y < height) cells[y][x] = "·";
989
+ };
990
+ const totalEdges = edges.length / 2, stride = Math.max(1, Math.ceil(totalEdges / MODEL3D_MAX_WIREFRAME_EDGES));
991
+ let displayedEdges = 0;
992
+ for (let i = 0; i < totalEdges; i += stride) {
993
+ const a = project(edges[i * 2]), b = project(edges[i * 2 + 1]);
994
+ if (!a || !b) continue;
995
+ const steps = Math.min(width + height, Math.max(Math.abs(a[0] - b[0]), Math.abs(a[1] - b[1]), 1));
996
+ for (let step = 0; step <= steps; step++) plot(Math.round(a[0] + (b[0] - a[0]) * step / steps), Math.round(a[1] + (b[1] - a[1]) * step / steps));
997
+ displayedEdges++;
998
+ }
999
+ if (!totalEdges) for (let i = 0; i < model.vertexCount; i += Math.max(1, Math.ceil(model.vertexCount / MODEL3D_MAX_WIREFRAME_EDGES))) {
1000
+ const p = project(i);
1001
+ if (p) plot(Math.round(p[0]), Math.round(p[1]));
1002
+ }
1003
+ return { text: cells.map((row) => row.join("")).join("\n"), displayedEdges, totalEdges };
1004
+ }
1005
+
1006
+ // packages/cli/src/tui/native/components/model3d.ts
1007
+ async function createEngine(renderer2, width, height, background2) {
1008
+ if (!process.versions["bun"] || !(process.platform === "linux" && process.arch === "x64" || process.platform === "darwin" || process.platform === "win32" && process.arch === "x64")) throw new Error("WebGPU is not packaged for this runtime.");
1009
+ const packageName = "@opentui/three";
1010
+ const { ThreeCliRenderer, SuperSampleType } = await import(packageName);
1011
+ return new ThreeCliRenderer(renderer2, { width, height, autoResize: false, superSample: SuperSampleType.CPU, alpha: true, backgroundColor: RGBA2.fromValues(background2.r, background2.g, background2.b, 0) });
1012
+ }
1013
+ async function readCapture(path) {
1014
+ const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
1015
+ try {
1016
+ const before = await file.stat();
1017
+ if (!before.isFile() || before.size <= 0 || before.size > MODEL3D_MAX_BYTES) throw new Error("3D preview requires a regular file up to 16 MiB.");
1018
+ const bytes = Buffer.alloc(before.size);
1019
+ let length = 0;
1020
+ while (length < bytes.length) {
1021
+ const read = await file.read(bytes, length, bytes.length - length, null);
1022
+ if (!read.bytesRead) break;
1023
+ length += read.bytesRead;
1024
+ }
1025
+ const extra = await file.read(Buffer.alloc(1), 0, 1, null), after = await file.stat();
1026
+ if (length !== bytes.length || extra.bytesRead) throw new Error("The captured model changed while being read.");
1027
+ if (bytes.length !== before.size || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new Error("The captured model changed while being read.");
1028
+ return bytes;
1029
+ } finally {
1030
+ await file.close();
1031
+ }
1032
+ }
1033
+ var NativeModel3DView = class extends BoxRenderable2 {
1034
+ constructor(renderer2, modelId, spec, colors, options = {}) {
1035
+ super(renderer2, { id: `model3d:${modelId}`, width: "100%", flexDirection: "column", flexShrink: 0, shouldFill: false });
1036
+ this.renderer = renderer2;
1037
+ this.modelId = modelId;
1038
+ this.colors = colors;
1039
+ this.options = options;
1040
+ this.spec = spec;
1041
+ let resolveClosed;
1042
+ this.closed = new Promise((resolve2) => {
1043
+ resolveClosed = resolve2;
1044
+ });
1045
+ this.resolveClosed = resolveClosed;
1046
+ this.location = new TextRenderable2(renderer2, { id: `model3d-location:${modelId}`, width: "100%", wrapMode: "word", fg: colors.dim, content: plainText(spec.displayName ?? spec.path), selectable: true });
1047
+ this.status = new TextRenderable2(renderer2, { id: `model3d-status:${modelId}`, width: "100%", wrapMode: "word", fg: colors.dim, content: "Loading 3D geometry…", selectable: true });
1048
+ const pointer = {
1049
+ onMouseDown: (event) => {
1050
+ if (event.button === 0) {
1051
+ event.currentTarget?.focus();
1052
+ this.drag = { x: event.x, y: event.y };
1053
+ event.stopPropagation();
1054
+ }
1055
+ },
1056
+ onMouseDrag: (event) => {
1057
+ if (this.drag) {
1058
+ this.cameraState = orbitModel3D(this.cameraState, event.x - this.drag.x, event.y - this.drag.y);
1059
+ this.drag = { x: event.x, y: event.y };
1060
+ this.changed();
1061
+ event.stopPropagation();
1062
+ }
1063
+ },
1064
+ onMouseDragEnd: () => {
1065
+ this.drag = void 0;
1066
+ },
1067
+ onMouseUp: () => {
1068
+ this.drag = void 0;
1069
+ },
1070
+ onMouseScroll: (event) => {
1071
+ if (event.scroll?.direction === "up" || event.scroll?.direction === "down") {
1072
+ this.zoom(event.scroll.direction === "up" ? -1 : 1);
1073
+ event.preventDefault();
1074
+ event.stopPropagation();
1075
+ }
1076
+ },
1077
+ onKeyDown: (key) => {
1078
+ this.handleKey(key);
1079
+ }
1080
+ };
1081
+ this.cpu = new TextRenderable2(renderer2, { id: `model3d-cpu:${modelId}`, width: "100%", height: 12, flexShrink: 0, wrapMode: "none", content: "", fg: colors.accent, selectable: false, ...pointer });
1082
+ this.cpu.focusable = true;
1083
+ this.surface = new FrameBufferRenderable(renderer2, {
1084
+ id: `model3d-surface:${modelId}`,
1085
+ width: 1,
1086
+ height: 12,
1087
+ flexShrink: 0,
1088
+ visible: false,
1089
+ respectAlpha: true,
1090
+ onSizeChange: () => {
1091
+ this.dirty = true;
1092
+ if (!this.closing) this.renderer.requestRender();
1093
+ },
1094
+ ...pointer
1095
+ });
1096
+ this.surface.focusable = true;
1097
+ this.controls = new BoxRenderable2(renderer2, { id: `model3d-controls:${modelId}`, width: "100%", flexDirection: "row", flexWrap: "wrap", gap: 2, flexShrink: 0 });
1098
+ for (const [id, label3, action] of [
1099
+ ["fit", "Fit", () => this.fit()],
1100
+ ["reset", "Reset", () => this.reset()],
1101
+ ["in", "Zoom +", () => this.zoom(-1)],
1102
+ ["out", "Zoom −", () => this.zoom(1)]
1103
+ ]) {
1104
+ const control = new TextRenderable2(renderer2, {
1105
+ id: `model3d-control:${modelId}:${id}`,
1106
+ content: label3,
1107
+ fg: colors.foreground,
1108
+ selectable: false,
1109
+ onMouseDown: (event) => {
1110
+ if (event.button === 0) {
1111
+ action();
1112
+ event.stopPropagation();
1113
+ }
1114
+ },
1115
+ onKeyDown: (key) => {
1116
+ if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
1117
+ action();
1118
+ key.preventDefault();
1119
+ key.stopPropagation();
1120
+ }
1121
+ }
1122
+ });
1123
+ control.focusable = true;
1124
+ this.controls.add(control);
1125
+ }
1126
+ this.add(this.location);
1127
+ this.add(this.cpu);
1128
+ this.add(this.surface);
1129
+ this.add(this.status);
1130
+ this.add(this.controls);
1131
+ this.hints = new TextRenderable2(renderer2, { id: `model3d-hints:${modelId}`, content: "Drag / arrows: orbit · wheel / + −: zoom · F: fit · R: reset", width: "100%", wrapMode: "word", fg: colors.dim });
1132
+ this.add(this.hints);
1133
+ renderer2.setFrameCallback(this.frame);
1134
+ }
1135
+ renderer;
1136
+ modelId;
1137
+ colors;
1138
+ options;
1139
+ spec;
1140
+ cameraState = initialModel3DCamera();
1141
+ location;
1142
+ status;
1143
+ cpu;
1144
+ surface;
1145
+ controls;
1146
+ hints;
1147
+ model;
1148
+ edges;
1149
+ scene;
1150
+ camera;
1151
+ engine;
1152
+ loading;
1153
+ drawing;
1154
+ cleanup;
1155
+ /** Bounded local diagnostic; a failed preview cleanup must not kill its TUI. */
1156
+ cleanupDiagnostic;
1157
+ resolveClosed;
1158
+ closed;
1159
+ closing = false;
1160
+ active = false;
1161
+ dirty = true;
1162
+ epoch = 0;
1163
+ pixelWidth = 0;
1164
+ pixelHeight = 0;
1165
+ drag;
1166
+ mode = "loading";
1167
+ frame = async () => {
1168
+ this.draw();
1169
+ };
1170
+ update(spec) {
1171
+ if (spec.path !== this.spec.path || spec.format !== this.spec.format) return;
1172
+ this.spec = spec;
1173
+ this.location.content = plainText(spec.displayName ?? spec.path);
1174
+ }
1175
+ applyTheme(colors) {
1176
+ this.colors = colors;
1177
+ this.location.fg = colors.dim;
1178
+ this.status.fg = colors.dim;
1179
+ this.cpu.fg = colors.accent;
1180
+ for (const child of this.controls.getChildren()) if (child instanceof TextRenderable2) child.fg = colors.foreground;
1181
+ this.changed();
1182
+ }
1183
+ async load() {
1184
+ const epoch = this.epoch;
1185
+ try {
1186
+ const bytes = await readCapture(this.spec.path);
1187
+ if (this.closing || epoch !== this.epoch) return;
1188
+ const model = await parseModel3D(bytes, this.spec.format);
1189
+ if (this.closing || epoch !== this.epoch) return;
1190
+ this.model = model;
1191
+ this.edges = model3DWireEdges(model);
1192
+ this.mode = "cpu";
1193
+ this.fit();
1194
+ if ((this.options.mode ?? process.env["OMNIUS_TUI_3D_RENDERER"]) !== "cpu") {
1195
+ try {
1196
+ const THREE = await import("three");
1197
+ if (this.closing) return;
1198
+ this.scene = new THREE.Scene();
1199
+ this.camera = new THREE.PerspectiveCamera(45, 1, 0.01, 100);
1200
+ this.scene.add(new THREE.HemisphereLight(16777215, 2631720, 2));
1201
+ const light = new THREE.DirectionalLight(16777215, 2);
1202
+ light.position.set(3, 4, 5);
1203
+ this.scene.add(light);
1204
+ const geometry = new THREE.BufferGeometry();
1205
+ geometry.setAttribute("position", new THREE.BufferAttribute(model.positions, 3));
1206
+ if (model.triangleCount) {
1207
+ geometry.setIndex(new THREE.BufferAttribute(model.triangles, 1));
1208
+ geometry.computeVertexNormals();
1209
+ this.scene.add(new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 12369084, roughness: 0.75, metalness: 0.05, side: THREE.DoubleSide })));
1210
+ } else if (model.segments.length) {
1211
+ geometry.setIndex(new THREE.BufferAttribute(model.segments, 1));
1212
+ this.scene.add(new THREE.LineSegments(geometry, new THREE.LineBasicMaterial({ color: 12369084 })));
1213
+ } else this.scene.add(new THREE.Points(geometry, new THREE.PointsMaterial({ color: 12369084, size: 0.025 })));
1214
+ this.engine = this.options.createEngine ? await this.options.createEngine(this.renderer, Math.max(1, this.pixelWidth), Math.max(1, this.pixelHeight)) : await createEngine(this.renderer, Math.max(1, this.pixelWidth), Math.max(1, this.pixelHeight), this.colors.background);
1215
+ await this.engine.init();
1216
+ if (this.closing) return;
1217
+ this.engine.setActiveCamera(this.camera);
1218
+ this.mode = "webgpu";
1219
+ this.surface.visible = true;
1220
+ this.cpu.visible = false;
1221
+ } catch {
1222
+ this.releaseScene();
1223
+ this.engine?.destroy();
1224
+ this.engine = void 0;
1225
+ this.mode = "cpu";
1226
+ }
1227
+ } else this.mode = "cpu";
1228
+ if (!this.closing) this.changed();
1229
+ } catch (error) {
1230
+ if (!this.closing) {
1231
+ this.mode = "error";
1232
+ this.status.content = `3D preview unavailable · ${plainText(error instanceof Error ? error.message : "Unable to read model").slice(0, 200)}`;
1233
+ this.renderer.requestRender();
1234
+ }
1235
+ }
1236
+ }
1237
+ reconcile(viewport, active = true) {
1238
+ if (this.closing || this.isDestroyed) return;
1239
+ let ancestor = this;
1240
+ while (ancestor && active) {
1241
+ active = ancestor.visible;
1242
+ ancestor = ancestor.parent;
1243
+ }
1244
+ this.active = active && this.y + this.height > viewport.y && this.y < viewport.y + viewport.height;
1245
+ if (!this.active) return;
1246
+ const chrome = [this.location, this.status, this.controls, this.hints].reduce((rows, node) => rows + (node.visible ? Math.max(1, node.height) : 0), 0);
1247
+ const width = Math.max(1, Math.min(240, Math.floor(this.width))), height = Math.max(3, Math.min(30, this.renderer.terminalHeight - 7 - chrome));
1248
+ if (width !== this.pixelWidth || height !== this.pixelHeight) {
1249
+ this.pixelWidth = width;
1250
+ this.pixelHeight = height;
1251
+ this.cpu.height = height;
1252
+ this.surface.width = width;
1253
+ this.surface.height = height;
1254
+ this.changed();
1255
+ }
1256
+ if (!this.loading && !this.model && this.mode !== "error") this.loading = this.load();
1257
+ if (this.dirty) this.draw();
1258
+ }
1259
+ changed() {
1260
+ this.dirty = true;
1261
+ if (!this.closing) {
1262
+ this.draw();
1263
+ this.renderer.requestRender();
1264
+ }
1265
+ }
1266
+ draw() {
1267
+ if (!this.active || !this.dirty || !this.model || this.closing || this.drawing) return;
1268
+ this.dirty = false;
1269
+ const model = this.model, width = Math.max(1, this.pixelWidth), height = Math.max(1, this.pixelHeight);
1270
+ const stats = `${model.vertexCount.toLocaleString("en-US")} ${model.vertexCount === 1 ? "vertex" : "vertices"} · ${model.triangleCount.toLocaleString("en-US")} ${model.triangleCount === 1 ? "triangle" : "triangles"}`;
1271
+ if (this.mode === "cpu") {
1272
+ const frame = projectModel3DWireframe(model, this.cameraState, width, height, this.edges);
1273
+ this.cpu.visible = true;
1274
+ this.surface.visible = false;
1275
+ this.cpu.content = frame.text;
1276
+ this.status.content = `CPU wireframe · ${stats}${frame.displayedEdges < frame.totalEdges ? ` · ${frame.displayedEdges}/${frame.totalEdges} edges sampled` : ""}${model.warnings.length ? `
1277
+ ${model.warnings.join(" ")}` : ""}`;
1278
+ } else if (this.mode === "webgpu" && this.engine && this.scene && this.camera) {
1279
+ this.camera.aspect = width / (height * 2);
1280
+ this.camera.position.set(...model3DCameraPosition(this.cameraState));
1281
+ this.camera.lookAt(0, 0, 0);
1282
+ this.camera.updateProjectionMatrix();
1283
+ this.status.content = `WebGPU · ${stats}${model.warnings.length ? `
1284
+ ${model.warnings.join(" ")}` : ""}`;
1285
+ const staging = OptimizedBuffer.create(width, height, this.renderer.widthMethod, { owner: this.renderer.nativeScene, respectAlpha: true, id: `model3d-stage:${this.modelId}` });
1286
+ staging.clear(RGBA2.fromValues(0, 0, 0, 0));
1287
+ this.engine.setSize(width, height);
1288
+ this.drawing = this.engine.drawScene(this.scene, staging, 0).then(() => {
1289
+ if (!this.closing && !this.surface.isDestroyed && width === this.pixelWidth && height === this.pixelHeight) {
1290
+ this.surface.frameBuffer.clear(RGBA2.fromValues(0, 0, 0, 0));
1291
+ this.surface.frameBuffer.drawFrameBuffer(0, 0, staging);
1292
+ } else if (!this.closing) this.dirty = true;
1293
+ }).catch(() => {
1294
+ if (!this.closing) {
1295
+ this.releaseScene();
1296
+ this.engine?.destroy();
1297
+ this.engine = void 0;
1298
+ this.mode = "cpu";
1299
+ this.dirty = true;
1300
+ }
1301
+ }).finally(() => {
1302
+ staging.destroy();
1303
+ this.drawing = void 0;
1304
+ if (!this.closing) {
1305
+ this.draw();
1306
+ this.renderer.requestRender();
1307
+ }
1308
+ });
1309
+ }
1310
+ }
1311
+ fit() {
1312
+ this.cameraState = fitModel3D(this.cameraState, Math.max(1, this.pixelWidth) / (Math.max(1, this.pixelHeight) * 2));
1313
+ this.changed();
1314
+ }
1315
+ reset() {
1316
+ this.cameraState = initialModel3DCamera();
1317
+ this.fit();
1318
+ }
1319
+ zoom(direction) {
1320
+ this.cameraState = zoomModel3D(this.cameraState, direction);
1321
+ this.changed();
1322
+ }
1323
+ handleKey(key) {
1324
+ if (!this.active || this.closing || key.eventType === "release") return false;
1325
+ let focused = this.renderer.currentFocusedRenderable;
1326
+ while (focused && focused !== this) focused = focused.parent;
1327
+ if (focused !== this) return false;
1328
+ if (["left", "right", "up", "down"].includes(key.name)) this.cameraState = orbitModel3D(this.cameraState, key.name === "left" ? -1 : key.name === "right" ? 1 : 0, key.name === "up" ? -1 : key.name === "down" ? 1 : 0);
1329
+ else if (key.sequence === "+" || key.sequence === "=" || key.sequence === "-") this.zoom(key.sequence === "-" ? 1 : -1);
1330
+ else if (key.name === "f") this.fit();
1331
+ else if (key.name === "r") this.reset();
1332
+ else return false;
1333
+ this.changed();
1334
+ key.preventDefault();
1335
+ key.stopPropagation();
1336
+ return true;
1337
+ }
1338
+ pauseRendering() {
1339
+ this.active = false;
1340
+ this.drag = void 0;
1341
+ }
1342
+ releaseScene() {
1343
+ const scene = this.scene;
1344
+ this.scene = void 0;
1345
+ this.camera = void 0;
1346
+ scene?.traverse((node) => {
1347
+ const mesh = node;
1348
+ mesh.geometry?.dispose();
1349
+ for (const material of Array.isArray(mesh.material) ? mesh.material : mesh.material ? [mesh.material] : []) material.dispose();
1350
+ });
1351
+ }
1352
+ dispose() {
1353
+ if (this.cleanup) return this.cleanup;
1354
+ this.closing = true;
1355
+ this.active = false;
1356
+ this.epoch++;
1357
+ this.renderer.removeFrameCallback(this.frame);
1358
+ this.cleanup = (async () => {
1359
+ try {
1360
+ await this.loading;
1361
+ await this.drawing;
1362
+ } finally {
1363
+ try {
1364
+ this.releaseScene();
1365
+ } finally {
1366
+ try {
1367
+ this.engine?.destroy();
1368
+ } finally {
1369
+ this.engine = void 0;
1370
+ this.model = void 0;
1371
+ this.edges = void 0;
1372
+ this.resolveClosed();
1373
+ }
1374
+ }
1375
+ }
1376
+ })().catch((error) => {
1377
+ this.cleanupDiagnostic = plainText(error instanceof Error ? error.message : "3D preview cleanup failed").slice(0, 200);
1378
+ });
1379
+ return this.cleanup;
1380
+ }
1381
+ destroySelf() {
1382
+ void this.dispose();
1383
+ super.destroySelf();
1384
+ }
1385
+ };
1386
+
741
1387
  // packages/cli/src/tui/native/components/code.ts
742
1388
  var aliases = {
743
1389
  js: "javascript",
@@ -972,7 +1618,7 @@ var NativeBlockView = class {
972
1618
  this.inline = inline;
973
1619
  this.spec = spec;
974
1620
  this.pixel = inline && spec.kind === "text" && Boolean(spec.actions?.length) && spec.actions.every((action) => action.id.startsWith("pixel:"));
975
- this.root = new BoxRenderable2(context.renderer, {
1621
+ this.root = new BoxRenderable3(context.renderer, {
976
1622
  id: `block:${spec.id}`,
977
1623
  flexDirection: "column",
978
1624
  flexShrink: inline && !this.pixel ? 1 : 0,
@@ -997,6 +1643,7 @@ var NativeBlockView = class {
997
1643
  progress;
998
1644
  image;
999
1645
  media;
1646
+ model3d;
1000
1647
  terminal;
1001
1648
  collapsed = false;
1002
1649
  previousModelCollapsed;
@@ -1016,7 +1663,7 @@ var NativeBlockView = class {
1016
1663
  this.root.marginBottom = 0;
1017
1664
  this.root.flexDirection = spec.kind === "row" ? "row" : "column";
1018
1665
  if (spec.title) {
1019
- this.title = new TextRenderable2(renderer2, {
1666
+ this.title = new TextRenderable3(renderer2, {
1020
1667
  id: `block-title:${spec.id}`,
1021
1668
  content: "",
1022
1669
  fg: colors.foreground,
@@ -1040,7 +1687,7 @@ var NativeBlockView = class {
1040
1687
  nativeControlFocus(this.title, 1);
1041
1688
  this.root.add(this.title);
1042
1689
  }
1043
- this.body = new BoxRenderable2(renderer2, {
1690
+ this.body = new BoxRenderable3(renderer2, {
1044
1691
  id: `block-body:${spec.id}`,
1045
1692
  flexDirection: spec.kind === "row" ? "row" : "column",
1046
1693
  width: this.pixel ? "auto" : "100%",
@@ -1064,6 +1711,10 @@ var NativeBlockView = class {
1064
1711
  this.media = new NativeMediaView(renderer2, spec.id, spec.media, colors, this.context.emit);
1065
1712
  this.context.mediaViews?.add(this.media);
1066
1713
  this.body.add(this.media);
1714
+ } else if (spec.kind === "model3d" && spec.model3d) {
1715
+ this.model3d = new NativeModel3DView(renderer2, spec.id, spec.model3d, colors);
1716
+ this.context.modelViews?.add(this.model3d);
1717
+ this.body.add(this.model3d);
1067
1718
  } else if (spec.kind === "image" && spec.imagePath) {
1068
1719
  this.image = new ImageRenderable2(renderer2, {
1069
1720
  id: `image:${spec.id}`,
@@ -1076,7 +1727,7 @@ var NativeBlockView = class {
1076
1727
  if (this.image) this.image.visible = false;
1077
1728
  if (this.text) {
1078
1729
  this.text.visible = true;
1079
- if (this.text instanceof TextRenderable2) this.text.content = styledText(this.spec.text ?? "Image unavailable");
1730
+ if (this.text instanceof TextRenderable3) this.text.content = styledText(this.spec.text ?? "Image unavailable");
1080
1731
  }
1081
1732
  this.context.emit({ type: "action", action: "image-error", id: spec.id, text: String(error) });
1082
1733
  },
@@ -1086,7 +1737,7 @@ var NativeBlockView = class {
1086
1737
  }
1087
1738
  });
1088
1739
  this.body.add(this.image);
1089
- this.text = new TextRenderable2(renderer2, { id: `image-fallback:${spec.id}`, content: "", visible: false, width: "100%", wrapMode: "word", fg: colors.dim, selectable: true });
1740
+ this.text = new TextRenderable3(renderer2, { id: `image-fallback:${spec.id}`, content: "", visible: false, width: "100%", wrapMode: "word", fg: colors.dim, selectable: true });
1090
1741
  this.body.add(this.text);
1091
1742
  } else if (spec.kind === "markdown") {
1092
1743
  this.text = new MarkdownRenderable(renderer2, {
@@ -1144,7 +1795,7 @@ var NativeBlockView = class {
1144
1795
  });
1145
1796
  this.body.add(this.text);
1146
1797
  } else if (spec.text !== void 0) {
1147
- this.text = new TextRenderable2(renderer2, {
1798
+ this.text = new TextRenderable3(renderer2, {
1148
1799
  id: `text:${spec.id}`,
1149
1800
  content: "",
1150
1801
  width: this.pixel ? "auto" : "100%",
@@ -1157,7 +1808,7 @@ var NativeBlockView = class {
1157
1808
  this.body.add(this.text);
1158
1809
  }
1159
1810
  if (spec.kind === "progress") {
1160
- this.progress = new TextRenderable2(renderer2, {
1811
+ this.progress = new TextRenderable3(renderer2, {
1161
1812
  id: `progress-track:${spec.id}`,
1162
1813
  width: "100%",
1163
1814
  height: 1,
@@ -1171,7 +1822,7 @@ var NativeBlockView = class {
1171
1822
  this.body.add(this.progress);
1172
1823
  }
1173
1824
  this.children = new NativeBlockList(this.context, this.body);
1174
- this.actions = new BoxRenderable2(renderer2, {
1825
+ this.actions = new BoxRenderable3(renderer2, {
1175
1826
  id: `block-actions:${spec.id}`,
1176
1827
  flexDirection: "row",
1177
1828
  gap: 1,
@@ -1185,6 +1836,11 @@ var NativeBlockView = class {
1185
1836
  this.media.destroy();
1186
1837
  this.media = void 0;
1187
1838
  }
1839
+ if (this.model3d) {
1840
+ this.context.modelViews?.delete(this.model3d);
1841
+ this.model3d.destroy();
1842
+ this.model3d = void 0;
1843
+ }
1188
1844
  this.children?.destroy();
1189
1845
  this.markdownSections?.destroy();
1190
1846
  this.markdownSections = void 0;
@@ -1222,7 +1878,7 @@ var NativeBlockView = class {
1222
1878
  this.markdownSections.destroy();
1223
1879
  this.markdownSections = void 0;
1224
1880
  }
1225
- if (this.text instanceof TextRenderable2) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
1881
+ if (this.text instanceof TextRenderable3) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
1226
1882
  else if (this.text instanceof MarkdownRenderable || this.text instanceof CodeRenderable) {
1227
1883
  const code = this.text instanceof CodeRenderable ? nativeCodeContent(plainText(spec.text), spec.language) : void 0;
1228
1884
  this.text.content = sections ? "" : code?.content ?? plainText(spec.text);
@@ -1231,6 +1887,7 @@ var NativeBlockView = class {
1231
1887
  else keepMarkdownFallback(this.text);
1232
1888
  }
1233
1889
  if (this.media && spec.media) this.media.update(spec.media);
1890
+ if (this.model3d && spec.model3d) this.model3d.update(spec.model3d);
1234
1891
  if (this.image && spec.imagePath && this.image.source !== spec.imagePath) this.image.source = spec.imagePath;
1235
1892
  if (this.terminal) this.terminal.height = spec.height ?? 14;
1236
1893
  if (this.image) this.image.height = spec.height ?? 12;
@@ -1241,7 +1898,7 @@ var NativeBlockView = class {
1241
1898
  this.actionsSignature = actionsSignature;
1242
1899
  for (const child of [...this.actions.getChildren()]) child.destroy();
1243
1900
  for (const action of spec.actions ?? []) {
1244
- const button = new TextRenderable2(this.context.renderer, {
1901
+ const button = new TextRenderable3(this.context.renderer, {
1245
1902
  id: `block-action:${spec.id}:${action.id}`,
1246
1903
  content: plainText(action.label),
1247
1904
  fg: this.context.colors.foreground,
@@ -1295,13 +1952,17 @@ var NativeBlockView = class {
1295
1952
  applyTheme() {
1296
1953
  const colors = this.context.colors;
1297
1954
  this.root.backgroundColor = colors.background;
1955
+ this.root.shouldFill = this.spec.kind !== "model3d";
1298
1956
  if (this.spec.kind === "card") this.root.borderColor = colors.border;
1299
1957
  else this.root.border = false;
1300
1958
  if (this.title) {
1301
1959
  this.title.fg = colors.foreground;
1302
1960
  this.title.bg = colors.background;
1303
1961
  }
1304
- if (this.body) this.body.backgroundColor = colors.background;
1962
+ if (this.body) {
1963
+ this.body.backgroundColor = colors.background;
1964
+ this.body.shouldFill = this.spec.kind !== "model3d";
1965
+ }
1305
1966
  if (this.text) {
1306
1967
  this.text.fg = colors.foreground;
1307
1968
  this.text.bg = colors.background;
@@ -1310,11 +1971,12 @@ var NativeBlockView = class {
1310
1971
  this.progress.fg = colors.foreground;
1311
1972
  this.progress.bg = colors.background;
1312
1973
  }
1313
- for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable2) {
1974
+ for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable3) {
1314
1975
  action.fg = colors.foreground;
1315
1976
  action.bg = colors.background;
1316
1977
  }
1317
1978
  this.media?.applyTheme(colors);
1979
+ this.model3d?.applyTheme(colors);
1318
1980
  this.children?.applyTheme();
1319
1981
  this.markdownSections?.applyTheme();
1320
1982
  }
@@ -1324,6 +1986,11 @@ var NativeBlockView = class {
1324
1986
  this.media.destroy();
1325
1987
  this.media = void 0;
1326
1988
  }
1989
+ if (this.model3d) {
1990
+ this.context.modelViews?.delete(this.model3d);
1991
+ this.model3d.destroy();
1992
+ this.model3d = void 0;
1993
+ }
1327
1994
  this.children?.destroy();
1328
1995
  this.markdownSections?.destroy();
1329
1996
  if (this.terminal) this.context.terminals.delete(this.spec.id);
@@ -1395,13 +2062,13 @@ var NativeBlockList = class {
1395
2062
 
1396
2063
  // packages/cli/src/tui/native/components/modals.ts
1397
2064
  import {
1398
- BoxRenderable as BoxRenderable4,
2065
+ BoxRenderable as BoxRenderable5,
1399
2066
  CliRenderEvents as CliRenderEvents3,
1400
2067
  InputRenderable,
1401
2068
  ScrollBoxRenderable as ScrollBoxRenderable2,
1402
2069
  TextareaRenderable,
1403
2070
  TextAttributes as TextAttributes4,
1404
- TextRenderable as TextRenderable3
2071
+ TextRenderable as TextRenderable4
1405
2072
  } from "@opentui/core";
1406
2073
 
1407
2074
  // packages/cli/src/tui/native/components/select.ts
@@ -1554,7 +2221,7 @@ var NativeSelectRenderable = class extends SelectRenderable {
1554
2221
  };
1555
2222
 
1556
2223
  // packages/cli/src/tui/native/components/corner-frame.ts
1557
- import { BoxRenderable as BoxRenderable3, CliRenderEvents as CliRenderEvents2, RenderableEvents as RenderableEvents2, TextAttributes as TextAttributes3 } from "@opentui/core";
2224
+ import { BoxRenderable as BoxRenderable4, CliRenderEvents as CliRenderEvents2, RenderableEvents as RenderableEvents2, TextAttributes as TextAttributes3 } from "@opentui/core";
1558
2225
  function drawCornerFrame(buffer, panel, colors, progress = 1) {
1559
2226
  const { x, y, width, height } = panel;
1560
2227
  if (width <= 0 || height <= 0) return;
@@ -1593,7 +2260,7 @@ var NativeCornerFrame = class {
1593
2260
  this.colors = colors;
1594
2261
  this.renderer = renderer2;
1595
2262
  this.container = container;
1596
- this.chrome = new BoxRenderable3(renderer2, {
2263
+ this.chrome = new BoxRenderable4(renderer2, {
1597
2264
  id: `${panel.id}:corners`,
1598
2265
  position: "absolute",
1599
2266
  left: 0,
@@ -1808,7 +2475,9 @@ function nativeModalWidth(spec, columns) {
1808
2475
  const blockWidth = (block, depth) => {
1809
2476
  if (depth > 16) return 0;
1810
2477
  const children = (block.children ?? []).slice(0, 256).map((child) => blockWidth(child, depth + 1));
2478
+ const mediaWidth = block.kind === "model3d" ? 96 : block.kind === "media" ? block.media?.kind === "image" ? 96 : 64 : 0;
1811
2479
  return Math.min(limit, Math.max(
2480
+ mediaWidth,
1812
2481
  longest(plainText(block.text)),
1813
2482
  longest(block.title ?? ""),
1814
2483
  actions(block.actions),
@@ -1840,7 +2509,7 @@ var NativeModalView = class {
1840
2509
  this.close = close2;
1841
2510
  this.spec = spec;
1842
2511
  const { renderer: renderer2, colors } = context;
1843
- this.root = new BoxRenderable4(renderer2, {
2512
+ this.root = new BoxRenderable5(renderer2, {
1844
2513
  id: `modal:${spec.id}`,
1845
2514
  position: "absolute",
1846
2515
  top: 0,
@@ -1862,7 +2531,7 @@ var NativeModalView = class {
1862
2531
  }
1863
2532
  }
1864
2533
  });
1865
- this.panel = new BoxRenderable4(renderer2, {
2534
+ this.panel = new BoxRenderable5(renderer2, {
1866
2535
  id: `modal-panel:${spec.id}`,
1867
2536
  flexDirection: "column",
1868
2537
  width: nativeModalWidth(spec, renderer2.terminalWidth),
@@ -1892,10 +2561,10 @@ var NativeModalView = class {
1892
2561
  this.panel.add(this.body);
1893
2562
  styleNativeScrollbars(this.body, colors);
1894
2563
  this.breadcrumb = this.addText(`modal-breadcrumb:${spec.id}`);
1895
- const header = new BoxRenderable4(renderer2, { id: `modal-heading:${spec.id}`, width: "100%", flexDirection: "row", gap: 1, flexShrink: 0 });
1896
- this.heading = new TextRenderable3(renderer2, { id: `modal-title:${spec.id}`, content: "", width: 0, flexGrow: 1, flexShrink: 1, wrapMode: "word", fg: colors.foreground });
2564
+ const header = new BoxRenderable5(renderer2, { id: `modal-heading:${spec.id}`, width: "100%", flexDirection: "row", gap: 1, flexShrink: 0 });
2565
+ this.heading = new TextRenderable4(renderer2, { id: `modal-title:${spec.id}`, content: "", width: 0, flexGrow: 1, flexShrink: 1, wrapMode: "word", fg: colors.foreground });
1897
2566
  this.heading.maxHeight = 2;
1898
- this.headerActions = new BoxRenderable4(renderer2, { id: `modal-header-actions:${spec.id}`, flexDirection: "row", gap: 1, flexShrink: 1, minWidth: 0, maxWidth: "60%" });
2567
+ this.headerActions = new BoxRenderable5(renderer2, { id: `modal-header-actions:${spec.id}`, flexDirection: "row", gap: 1, flexShrink: 1, minWidth: 0, maxWidth: "60%" });
1899
2568
  header.add(this.heading);
1900
2569
  header.add(this.headerActions);
1901
2570
  this.panel.add(header, 0);
@@ -1904,11 +2573,11 @@ var NativeModalView = class {
1904
2573
  else if (spec.kind === "input") this.buildInput();
1905
2574
  else if (spec.kind === "document") {
1906
2575
  this.document = this.body;
1907
- const documentBlocks = new BoxRenderable4(renderer2, { id: `modal-document-blocks:${spec.id}`, width: "100%", flexDirection: "column", flexShrink: 0 });
2576
+ const documentBlocks = new BoxRenderable5(renderer2, { id: `modal-document-blocks:${spec.id}`, width: "100%", flexDirection: "column", flexShrink: 0 });
1908
2577
  this.body.add(documentBlocks);
1909
2578
  this.blocks = new NativeBlockList(this.modalContext(), documentBlocks);
1910
2579
  } else {
1911
- this.progress = new TextRenderable3(renderer2, {
2580
+ this.progress = new TextRenderable4(renderer2, {
1912
2581
  id: `modal-progress:${spec.id}`,
1913
2582
  width: "100%",
1914
2583
  height: 1,
@@ -1920,15 +2589,16 @@ var NativeModalView = class {
1920
2589
  selectable: false
1921
2590
  });
1922
2591
  this.body.add(this.progress);
1923
- const progressBlocks = new BoxRenderable4(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
2592
+ const progressBlocks = new BoxRenderable5(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
1924
2593
  this.body.add(progressBlocks);
1925
2594
  this.blocks = new NativeBlockList(this.modalContext(), progressBlocks);
1926
2595
  }
1927
2596
  this.error = this.addText(`modal-error:${spec.id}`);
1928
2597
  this.hints = this.addText(`modal-hints:${spec.id}`, this.panel);
1929
2598
  this.hints.maxHeight = 2;
1930
- this.actions = new BoxRenderable4(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", flexWrap: "wrap", flexShrink: 0, gap: 1, width: "100%", marginTop: 0 });
2599
+ this.actions = new BoxRenderable5(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", flexWrap: "wrap", flexShrink: 0, gap: 1, width: "100%", marginTop: 0 });
1931
2600
  this.panel.add(this.actions);
2601
+ this.panel.renderBefore = this.paintModelChrome;
1932
2602
  this.update(spec);
1933
2603
  renderer2.on(CliRenderEvents3.RESIZE, this.fit);
1934
2604
  this.panel.on("resize", this.fit);
@@ -1967,15 +2637,42 @@ var NativeModalView = class {
1967
2637
  optionSignature = "";
1968
2638
  disposed = false;
1969
2639
  revealFocused = false;
2640
+ mediaViews = /* @__PURE__ */ new Set();
2641
+ modelViews = /* @__PURE__ */ new Set();
2642
+ transparentModel = false;
2643
+ paintModelChrome = (buffer) => {
2644
+ if (!this.transparentModel || this.disposed || this.frame.animating) return;
2645
+ const left = this.panel.x + 1, right = this.panel.x + this.panel.width - 1;
2646
+ const top = this.panel.y + 1, bottom = this.panel.y + this.panel.height - 1;
2647
+ if (right <= left || bottom <= top) return;
2648
+ const viewport = this.body.viewport;
2649
+ const canvases = [...this.modelViews].flatMap((model) => {
2650
+ const canvas = [model.findDescendantById(`model3d-surface:${model.modelId}`), model.findDescendantById(`model3d-cpu:${model.modelId}`)].find((node) => node?.visible && !node.isDestroyed);
2651
+ if (!canvas) return [];
2652
+ const x = Math.max(left, viewport.x, canvas.x), y = Math.max(top, viewport.y, canvas.y);
2653
+ const endX = Math.min(right, viewport.x + viewport.width, canvas.x + canvas.width);
2654
+ const endY = Math.min(bottom, viewport.y + viewport.height, canvas.y + canvas.height);
2655
+ return endX > x && endY > y ? [{ x, y, endX, endY }] : [];
2656
+ }).sort((a, b) => a.x - b.x);
2657
+ for (let y = top; y < bottom; y++) {
2658
+ let x = left;
2659
+ for (const canvas of canvases) {
2660
+ if (y < canvas.y || y >= canvas.endY) continue;
2661
+ if (canvas.x > x) buffer.fillRect(x, y, canvas.x - x, 1, this.context.colors.background);
2662
+ x = Math.max(x, canvas.endX);
2663
+ }
2664
+ if (right > x) buffer.fillRect(x, y, right - x, 1, this.context.colors.background);
2665
+ }
2666
+ };
1970
2667
  addText(id, parent = this.body) {
1971
- const text = new TextRenderable3(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
2668
+ const text = new TextRenderable4(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
1972
2669
  parent.add(text);
1973
2670
  return text;
1974
2671
  }
1975
2672
  replaceActions(container, actions, prefix = "") {
1976
2673
  for (const child of [...container.getChildren()]) child.destroy();
1977
2674
  for (const action of actions) {
1978
- const button = new TextRenderable3(this.context.renderer, {
2675
+ const button = new TextRenderable4(this.context.renderer, {
1979
2676
  id: `modal-action:${this.spec.id}:${prefix}${action.id}`,
1980
2677
  content: plainText(action.label),
1981
2678
  minWidth: 0,
@@ -2114,7 +2811,17 @@ var NativeModalView = class {
2114
2811
  this.refreshMask();
2115
2812
  this.refreshOptions(patch.selectedIndex);
2116
2813
  this.blocks?.replace(spec.blocks ?? []);
2117
- if (this.progress) this.progress.content = nativeProgressText(spec.progress, true);
2814
+ const transparentModel = spec.kind === "document" && Boolean(spec.blocks?.some((block) => block.kind === "model3d"));
2815
+ if (transparentModel !== this.transparentModel) {
2816
+ for (const box of [this.body, this.body.wrapper, this.body.viewport, this.body.content, this.blocks?.parent])
2817
+ if (box instanceof BoxRenderable5) box.shouldFill = !transparentModel;
2818
+ this.frame.chrome.shouldFill = !transparentModel;
2819
+ this.transparentModel = transparentModel;
2820
+ }
2821
+ if (this.progress) {
2822
+ this.progress.visible = spec.progress !== null;
2823
+ this.progress.content = nativeProgressText(spec.progress ?? void 0, true);
2824
+ }
2118
2825
  const headerActionsSignature = JSON.stringify(spec.headerActions ?? []);
2119
2826
  if (headerActionsSignature !== this.headerActionsSignature) {
2120
2827
  this.headerActionsSignature = headerActionsSignature;
@@ -2229,12 +2936,27 @@ var NativeModalView = class {
2229
2936
  }
2230
2937
  modalContext() {
2231
2938
  const scoped = Object.create(this.context);
2939
+ scoped.mediaViews = this.mediaViews;
2940
+ scoped.modelViews = this.modelViews;
2232
2941
  scoped.emit = (event) => {
2233
2942
  if (event.type === "action") this.emitAction(event.action, event.id);
2234
2943
  else this.context.emit(event);
2235
2944
  };
2236
2945
  return scoped;
2237
2946
  }
2947
+ reconcileMedia(active) {
2948
+ for (const media of this.mediaViews) media.reconcile(this.body.viewport, active && !this.frame.closing);
2949
+ for (const model of this.modelViews) model.reconcile(this.body.viewport, active && !this.frame.closing);
2950
+ }
2951
+ pauseMedia() {
2952
+ return [...[...this.mediaViews].map((media) => {
2953
+ media.pausePlayback();
2954
+ return media.closed;
2955
+ }), ...[...this.modelViews].map((model) => {
2956
+ model.pauseRendering();
2957
+ return model.closed;
2958
+ })];
2959
+ }
2238
2960
  emitAction(action, value) {
2239
2961
  const current = this.current();
2240
2962
  this.context.emit({ type: "modal-action", id: this.spec.id, action, key: current?.item.key, index: current?.index, value });
@@ -2307,7 +3029,8 @@ var NativeModalView = class {
2307
3029
  return consume();
2308
3030
  }
2309
3031
  const focusedId = this.context.renderer.currentFocusedRenderable?.id ?? "";
2310
- if ((key.name === "return" || key.sequence === " ") && /^(?:block-title:|block-action:|modal-action:)/.test(focusedId)) return false;
3032
+ for (const model of this.modelViews) if (model.handleKey(key)) return consume();
3033
+ if ((key.name === "return" || key.sequence === " ") && /^(?:block-title:|block-action:|modal-action:|media-control:|media-preview:|model3d-control:)/.test(focusedId)) return false;
2311
3034
  if (this.spec.kind === "select") {
2312
3035
  if (key.name === "up" || key.name === "down" || key.name === "pageup" || key.name === "pagedown") {
2313
3036
  const amount = key.name.startsWith("page") ? Math.max(1, Math.floor((this.select?.height ?? 5) / (this.select?.showDescription ? 2 : 1))) : 1;
@@ -2428,7 +3151,7 @@ var NativeModalView = class {
2428
3151
  editor.placeholderColor = colors.foreground;
2429
3152
  }
2430
3153
  if (this.secretMask) this.secretMask.fg = colors.foreground;
2431
- for (const action of [...this.actions.getChildren(), ...this.headerActions.getChildren()]) if (action instanceof TextRenderable3) {
3154
+ for (const action of [...this.actions.getChildren(), ...this.headerActions.getChildren()]) if (action instanceof TextRenderable4) {
2432
3155
  action.fg = colors.foreground;
2433
3156
  action.bg = colors.background;
2434
3157
  }
@@ -2453,6 +3176,14 @@ var NativeModalStack = class {
2453
3176
  restoreFocus;
2454
3177
  views = [];
2455
3178
  byId = /* @__PURE__ */ new Map();
3179
+ pendingMedia = /* @__PURE__ */ new Set();
3180
+ trackMedia(promises) {
3181
+ for (const promise of promises) {
3182
+ if (this.pendingMedia.has(promise)) continue;
3183
+ this.pendingMedia.add(promise);
3184
+ void promise.then(() => this.pendingMedia.delete(promise), () => this.pendingMedia.delete(promise));
3185
+ }
3186
+ }
2456
3187
  dimUnderlay = (buffer) => {
2457
3188
  const panel = this.active?.panel;
2458
3189
  if (!panel || panel.isDestroyed || !panel.visible) return;
@@ -2487,6 +3218,7 @@ var NativeModalStack = class {
2487
3218
  close(id, result, notify = false) {
2488
3219
  const view = this.byId.get(id);
2489
3220
  if (!view) return;
3221
+ this.trackMedia(view.pauseMedia());
2490
3222
  view.frame.close(() => {
2491
3223
  const wasActive = view === this.active;
2492
3224
  view.destroy();
@@ -2504,9 +3236,21 @@ var NativeModalStack = class {
2504
3236
  applyTheme() {
2505
3237
  for (const view of this.views) view.applyTheme();
2506
3238
  }
3239
+ reconcileMedia() {
3240
+ for (const view of this.views) view.reconcileMedia(view === this.active);
3241
+ }
3242
+ pauseMedia() {
3243
+ return [...this.pendingMedia, ...this.views.flatMap((view) => view.pauseMedia())];
3244
+ }
3245
+ async settleMedia() {
3246
+ while (this.pendingMedia.size) await Promise.allSettled([...this.pendingMedia]);
3247
+ }
2507
3248
  destroy() {
2508
3249
  this.context.renderer.removePostProcessFn(this.dimUnderlay);
2509
- for (const view of this.views) view.destroy();
3250
+ for (const view of this.views) {
3251
+ this.trackMedia(view.pauseMedia());
3252
+ view.destroy();
3253
+ }
2510
3254
  this.views = [];
2511
3255
  this.byId.clear();
2512
3256
  }
@@ -2514,12 +3258,12 @@ var NativeModalStack = class {
2514
3258
 
2515
3259
  // packages/cli/src/tui/native/components/instruments.ts
2516
3260
  import {
2517
- BoxRenderable as BoxRenderable8,
2518
- RGBA as RGBA2,
3261
+ BoxRenderable as BoxRenderable9,
3262
+ RGBA as RGBA3,
2519
3263
  RenderableEvents as RenderableEvents6,
2520
3264
  ScrollBoxRenderable as ScrollBoxRenderable6,
2521
3265
  TextAttributes as TextAttributes8,
2522
- TextRenderable as TextRenderable7
3266
+ TextRenderable as TextRenderable8
2523
3267
  } from "@opentui/core";
2524
3268
 
2525
3269
  // packages/cli/src/tui/native/components/device-history.ts
@@ -2576,12 +3320,12 @@ function deviceHistoryGraph(samples, field, width) {
2576
3320
 
2577
3321
  // packages/cli/src/tui/native/components/task-notes.ts
2578
3322
  import {
2579
- BoxRenderable as BoxRenderable5,
3323
+ BoxRenderable as BoxRenderable6,
2580
3324
  CliRenderEvents as CliRenderEvents4,
2581
3325
  RenderableEvents as RenderableEvents3,
2582
3326
  ScrollBoxRenderable as ScrollBoxRenderable3,
2583
3327
  TextAttributes as TextAttributes5,
2584
- TextRenderable as TextRenderable4
3328
+ TextRenderable as TextRenderable5
2585
3329
  } from "@opentui/core";
2586
3330
 
2587
3331
  // packages/cli/src/tui/native/components/instrument-format.ts
@@ -2654,7 +3398,7 @@ var marks = ["▨", "▧", "▥", "▦"];
2654
3398
  var reducedMotion = () => process.env["OMNIUS_TUI_REDUCE_MOTION"] === "1" || process.env["REDUCE_MOTION"] === "1";
2655
3399
  var safe = (value) => typeof value === "string" ? plainText(value) : "";
2656
3400
  var lines = (values) => Array.isArray(values) ? values.filter((value) => typeof value === "string").map(safe) : [];
2657
- var column = (renderer2, id) => new BoxRenderable5(renderer2, { id, width: "100%", flexDirection: "column", flexShrink: 0, minWidth: 0 });
3401
+ var column = (renderer2, id) => new BoxRenderable6(renderer2, { id, width: "100%", flexDirection: "column", flexShrink: 0, minWidth: 0 });
2658
3402
  function visible(node, renderer2) {
2659
3403
  if (node.width < 1 || node.height < 1 || node.y >= renderer2.terminalHeight || node.y + node.height <= 0) return false;
2660
3404
  for (let current = node; current; current = current.parent) {
@@ -2702,7 +3446,7 @@ var ActiveBlocks = class {
2702
3446
  this.renderer.off(CliRenderEvents4.FRAME, this.sync);
2703
3447
  }
2704
3448
  };
2705
- var Disclosure = class extends TextRenderable4 {
3449
+ var Disclosure = class extends TextRenderable5 {
2706
3450
  constructor(renderer2, id, activate, colors, ruled = false) {
2707
3451
  super(renderer2, {
2708
3452
  id,
@@ -2746,9 +3490,9 @@ var Disclosure = class extends TextRenderable4 {
2746
3490
  open = true;
2747
3491
  label = "";
2748
3492
  base = TextAttributes5.BOLD;
2749
- update(label3, open, attributes = TextAttributes5.BOLD) {
3493
+ update(label3, open2, attributes = TextAttributes5.BOLD) {
2750
3494
  this.label = label3;
2751
- this.open = open;
3495
+ this.open = open2;
2752
3496
  this.base = attributes;
2753
3497
  this.refresh();
2754
3498
  this.style();
@@ -2791,13 +3535,13 @@ function layerStatus(layer) {
2791
3535
  if (["verified", "integrated"].includes(status)) return `REPORTED ${status.toUpperCase()} · AUTHORITY NOT ATTESTED`;
2792
3536
  return ["bound", "recorded", "tool complete", "tool failed", "compact", "hold", "accepted", "requested", "held", "incomplete verification", "incomplete_verification", "incomplete", "awaiting_input", "not attached", "proposed", "specified", "ready", "pending", "waiting", "leased", "running", "candidate", "verifying", "blocked", "retryable", "completed", "failed", "rejected", "cancelled", "superseded", "received", "queued", "acknowledged", "visible_in_request", "reconciliation_required", "reconciled", "first_affected_action", "consumed"].includes(status) ? status.replace(/_/g, " ").toUpperCase() : "UNKNOWN";
2793
3537
  }
2794
- var NativeTaskNotesView = class extends BoxRenderable5 {
3538
+ var NativeTaskNotesView = class extends BoxRenderable6 {
2795
3539
  constructor(renderer2, colors, reveal) {
2796
3540
  super(renderer2, { id: "omnius-task-notes", width: "100%", minWidth: 0, flexShrink: 0, flexDirection: "column", visible: false });
2797
3541
  this.renderer = renderer2;
2798
3542
  this.colors = colors;
2799
3543
  this.reveal = reveal;
2800
- const text = (id, attributes = 0) => new TextRenderable4(renderer2, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, fg: colors.foreground, bg: colors.background, attributes, wrapMode: "word" });
3544
+ const text = (id, attributes = 0) => new TextRenderable5(renderer2, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, fg: colors.foreground, bg: colors.background, attributes, wrapMode: "word" });
2801
3545
  const heading = (id, label3, key) => {
2802
3546
  const node = new Disclosure(renderer2, id, () => {
2803
3547
  this.presentation[key] = !this.presentation[key];
@@ -2874,7 +3618,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2874
3618
  const saved = state?.sessionId ? this.sessions.get(state.sessionId) : void 0;
2875
3619
  this.presentation = saved ? { open: saved.open, tasks: saved.tasks, notes: saved.notes, layers: saved.layers } : { open: true, tasks: true, notes: true, layers: true };
2876
3620
  this.disclosures.clear();
2877
- if (saved) for (const [id, open] of Object.entries(saved.disclosures)) this.disclosures.set(id, open);
3621
+ if (saved) for (const [id, open2] of Object.entries(saved.disclosures)) this.disclosures.set(id, open2);
2878
3622
  }
2879
3623
  this.state = state;
2880
3624
  this.visible = !!state;
@@ -2907,7 +3651,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2907
3651
  this.refreshRow(row);
2908
3652
  }, this.colors);
2909
3653
  title.on(RenderableEvents3.FOCUSED, () => this.reveal?.(title));
2910
- const details = new TextRenderable4(this.renderer, { id: `task:${item.id}:metadata`, content: "", width: "100%", minWidth: 0, wrapMode: "word", flexShrink: 0, fg: this.colors.foreground, bg: this.colors.background, attributes: TextAttributes5.DIM });
3654
+ const details = new TextRenderable5(this.renderer, { id: `task:${item.id}:metadata`, content: "", width: "100%", minWidth: 0, wrapMode: "word", flexShrink: 0, fg: this.colors.foreground, bg: this.colors.background, attributes: TextAttributes5.DIM });
2911
3655
  const detailButton = new Disclosure(this.renderer, `task:${item.id}:details`, () => {
2912
3656
  const key = `meta:${item.id}`;
2913
3657
  this.disclosures.set(key, !this.disclosures.get(key));
@@ -2971,7 +3715,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2971
3715
  const id = String(note.id);
2972
3716
  let row = this.noteRows.get(id);
2973
3717
  if (!row) {
2974
- row = new TextRenderable4(this.renderer, { id: `note:${id}`, content: "", width: "100%", minWidth: 0, wrapMode: "word", flexShrink: 0, fg: this.colors.foreground, bg: this.colors.background });
3718
+ row = new TextRenderable5(this.renderer, { id: `note:${id}`, content: "", width: "100%", minWidth: 0, wrapMode: "word", flexShrink: 0, fg: this.colors.foreground, bg: this.colors.background });
2975
3719
  this.noteRows.set(id, row);
2976
3720
  }
2977
3721
  const text = `□ ${safe(note.category).toUpperCase()} · ${safe(note.content)}${note.file ? `
@@ -2992,7 +3736,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2992
3736
  const id = safe(layer.id);
2993
3737
  let row = this.layerRows.get(id);
2994
3738
  if (!row) {
2995
- row = new TextRenderable4(this.renderer, { id: `work-layer:${id}`, content: "", width: "100%", minWidth: 0, wrapMode: "word", flexShrink: 0, fg: this.colors.foreground, bg: this.colors.background });
3739
+ row = new TextRenderable5(this.renderer, { id: `work-layer:${id}`, content: "", width: "100%", minWidth: 0, wrapMode: "word", flexShrink: 0, fg: this.colors.foreground, bg: this.colors.background });
2996
3740
  this.layerRows.set(id, row);
2997
3741
  }
2998
3742
  const progress = typeof layer.progress === "number" && Number.isFinite(layer.progress) && layer.progress >= 0 && layer.progress <= 1 ? ` · ${Math.round(layer.progress * 100)}%` : "";
@@ -3051,7 +3795,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3051
3795
  if (!state) return;
3052
3796
  this.presentation = { open: state.open, tasks: state.tasks, notes: state.notes, layers: state.layers };
3053
3797
  this.disclosures.clear();
3054
- for (const [id, open] of Object.entries(state.disclosures)) this.disclosures.set(id, open);
3798
+ for (const [id, open2] of Object.entries(state.disclosures)) this.disclosures.set(id, open2);
3055
3799
  for (const row of this.rows.values()) this.refreshRow(row);
3056
3800
  this.applyPresentation();
3057
3801
  }
@@ -3059,7 +3803,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3059
3803
  const targets = [];
3060
3804
  const visit = (node) => {
3061
3805
  if (!node.visible) return;
3062
- if (node instanceof TextRenderable4 && node.focusable) targets.push(node);
3806
+ if (node instanceof TextRenderable5 && node.focusable) targets.push(node);
3063
3807
  for (const child of node.getChildren()) visit(child);
3064
3808
  };
3065
3809
  if (this.visible) visit(this);
@@ -3068,7 +3812,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3068
3812
  applyTheme(colors) {
3069
3813
  this.colors = colors;
3070
3814
  const visit = (node) => {
3071
- if (node instanceof TextRenderable4) {
3815
+ if (node instanceof TextRenderable5) {
3072
3816
  node.fg = colors.foreground;
3073
3817
  node.bg = colors.background;
3074
3818
  }
@@ -3082,7 +3826,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3082
3826
  super.destroySelf();
3083
3827
  }
3084
3828
  };
3085
- var NativeTaskStrip = class extends BoxRenderable5 {
3829
+ var NativeTaskStrip = class extends BoxRenderable6 {
3086
3830
  constructor(renderer2, colors, inspect) {
3087
3831
  super(renderer2, {
3088
3832
  id: "omnius-task-strip",
@@ -3101,7 +3845,7 @@ var NativeTaskStrip = class extends BoxRenderable5 {
3101
3845
  this.renderer = renderer2;
3102
3846
  this.colors = colors;
3103
3847
  this.inspect = inspect;
3104
- this.label = new TextRenderable4(renderer2, {
3848
+ this.label = new TextRenderable5(renderer2, {
3105
3849
  id: "omnius-task-strip:heading",
3106
3850
  content: "TASKS ",
3107
3851
  height: 1,
@@ -3200,7 +3944,7 @@ var NativeTaskStrip = class extends BoxRenderable5 {
3200
3944
  for (const [index, item] of items.entries()) {
3201
3945
  let chip = this.chips.get(item.id);
3202
3946
  if (!chip) {
3203
- const label3 = new TextRenderable4(this.renderer, {
3947
+ const label3 = new TextRenderable5(this.renderer, {
3204
3948
  id: `task-strip:${item.id}`,
3205
3949
  content: "",
3206
3950
  height: 1,
@@ -3280,26 +4024,26 @@ var NativeTaskStrip = class extends BoxRenderable5 {
3280
4024
 
3281
4025
  // packages/cli/src/tui/native/components/work-queue.ts
3282
4026
  import {
3283
- BoxRenderable as BoxRenderable6,
4027
+ BoxRenderable as BoxRenderable7,
3284
4028
  CliRenderEvents as CliRenderEvents5,
3285
4029
  RenderableEvents as RenderableEvents4,
3286
4030
  ScrollBoxRenderable as ScrollBoxRenderable4,
3287
4031
  TextAttributes as TextAttributes6,
3288
- TextRenderable as TextRenderable5
4032
+ TextRenderable as TextRenderable6
3289
4033
  } from "@opentui/core";
3290
4034
  var frames = ["▨", "▧", "▥", "▦"];
3291
4035
  var actionLabels = { queue: "QUEUE", apply: "APPLY", override: "OVERRIDE", cancel: "CANCEL", retry: "RETRY", up: "UP", down: "DOWN" };
3292
4036
  var safe2 = (value) => typeof value === "string" ? plainText(value) : "";
3293
4037
  var label = (value) => safe2(value).replace(/[\r\n\t]+/g, " ");
3294
4038
  var reducedMotion2 = () => process.env["OMNIUS_TUI_REDUCE_MOTION"] === "1" || process.env["REDUCE_MOTION"] === "1";
3295
- var column2 = (renderer2, id) => new BoxRenderable6(renderer2, { id, width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0 });
4039
+ var column2 = (renderer2, id) => new BoxRenderable7(renderer2, { id, width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0 });
3296
4040
  var historyStates = /* @__PURE__ */ new Set(["completed", "applied", "failed", "cancelled"]);
3297
4041
  var activeStates = /* @__PURE__ */ new Set(["claiming", "running", "admitted"]);
3298
4042
  var receiptTime = (at) => {
3299
4043
  const date = new Date(typeof at === "number" ? at : NaN);
3300
4044
  return Number.isFinite(date.getTime()) ? date.toISOString() : "";
3301
4045
  };
3302
- var WorkControl = class extends TextRenderable5 {
4046
+ var WorkControl = class extends TextRenderable6 {
3303
4047
  constructor(renderer2, id, activate, colors, disclosure = false, ruled = false, fixedWidth) {
3304
4048
  super(renderer2, {
3305
4049
  id,
@@ -3342,9 +4086,9 @@ var WorkControl = class extends TextRenderable5 {
3342
4086
  open = false;
3343
4087
  caption = "";
3344
4088
  base = 0;
3345
- update(caption, open = false, attributes = 0) {
4089
+ update(caption, open2 = false, attributes = 0) {
3346
4090
  this.caption = caption;
3347
- this.open = open;
4091
+ this.open = open2;
3348
4092
  this.base = attributes;
3349
4093
  this.refresh();
3350
4094
  this.style();
@@ -3361,7 +4105,7 @@ var WorkControl = class extends TextRenderable5 {
3361
4105
  if (this.plainText !== value) this.content = value;
3362
4106
  }
3363
4107
  };
3364
- var NativeWorkQueueView = class extends BoxRenderable6 {
4108
+ var NativeWorkQueueView = class extends BoxRenderable7 {
3365
4109
  constructor(renderer2, colors, onAction, reveal) {
3366
4110
  super(renderer2, { id: "omnius-work-queue", width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0, visible: false });
3367
4111
  this.renderer = renderer2;
@@ -3422,7 +4166,7 @@ var NativeWorkQueueView = class extends BoxRenderable6 {
3422
4166
  frame = 0;
3423
4167
  disposed = false;
3424
4168
  text(id, attributes = 0) {
3425
- return new TextRenderable5(this.renderer, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, wrapMode: "word", fg: this.colors.foreground, bg: this.colors.background, attributes });
4169
+ return new TextRenderable6(this.renderer, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, wrapMode: "word", fg: this.colors.foreground, bg: this.colors.background, attributes });
3426
4170
  }
3427
4171
  control(id, activate, disclosure = false, ruled = false, width) {
3428
4172
  const node = new WorkControl(this.renderer, id, activate, this.colors, disclosure, ruled, width);
@@ -3504,22 +4248,22 @@ var NativeWorkQueueView = class extends BoxRenderable6 {
3504
4248
  const metadata2 = this.text(`${id}:metadata`, TextAttributes6.DIM), original = this.text(`${id}:original`);
3505
4249
  details.add(original);
3506
4250
  details.add(metadata2);
3507
- const controls = new BoxRenderable6(this.renderer, { id: `${id}:actions`, width: "100%", minWidth: 0, flexDirection: "row", flexWrap: "wrap", flexShrink: 0 });
4251
+ const controls = new BoxRenderable7(this.renderer, { id: `${id}:actions`, width: "100%", minWidth: 0, flexDirection: "row", flexWrap: "wrap", flexShrink: 0 });
3508
4252
  details.add(controls);
3509
4253
  const row = { item, root, title, details, original, metadata: metadata2, controls, actions: /* @__PURE__ */ new Map() };
3510
4254
  this.rows.set(item.id, row);
3511
4255
  return row;
3512
4256
  }
3513
4257
  refreshRow(row) {
3514
- const item = row.item, open = this.presentation.disclosures[item.id] === true;
4258
+ const item = row.item, open2 = this.presentation.disclosures[item.id] === true;
3515
4259
  const state = label(item.state).replace(/_/g, " ").toUpperCase();
3516
4260
  const mark = item.state === "reviewing" || item.state === "claiming" ? frames[this.frame % frames.length] : item.state === "running" ? "▨" : item.state === "needs_review" || item.state === "interrupted" ? "▣" : historyStates.has(item.state) ? "■" : "□";
3517
4261
  row.title.update(
3518
4262
  `${mark} ${row.position === void 0 ? "" : `${row.position}. `}${state} · ${label(item.title) || "Untitled request"}`,
3519
- open,
4263
+ open2,
3520
4264
  item.state === "queued" ? TextAttributes6.DIM : item.state === "running" || item.state === "needs_review" ? TextAttributes6.BOLD : 0
3521
4265
  );
3522
- row.details.visible = open;
4266
+ row.details.visible = open2;
3523
4267
  const original = `ORIGINAL
3524
4268
  ${safe2(item.originalText)}`;
3525
4269
  if (row.original.plainText !== original) row.original.content = original;
@@ -3607,7 +4351,7 @@ RUN ${label(receipt.runId)}` : ""}`)
3607
4351
  const targets = [];
3608
4352
  const visit = (node) => {
3609
4353
  if (!node.visible) return;
3610
- if (node instanceof TextRenderable5 && node.focusable) targets.push(node);
4354
+ if (node instanceof TextRenderable6 && node.focusable) targets.push(node);
3611
4355
  for (const child of node.getChildren()) visit(child);
3612
4356
  };
3613
4357
  if (this.visible) visit(this);
@@ -3616,7 +4360,7 @@ RUN ${label(receipt.runId)}` : ""}`)
3616
4360
  applyTheme(colors) {
3617
4361
  this.colors = colors;
3618
4362
  const visit = (node) => {
3619
- if (node instanceof TextRenderable5) {
4363
+ if (node instanceof TextRenderable6) {
3620
4364
  node.fg = colors.foreground;
3621
4365
  node.bg = colors.background;
3622
4366
  }
@@ -3635,11 +4379,11 @@ RUN ${label(receipt.runId)}` : ""}`)
3635
4379
 
3636
4380
  // packages/cli/src/tui/native/components/workspace-files.ts
3637
4381
  import {
3638
- BoxRenderable as BoxRenderable7,
4382
+ BoxRenderable as BoxRenderable8,
3639
4383
  RenderableEvents as RenderableEvents5,
3640
4384
  ScrollBoxRenderable as ScrollBoxRenderable5,
3641
4385
  TextAttributes as TextAttributes7,
3642
- TextRenderable as TextRenderable6
4386
+ TextRenderable as TextRenderable7
3643
4387
  } from "@opentui/core";
3644
4388
  var label2 = (value) => plainText(value).replace(/[\r\n\t]/g, " ");
3645
4389
  var identity = (state) => state ? `${state.projectRoot}\0${state.sessionId ?? ""}` : "";
@@ -3651,7 +4395,7 @@ var activityLabel = (item) => {
3651
4395
  const kind = { read: "R", write: "W", create: "+", delete: "−" }[item.activity.kind];
3652
4396
  return kind + (item.activity.phase === "verified" ? "✓" : item.activity.phase === "failed" ? "!" : "…");
3653
4397
  };
3654
- var FileControl = class extends TextRenderable6 {
4398
+ var FileControl = class extends TextRenderable7 {
3655
4399
  constructor(renderer2, id, colors, activate, ruled = false, width) {
3656
4400
  super(renderer2, {
3657
4401
  id,
@@ -3689,9 +4433,9 @@ var FileControl = class extends TextRenderable6 {
3689
4433
  ruled;
3690
4434
  caption = "";
3691
4435
  open = false;
3692
- setLabel(value, open = false) {
4436
+ setLabel(value, open2 = false) {
3693
4437
  this.caption = value;
3694
- this.open = open;
4438
+ this.open = open2;
3695
4439
  this.refresh();
3696
4440
  this.style();
3697
4441
  }
@@ -3706,7 +4450,7 @@ var FileControl = class extends TextRenderable6 {
3706
4450
  this.content = this.ruled ? formatSectionBanner(this.caption, this.width, this.open) : fitInstrumentText(this.caption, this.width);
3707
4451
  }
3708
4452
  };
3709
- var NativeWorkspaceFilesView = class extends BoxRenderable7 {
4453
+ var NativeWorkspaceFilesView = class extends BoxRenderable8 {
3710
4454
  constructor(renderer2, colors, onAction, reveal) {
3711
4455
  super(renderer2, { id: "omnius-workspace-files", width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0, visible: false });
3712
4456
  this.renderer = renderer2;
@@ -3715,9 +4459,9 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3715
4459
  this.reveal = reveal;
3716
4460
  this.header = new FileControl(renderer2, "omnius-workspace-files:heading", colors, () => this.setOpen(!this.open), true);
3717
4461
  this.add(this.header);
3718
- this.body = new BoxRenderable7(renderer2, { id: "omnius-workspace-files:body", width: "100%", minWidth: 0, minHeight: 0, flexDirection: "column", flexGrow: 1, flexBasis: 0, flexShrink: 1, visible: false });
4462
+ this.body = new BoxRenderable8(renderer2, { id: "omnius-workspace-files:body", width: "100%", minWidth: 0, minHeight: 0, flexDirection: "column", flexGrow: 1, flexBasis: 0, flexShrink: 1, visible: false });
3719
4463
  this.add(this.body);
3720
- const heading = new BoxRenderable7(renderer2, { id: "omnius-workspace-files:location", width: "100%", minWidth: 0, height: 1, flexDirection: "row", gap: 1, flexShrink: 0 });
4464
+ const heading = new BoxRenderable8(renderer2, { id: "omnius-workspace-files:location", width: "100%", minWidth: 0, height: 1, flexDirection: "row", gap: 1, flexShrink: 0 });
3721
4465
  this.body.add(heading);
3722
4466
  this.rootLabel = this.text("omnius-workspace-files:root");
3723
4467
  this.rootLabel.width = 0;
@@ -3751,7 +4495,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3751
4495
  contentOptions: { flexDirection: "column", paddingRight: 1 }
3752
4496
  });
3753
4497
  this.body.add(this.scroll);
3754
- this.rootPages = new BoxRenderable7(renderer2, { id: "omnius-workspace-files:pages", width: "100%", height: 1, flexDirection: "row", gap: 2, flexShrink: 0 });
4498
+ this.rootPages = new BoxRenderable8(renderer2, { id: "omnius-workspace-files:pages", width: "100%", height: 1, flexDirection: "row", gap: 2, flexShrink: 0 });
3755
4499
  this.body.add(this.rootPages);
3756
4500
  this.rootStart = new FileControl(renderer2, "omnius-workspace-files:start", colors, () => {
3757
4501
  this.lastClick = void 0;
@@ -3800,7 +4544,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3800
4544
  layoutMinimum = 0;
3801
4545
  layoutExpanded = false;
3802
4546
  text(id) {
3803
- return new TextRenderable6(this.renderer, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, wrapMode: "word", fg: this.colors.foreground, bg: this.colors.background });
4547
+ return new TextRenderable7(this.renderer, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, wrapMode: "word", fg: this.colors.foreground, bg: this.colors.background });
3804
4548
  }
3805
4549
  update(state) {
3806
4550
  if (state === this.state) return;
@@ -3861,7 +4605,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3861
4605
  if (!pagePaths.has(item.path)) return;
3862
4606
  let page = this.pagination.get(item.path);
3863
4607
  if (!page) {
3864
- const root = new BoxRenderable7(this.renderer, { id: `${rowId(item.path)}:pages`, width: "100%", height: 1, minWidth: 0, flexDirection: "row", gap: 2, paddingLeft: Math.min(16, (item.depth + 1) * 2), flexShrink: 0 });
4608
+ const root = new BoxRenderable8(this.renderer, { id: `${rowId(item.path)}:pages`, width: "100%", height: 1, minWidth: 0, flexDirection: "row", gap: 2, paddingLeft: Math.min(16, (item.depth + 1) * 2), flexShrink: 0 });
3865
4609
  const start = new FileControl(this.renderer, `${root.id}:start`, this.colors, () => {
3866
4610
  this.lastClick = void 0;
3867
4611
  this.action("start", item.path);
@@ -3911,7 +4655,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3911
4655
  }
3912
4656
  }
3913
4657
  createRow(item) {
3914
- const root = new BoxRenderable7(this.renderer, { id: rowId(item.path), width: "100%", minWidth: 0, height: 1, flexShrink: 0, flexDirection: "row" });
4658
+ const root = new BoxRenderable8(this.renderer, { id: rowId(item.path), width: "100%", minWidth: 0, height: 1, flexShrink: 0, flexDirection: "row" });
3915
4659
  const disclosure = new FileControl(this.renderer, `${root.id}:disclosure`, this.colors, () => {
3916
4660
  const current = this.rows.get(item.path)?.item;
3917
4661
  this.lastClick = void 0;
@@ -3922,7 +4666,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3922
4666
  }, false, 2);
3923
4667
  disclosure.focusable = false;
3924
4668
  root.add(disclosure);
3925
- const name = new TextRenderable6(this.renderer, {
4669
+ const name = new TextRenderable7(this.renderer, {
3926
4670
  id: `${root.id}:name`,
3927
4671
  content: "",
3928
4672
  width: 0,
@@ -4026,12 +4770,12 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
4026
4770
  if (!state) return;
4027
4771
  this.onAction?.({ type: "workspace-action", action, path, projectRoot: state.projectRoot, sessionId: state.sessionId, revision: state.revision, epoch: state.epoch, ...action === "visibility" ? { open: this.open } : {} });
4028
4772
  }
4029
- setOpen(open) {
4030
- this.open = open;
4773
+ setOpen(open2) {
4774
+ this.open = open2;
4031
4775
  this.lastClick = void 0;
4032
4776
  this.applyPresentation();
4033
4777
  this.action("visibility", "");
4034
- if (open) this.reveal?.(this.header);
4778
+ if (open2) this.reveal?.(this.header);
4035
4779
  }
4036
4780
  get isOpen() {
4037
4781
  return this.open;
@@ -4074,7 +4818,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
4074
4818
  applyTheme(colors) {
4075
4819
  this.colors = colors;
4076
4820
  const visit = (node) => {
4077
- if (node instanceof TextRenderable6) {
4821
+ if (node instanceof TextRenderable7) {
4078
4822
  node.fg = colors.foreground;
4079
4823
  node.bg = colors.background;
4080
4824
  }
@@ -4151,9 +4895,9 @@ var NativeWorkspaceReferenceRow = class extends ScrollBoxRenderable5 {
4151
4895
  };
4152
4896
 
4153
4897
  // packages/cli/src/tui/native/components/instruments.ts
4154
- var foreground = RGBA2.defaultForeground();
4155
- var background = RGBA2.defaultBackground();
4156
- var SectionBanner = class extends TextRenderable7 {
4898
+ var foreground = RGBA3.defaultForeground();
4899
+ var background = RGBA3.defaultBackground();
4900
+ var SectionBanner = class extends TextRenderable8 {
4157
4901
  constructor(renderer2, title, id, activate) {
4158
4902
  super(renderer2, {
4159
4903
  id,
@@ -4208,7 +4952,7 @@ var SectionBanner = class extends TextRenderable7 {
4208
4952
  if (this.plainText !== next) this.content = next;
4209
4953
  }
4210
4954
  };
4211
- var TelemetryRow = class extends TextRenderable7 {
4955
+ var TelemetryRow = class extends TextRenderable8 {
4212
4956
  constructor(renderer2, label3, id, reserved = 12) {
4213
4957
  super(renderer2, {
4214
4958
  id,
@@ -4254,7 +4998,7 @@ var SignalMeter = class extends TelemetryRow {
4254
4998
  this.update(formatSignalMeter(value, maximum));
4255
4999
  }
4256
5000
  };
4257
- var InstrumentDivider = class extends TextRenderable7 {
5001
+ var InstrumentDivider = class extends TextRenderable8 {
4258
5002
  constructor(renderer2, actions) {
4259
5003
  super(renderer2, {
4260
5004
  id: "omnius-instrument-divider",
@@ -4351,17 +5095,17 @@ var InstrumentDivider = class extends TextRenderable7 {
4351
5095
  this.attributes = this.focused ? TextAttributes8.INVERSE : TextAttributes8.DIM;
4352
5096
  }
4353
5097
  };
4354
- var DeviceMonitor = class extends BoxRenderable8 {
5098
+ var DeviceMonitor = class extends BoxRenderable9 {
4355
5099
  constructor(renderer2, action) {
4356
5100
  super(renderer2, { id: "omnius-devices", width: "100%", height: 5, minHeight: 3, flexShrink: 0, flexDirection: "column", visible: false });
4357
5101
  this.action = action;
4358
5102
  this.heading = new SectionBanner(renderer2, "DEVICES", "omnius-devices:heading", () => this.toggle());
4359
5103
  this.heading.update("DEVICES", false);
4360
5104
  this.add(this.heading);
4361
- this.summary = new TextRenderable7(renderer2, { id: "omnius-devices:summary", content: "--", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
5105
+ this.summary = new TextRenderable8(renderer2, { id: "omnius-devices:summary", content: "--", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
4362
5106
  this.add(this.summary);
4363
- this.compactLoad = new TextRenderable7(renderer2, { id: "omnius-devices:load-history", content: "", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
4364
- this.compactMemory = new TextRenderable7(renderer2, { id: "omnius-devices:memory-history", content: "", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
5107
+ this.compactLoad = new TextRenderable8(renderer2, { id: "omnius-devices:load-history", content: "", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
5108
+ this.compactMemory = new TextRenderable8(renderer2, { id: "omnius-devices:memory-history", content: "", width: "100%", height: 1, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true });
4365
5109
  this.add(this.compactLoad);
4366
5110
  this.add(this.compactMemory);
4367
5111
  this.scroll = new ScrollBoxRenderable6(renderer2, {
@@ -4381,7 +5125,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4381
5125
  this.add(this.scroll);
4382
5126
  for (const [key, title] of [["cpu", "CPU"], ["memory", "MEMORY"], ["disk", "DISK"], ["network", "NETWORK"], ["gpu", "GPU"]])
4383
5127
  this.createSection(renderer2, key, title);
4384
- this.notes = new TextRenderable7(renderer2, {
5128
+ this.notes = new TextRenderable8(renderer2, {
4385
5129
  id: "omnius-devices:notes",
4386
5130
  content: "",
4387
5131
  width: "100%",
@@ -4393,7 +5137,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4393
5137
  truncate: true
4394
5138
  });
4395
5139
  this.sections.get("gpu").body.add(this.notes);
4396
- const controls = new BoxRenderable8(renderer2, { id: "omnius-devices:controls", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2 });
5140
+ const controls = new BoxRenderable9(renderer2, { id: "omnius-devices:controls", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2 });
4397
5141
  this.add(controls);
4398
5142
  this.policy = this.button(renderer2, "omnius-devices:policy", "GPU POLICY", () => {
4399
5143
  if (this.canSelect()) this.action?.("/gpu");
@@ -4422,7 +5166,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4422
5166
  historySource = "";
4423
5167
  rows = /* @__PURE__ */ new Map();
4424
5168
  createSection(renderer2, key, title) {
4425
- const root = new BoxRenderable8(renderer2, {
5169
+ const root = new BoxRenderable9(renderer2, {
4426
5170
  id: `omnius-devices:${key}`,
4427
5171
  width: "100%",
4428
5172
  minWidth: 0,
@@ -4438,7 +5182,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4438
5182
  });
4439
5183
  const header = new SectionBanner(renderer2, title, `${root.id}:heading`, () => this.toggleDeviceSection(key));
4440
5184
  header.on(RenderableEvents6.FOCUSED, () => this.revealReading(header));
4441
- const body = new BoxRenderable8(renderer2, { id: `${root.id}:body`, width: "100%", minWidth: 0, flexShrink: 0, flexDirection: "column", visible: this.sectionState[key] });
5185
+ const body = new BoxRenderable9(renderer2, { id: `${root.id}:body`, width: "100%", minWidth: 0, flexShrink: 0, flexDirection: "column", visible: this.sectionState[key] });
4442
5186
  root.add(header);
4443
5187
  root.add(body);
4444
5188
  this.scroll.add(root);
@@ -4449,7 +5193,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4449
5193
  readings.set(label3, row);
4450
5194
  body.add(row);
4451
5195
  }
4452
- const detail = key !== "gpu" ? new TextRenderable7(renderer2, {
5196
+ const detail = key !== "gpu" ? new TextRenderable8(renderer2, {
4453
5197
  id: `${root.id}:detail`,
4454
5198
  content: "",
4455
5199
  width: "100%",
@@ -4529,7 +5273,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4529
5273
  this.sections.get("network").detail.content = system?.network.source === "proc-net-dev" ? "All non-loopback interfaces" : system?.network.source === "netstat" ? "Interface en0" : "Interface counters unavailable";
4530
5274
  }
4531
5275
  button(renderer2, id, label3, activate) {
4532
- const button = new TextRenderable7(renderer2, {
5276
+ const button = new TextRenderable8(renderer2, {
4533
5277
  id,
4534
5278
  content: label3,
4535
5279
  height: 1,
@@ -4592,7 +5336,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4592
5336
  for (const device of snapshot?.devices ?? []) {
4593
5337
  let row = this.rows.get(device.uuid);
4594
5338
  if (!row) {
4595
- const root = new BoxRenderable8(this._ctx, {
5339
+ const root = new BoxRenderable9(this._ctx, {
4596
5340
  id: `omnius-device:${device.uuid}`,
4597
5341
  width: "100%",
4598
5342
  flexDirection: "column",
@@ -4611,9 +5355,9 @@ var DeviceMonitor = class extends BoxRenderable8 {
4611
5355
  title.on(RenderableEvents6.FOCUSED, () => this.revealReading(title));
4612
5356
  const load = new TelemetryRow(this._ctx, "LOAD", `${root.id}:load`, 16);
4613
5357
  const memory = new TelemetryRow(this._ctx, "VRAM", `${root.id}:memory`, 16);
4614
- const plot = (name) => new TextRenderable7(this._ctx, { id: `${root.id}:${name}-history`, content: "", width: "100%", height: 3, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true, selectable: false });
5358
+ const plot = (name) => new TextRenderable8(this._ctx, { id: `${root.id}:${name}-history`, content: "", width: "100%", height: 3, flexShrink: 0, fg: foreground, bg: background, wrapMode: "none", truncate: true, selectable: false });
4615
5359
  const loadGraph = plot("load"), memoryGraph = plot("memory"), history = new DeviceHistory();
4616
- const reason = new TextRenderable7(this._ctx, {
5360
+ const reason = new TextRenderable8(this._ctx, {
4617
5361
  id: `${root.id}:reason`,
4618
5362
  content: "",
4619
5363
  width: "100%",
@@ -4757,7 +5501,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4757
5501
  ];
4758
5502
  }
4759
5503
  };
4760
- var ChannelMap = class extends BoxRenderable8 {
5504
+ var ChannelMap = class extends BoxRenderable9 {
4761
5505
  constructor(renderer2, id, inspect, reveal) {
4762
5506
  super(renderer2, {
4763
5507
  id,
@@ -4789,7 +5533,7 @@ var ChannelMap = class extends BoxRenderable8 {
4789
5533
  if (!this.channels.has(agent.id))
4790
5534
  this.channels.set(agent.id, this.channels.size + 1);
4791
5535
  if (!this.rows.has(agent.id)) {
4792
- const row = new TextRenderable7(this._ctx, {
5536
+ const row = new TextRenderable8(this._ctx, {
4793
5537
  id: `${this.id}:channel:${agent.id}`,
4794
5538
  content: "",
4795
5539
  width: "100%",
@@ -4855,7 +5599,7 @@ var ChannelMap = class extends BoxRenderable8 {
4855
5599
  }
4856
5600
  }
4857
5601
  };
4858
- var InstrumentRail = class extends BoxRenderable8 {
5602
+ var InstrumentRail = class extends BoxRenderable9 {
4859
5603
  workQueue;
4860
5604
  workspaceFiles;
4861
5605
  taskNotes;
@@ -4942,7 +5686,7 @@ var InstrumentRail = class extends BoxRenderable8 {
4942
5686
  () => this.reveal(this.inferenceHeader)
4943
5687
  );
4944
5688
  this.scroll.add(this.inferenceHeader);
4945
- this.inference = new BoxRenderable8(renderer2, {
5689
+ this.inference = new BoxRenderable9(renderer2, {
4946
5690
  id: `${id}:inference-body`,
4947
5691
  width: "100%",
4948
5692
  flexDirection: "column",
@@ -4957,7 +5701,7 @@ var InstrumentRail = class extends BoxRenderable8 {
4957
5701
  };
4958
5702
  addRow(this.inference, "route", "ROUTE");
4959
5703
  addRow(this.inference, "backend", "BACKEND");
4960
- this.model = new TextRenderable7(renderer2, {
5704
+ this.model = new TextRenderable8(renderer2, {
4961
5705
  id: `${id}:model`,
4962
5706
  content: "MODEL --",
4963
5707
  width: "100%",
@@ -4986,7 +5730,7 @@ var InstrumentRail = class extends BoxRenderable8 {
4986
5730
  });
4987
5731
  this.contextHeader.on(RenderableEvents6.FOCUSED, () => this.reveal(this.contextHeader));
4988
5732
  this.inference.add(this.contextHeader);
4989
- this.contextDetails = new BoxRenderable8(renderer2, { id: `${id}:context-detail-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
5733
+ this.contextDetails = new BoxRenderable9(renderer2, { id: `${id}:context-detail-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
4990
5734
  this.inference.add(this.contextDetails);
4991
5735
  this.contextHeader.update("CONTEXT DETAILS", false);
4992
5736
  addRow(this.contextDetails, "messages", "MESSAGES");
@@ -5002,7 +5746,7 @@ var InstrumentRail = class extends BoxRenderable8 {
5002
5746
  this.routeAttemptsHeader.visible = false;
5003
5747
  this.routeAttemptsHeader.on(RenderableEvents6.FOCUSED, () => this.reveal(this.routeAttemptsHeader));
5004
5748
  this.inference.add(this.routeAttemptsHeader);
5005
- this.routeAttempts = new BoxRenderable8(renderer2, { id: `${id}:route-attempt-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
5749
+ this.routeAttempts = new BoxRenderable9(renderer2, { id: `${id}:route-attempt-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
5006
5750
  this.inference.add(this.routeAttempts);
5007
5751
  this.sessionHeader = new SectionBanner(
5008
5752
  renderer2,
@@ -5015,7 +5759,7 @@ var InstrumentRail = class extends BoxRenderable8 {
5015
5759
  () => this.reveal(this.sessionHeader)
5016
5760
  );
5017
5761
  this.scroll.add(this.sessionHeader);
5018
- this.session = new BoxRenderable8(renderer2, {
5762
+ this.session = new BoxRenderable9(renderer2, {
5019
5763
  id: `${id}:session-body`,
5020
5764
  width: "100%",
5021
5765
  flexDirection: "column",
@@ -5113,9 +5857,9 @@ var InstrumentRail = class extends BoxRenderable8 {
5113
5857
  let row = this.routeAttemptRows[index];
5114
5858
  if (!row) {
5115
5859
  const id = `${this.routeAttempts.id}:${index}`;
5116
- const box = new BoxRenderable8(this.ctx, { id, width: "100%", flexDirection: "column", flexShrink: 0 });
5117
- const title2 = new TextRenderable7(this.ctx, { id: `${id}:title`, width: "100%", height: 1, content: "", fg: foreground, bg: background, wrapMode: "none", truncate: true, flexShrink: 0 });
5118
- const detail2 = new TextRenderable7(this.ctx, { id: `${id}:detail`, width: "100%", content: "", fg: foreground, bg: background, attributes: TextAttributes8.DIM, wrapMode: "word", flexShrink: 0 });
5860
+ const box = new BoxRenderable9(this.ctx, { id, width: "100%", flexDirection: "column", flexShrink: 0 });
5861
+ const title2 = new TextRenderable8(this.ctx, { id: `${id}:title`, width: "100%", height: 1, content: "", fg: foreground, bg: background, wrapMode: "none", truncate: true, flexShrink: 0 });
5862
+ const detail2 = new TextRenderable8(this.ctx, { id: `${id}:detail`, width: "100%", content: "", fg: foreground, bg: background, attributes: TextAttributes8.DIM, wrapMode: "word", flexShrink: 0 });
5119
5863
  box.add(title2);
5120
5864
  box.add(detail2);
5121
5865
  this.routeAttempts.add(box);
@@ -5138,7 +5882,7 @@ var InstrumentRail = class extends BoxRenderable8 {
5138
5882
  applyTheme(colors) {
5139
5883
  this.backgroundColor = colors.background;
5140
5884
  const visit = (node) => {
5141
- if (node instanceof TextRenderable7) {
5885
+ if (node instanceof TextRenderable8) {
5142
5886
  node.fg = colors.foreground;
5143
5887
  node.bg = colors.background;
5144
5888
  }
@@ -6691,7 +7435,7 @@ var VimComposer = class {
6691
7435
  };
6692
7436
 
6693
7437
  // packages/cli/src/tui/native/components/selection-menu.ts
6694
- import { BoxRenderable as BoxRenderable9, CliRenderEvents as CliRenderEvents6, TextAttributes as TextAttributes9, TextRenderable as TextRenderable8 } from "@opentui/core";
7438
+ import { BoxRenderable as BoxRenderable10, CliRenderEvents as CliRenderEvents6, TextAttributes as TextAttributes9, TextRenderable as TextRenderable9 } from "@opentui/core";
6695
7439
  var NativeSelectionMenu = class {
6696
7440
  constructor(renderer2, colors, copy, restoreFocus) {
6697
7441
  this.renderer = renderer2;
@@ -6719,7 +7463,7 @@ var NativeSelectionMenu = class {
6719
7463
  this.selectedText = text;
6720
7464
  this.anchor = { x, y };
6721
7465
  this.previousFocus = this.renderer.currentFocusedRenderable;
6722
- this.overlay = new BoxRenderable9(this.renderer, {
7466
+ this.overlay = new BoxRenderable10(this.renderer, {
6723
7467
  id: "omnius-selection-menu",
6724
7468
  position: "absolute",
6725
7469
  left: 0,
@@ -6735,7 +7479,7 @@ var NativeSelectionMenu = class {
6735
7479
  event.stopPropagation();
6736
7480
  }
6737
7481
  });
6738
- this.panel = new BoxRenderable9(this.renderer, {
7482
+ this.panel = new BoxRenderable10(this.renderer, {
6739
7483
  id: "omnius-selection-menu-panel",
6740
7484
  position: "absolute",
6741
7485
  width: 14,
@@ -6749,7 +7493,7 @@ var NativeSelectionMenu = class {
6749
7493
  event.stopPropagation();
6750
7494
  }
6751
7495
  });
6752
- const action = new TextRenderable8(this.renderer, {
7496
+ const action = new TextRenderable9(this.renderer, {
6753
7497
  id: "omnius-selection-copy",
6754
7498
  content: "COPY",
6755
7499
  height: 1,
@@ -6798,7 +7542,7 @@ var NativeSelectionMenu = class {
6798
7542
  if (!this.panel) return;
6799
7543
  this.panel.backgroundColor = colors.background;
6800
7544
  this.frame?.setColors(colors);
6801
- for (const child of this.panel.getChildren()) if (child instanceof TextRenderable8) {
7545
+ for (const child of this.panel.getChildren()) if (child instanceof TextRenderable9) {
6802
7546
  child.fg = colors.foreground;
6803
7547
  child.bg = colors.background;
6804
7548
  }
@@ -6824,8 +7568,8 @@ var NativeSelectionMenu = class {
6824
7568
  };
6825
7569
 
6826
7570
  // packages/cli/src/tui/native/components/tab-strip.ts
6827
- import { BoxRenderable as BoxRenderable10, CliRenderEvents as CliRenderEvents7, ScrollBoxRenderable as ScrollBoxRenderable7, SliderRenderable, TextAttributes as TextAttributes10, TextRenderable as TextRenderable9 } from "@opentui/core";
6828
- var NativeTabStrip = class extends BoxRenderable10 {
7571
+ import { BoxRenderable as BoxRenderable11, CliRenderEvents as CliRenderEvents7, ScrollBoxRenderable as ScrollBoxRenderable7, SliderRenderable, TextAttributes as TextAttributes10, TextRenderable as TextRenderable10 } from "@opentui/core";
7572
+ var NativeTabStrip = class extends BoxRenderable11 {
6829
7573
  constructor(renderer2, options) {
6830
7574
  super(renderer2, {
6831
7575
  id: options.id,
@@ -6908,7 +7652,7 @@ var NativeTabStrip = class extends BoxRenderable10 {
6908
7652
  for (const action of items) {
6909
7653
  let entry = this.buttons.get(action.id);
6910
7654
  if (!entry) {
6911
- const button = new TextRenderable9(this.renderer, {
7655
+ const button = new TextRenderable10(this.renderer, {
6912
7656
  id: `${this.options.prefix}:${action.id}`,
6913
7657
  content: "",
6914
7658
  height: 1,
@@ -6936,7 +7680,7 @@ var NativeTabStrip = class extends BoxRenderable10 {
6936
7680
  });
6937
7681
  button.on("blurred", () => this.style(entry));
6938
7682
  if (this.options.close) {
6939
- const close2 = new TextRenderable9(this.renderer, {
7683
+ const close2 = new TextRenderable10(this.renderer, {
6940
7684
  id: `${this.options.prefix}-close:${action.id}`,
6941
7685
  content: "×",
6942
7686
  width: 1,
@@ -7071,13 +7815,13 @@ var NativeFrontend = class {
7071
7815
  this.renderer = renderer2;
7072
7816
  this.emit = emit;
7073
7817
  const colors = palette(initialTheme);
7074
- this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map(), mediaViews: /* @__PURE__ */ new Set() };
7818
+ this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map(), mediaViews: /* @__PURE__ */ new Set(), modelViews: /* @__PURE__ */ new Set() };
7075
7819
  this.disposeSelectionContrast = installDefaultSelectionContrast(renderer2);
7076
7820
  this.clipboard = options.clipboard ?? createClipboard({ host: createHostClipboard({ timeoutMs: 1500 }), terminal: createRendererClipboardAdapter(renderer2) });
7077
7821
  this.selectionMenu = new NativeSelectionMenu(renderer2, colors, (text) => {
7078
7822
  void this.copy(text);
7079
7823
  }, () => this.restoreFocus());
7080
- this.root = new BoxRenderable11(renderer2, {
7824
+ this.root = new BoxRenderable12(renderer2, {
7081
7825
  id: "omnius-native",
7082
7826
  width: "100%",
7083
7827
  height: "100%",
@@ -7085,7 +7829,7 @@ var NativeFrontend = class {
7085
7829
  backgroundColor: colors.background
7086
7830
  });
7087
7831
  renderer2.root.add(this.root);
7088
- this.header = new BoxRenderable11(renderer2, {
7832
+ this.header = new BoxRenderable12(renderer2, {
7089
7833
  id: "omnius-header",
7090
7834
  width: "100%",
7091
7835
  minWidth: 0,
@@ -7109,9 +7853,9 @@ var NativeFrontend = class {
7109
7853
  TextAttributes11.DIM
7110
7854
  );
7111
7855
  };
7112
- const headerRow = new BoxRenderable11(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
7856
+ const headerRow = new BoxRenderable12(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
7113
7857
  this.header.add(headerRow);
7114
- this.identityMarker = new TextRenderable10(renderer2, {
7858
+ this.identityMarker = new TextRenderable11(renderer2, {
7115
7859
  id: "omnius-marker",
7116
7860
  content: "■",
7117
7861
  width: 1,
@@ -7121,7 +7865,7 @@ var NativeFrontend = class {
7121
7865
  selectable: false
7122
7866
  });
7123
7867
  headerRow.add(this.identityMarker);
7124
- this.identity = new TextRenderable10(renderer2, {
7868
+ this.identity = new TextRenderable11(renderer2, {
7125
7869
  id: "omnius-identity",
7126
7870
  content: "OMNIUS",
7127
7871
  fg: colors.foreground,
@@ -7159,9 +7903,9 @@ var NativeFrontend = class {
7159
7903
  verticalScrollbarOptions: { visible: false }
7160
7904
  });
7161
7905
  headerRow.add(this.headerActions);
7162
- this.headerPanel = new TextRenderable10(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
7906
+ this.headerPanel = new TextRenderable11(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
7163
7907
  this.header.add(this.headerPanel);
7164
- this.detailButton = new TextRenderable10(renderer2, {
7908
+ this.detailButton = new TextRenderable11(renderer2, {
7165
7909
  id: "omnius-details-toggle",
7166
7910
  content: "+ DETAILS",
7167
7911
  height: 1,
@@ -7202,7 +7946,7 @@ var NativeFrontend = class {
7202
7946
  }, 600);
7203
7947
  this.identityMarkerTimer.unref?.();
7204
7948
  }
7205
- this.sessionRow = new BoxRenderable11(renderer2, {
7949
+ this.sessionRow = new BoxRenderable12(renderer2, {
7206
7950
  id: "omnius-sessions-row",
7207
7951
  width: "100%",
7208
7952
  height: 1,
@@ -7221,7 +7965,7 @@ var NativeFrontend = class {
7221
7965
  close: (id) => this.emitAction("session-close", id)
7222
7966
  });
7223
7967
  this.sessionRow.add(this.sessions);
7224
- this.newSession = new TextRenderable10(renderer2, {
7968
+ this.newSession = new TextRenderable11(renderer2, {
7225
7969
  id: "omnius-session-new",
7226
7970
  content: "+ NEW",
7227
7971
  width: 5,
@@ -7250,7 +7994,7 @@ var NativeFrontend = class {
7250
7994
  this.newSession.on("focused", styleNew);
7251
7995
  this.newSession.on("blurred", styleNew);
7252
7996
  this.sessionRow.add(this.newSession);
7253
- this.sessionPicker = new TextRenderable10(renderer2, {
7997
+ this.sessionPicker = new TextRenderable11(renderer2, {
7254
7998
  id: "omnius-session-picker",
7255
7999
  content: "SESSIONS",
7256
8000
  width: 8,
@@ -7279,9 +8023,9 @@ var NativeFrontend = class {
7279
8023
  this.sessionPicker.on("focused", stylePicker);
7280
8024
  this.sessionPicker.on("blurred", stylePicker);
7281
8025
  this.sessionRow.add(this.sessionPicker);
7282
- this.routeWarning = new TextRenderable10(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes11.BOLD | TextAttributes11.UNDERLINE, visible: false, flexShrink: 0 });
8026
+ this.routeWarning = new TextRenderable11(renderer2, { id: "omnius-route-warning", content: "", width: "100%", wrapMode: "word", fg: colors.warning, attributes: TextAttributes11.BOLD | TextAttributes11.UNDERLINE, visible: false, flexShrink: 0 });
7283
8027
  this.root.add(this.routeWarning);
7284
- this.taskPane = new BoxRenderable11(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
8028
+ this.taskPane = new BoxRenderable12(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
7285
8029
  this.tasks = new NativeBlockList(this.context, this.taskPane);
7286
8030
  this.root.add(this.taskPane);
7287
8031
  this.taskStrip = new NativeTaskStrip(renderer2, colors, (id) => {
@@ -7292,9 +8036,9 @@ var NativeFrontend = class {
7292
8036
  renderer2.requestRender();
7293
8037
  });
7294
8038
  this.root.add(this.taskStrip);
7295
- this.body = new BoxRenderable11(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
8039
+ this.body = new BoxRenderable12(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
7296
8040
  this.root.add(this.body);
7297
- this.transcriptPane = new BoxRenderable11(renderer2, { id: "omnius-transcript-pane", flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, minHeight: 1 });
8041
+ this.transcriptPane = new BoxRenderable12(renderer2, { id: "omnius-transcript-pane", flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, minHeight: 1 });
7298
8042
  this.body.add(this.transcriptPane);
7299
8043
  this.scroll = new ScrollBoxRenderable8(renderer2, {
7300
8044
  id: "omnius-conversation",
@@ -7381,7 +8125,7 @@ var NativeFrontend = class {
7381
8125
  }
7382
8126
  });
7383
8127
  this.body.add(this.instruments);
7384
- this.embeddedPane = new BoxRenderable11(renderer2, {
8128
+ this.embeddedPane = new BoxRenderable12(renderer2, {
7385
8129
  id: "omnius-editor-pane",
7386
8130
  width: "50%",
7387
8131
  flexShrink: 0,
@@ -7392,7 +8136,7 @@ var NativeFrontend = class {
7392
8136
  visible: false
7393
8137
  });
7394
8138
  this.body.add(this.embeddedPane);
7395
- this.liveButton = new TextRenderable10(renderer2, {
8139
+ this.liveButton = new TextRenderable11(renderer2, {
7396
8140
  id: "omnius-live-button",
7397
8141
  content: "↓ Live",
7398
8142
  width: "100%",
@@ -7415,7 +8159,7 @@ var NativeFrontend = class {
7415
8159
  });
7416
8160
  this.liveButton.focusable = true;
7417
8161
  this.transcriptPane.add(this.liveButton);
7418
- this.queue = new TextRenderable10(renderer2, { id: "omnius-steering-queue", content: "", width: "100%", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, visible: false, flexShrink: 0 });
8162
+ this.queue = new TextRenderable11(renderer2, { id: "omnius-steering-queue", content: "", width: "100%", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, visible: false, flexShrink: 0 });
7419
8163
  this.transcriptPane.add(this.queue);
7420
8164
  this.suggestions = new NativeSelectRenderable(renderer2, {
7421
8165
  id: "omnius-suggestions",
@@ -7434,9 +8178,9 @@ var NativeFrontend = class {
7434
8178
  this.suggestions.onActivate = () => this.acceptSuggestion();
7435
8179
  this.suggestions.on("itemSelected", () => this.acceptSuggestion());
7436
8180
  this.transcriptPane.add(this.suggestions);
7437
- this.status = new BoxRenderable11(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
8181
+ this.status = new BoxRenderable12(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
7438
8182
  this.transcriptPane.add(this.status);
7439
- this.footer = new BoxRenderable11(renderer2, {
8183
+ this.footer = new BoxRenderable12(renderer2, {
7440
8184
  id: "omnius-input-box",
7441
8185
  width: "100%",
7442
8186
  flexShrink: 0,
@@ -7445,7 +8189,7 @@ var NativeFrontend = class {
7445
8189
  flexDirection: "column"
7446
8190
  });
7447
8191
  this.root.add(this.footer);
7448
- this.viewTabsRow = new BoxRenderable11(renderer2, {
8192
+ this.viewTabsRow = new BoxRenderable12(renderer2, {
7449
8193
  id: "omnius-view-tabs-row",
7450
8194
  width: "100%",
7451
8195
  height: 1,
@@ -7478,7 +8222,7 @@ var NativeFrontend = class {
7478
8222
  TextAttributes11.DIM
7479
8223
  );
7480
8224
  };
7481
- this.footerRule = new BoxRenderable11(renderer2, {
8225
+ this.footerRule = new BoxRenderable12(renderer2, {
7482
8226
  id: "omnius-input-rule",
7483
8227
  width: "100%",
7484
8228
  height: 1,
@@ -7499,9 +8243,9 @@ var NativeFrontend = class {
7499
8243
  this.emit(event);
7500
8244
  });
7501
8245
  this.footer.add(this.workspaceReferences);
7502
- const inputRow = new BoxRenderable11(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
8246
+ const inputRow = new BoxRenderable12(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
7503
8247
  this.footer.add(inputRow);
7504
- this.prompt = new TextRenderable10(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
8248
+ this.prompt = new TextRenderable11(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
7505
8249
  inputRow.add(this.prompt);
7506
8250
  this.editor = new TextareaRenderable2(renderer2, {
7507
8251
  id: "omnius-input",
@@ -7521,7 +8265,7 @@ var NativeFrontend = class {
7521
8265
  onCursorChange: () => this.editorChanged(),
7522
8266
  onSubmit: () => this.submit()
7523
8267
  });
7524
- this.vimMode = new TextRenderable10(renderer2, {
8268
+ this.vimMode = new TextRenderable11(renderer2, {
7525
8269
  id: "omnius-vim-mode",
7526
8270
  content: "INSERT",
7527
8271
  height: 1,
@@ -7542,17 +8286,17 @@ var NativeFrontend = class {
7542
8286
  this.renderer.requestRender();
7543
8287
  });
7544
8288
  inputRow.add(this.editor);
7545
- this.enhance = new BoxRenderable11(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
8289
+ this.enhance = new BoxRenderable12(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
7546
8290
  inputRow.add(this.enhance);
7547
- this.activity = new TextRenderable10(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
8291
+ this.activity = new TextRenderable11(renderer2, { id: "omnius-activity", content: "", flexGrow: 1, minWidth: 0, height: 1, wrapMode: "none", truncate: true, visible: false, fg: colors.accent });
7548
8292
  this.status.add(this.activity);
7549
- this.media = new BoxRenderable11(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
7550
- this.mediaText = new TextRenderable10(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
8293
+ this.media = new BoxRenderable12(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
8294
+ this.mediaText = new TextRenderable11(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
7551
8295
  this.media.add(this.mediaText);
7552
- this.micTrack = new TextRenderable10(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
8296
+ this.micTrack = new TextRenderable11(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
7553
8297
  this.media.add(this.micTrack);
7554
8298
  this.status.add(this.media);
7555
- this.copyFeedback = new TextRenderable10(renderer2, {
8299
+ this.copyFeedback = new TextRenderable11(renderer2, {
7556
8300
  id: "omnius-copy-feedback",
7557
8301
  content: "",
7558
8302
  height: 1,
@@ -7563,14 +8307,14 @@ var NativeFrontend = class {
7563
8307
  visible: false
7564
8308
  });
7565
8309
  this.status.add(this.copyFeedback);
7566
- this.metricsRow = new BoxRenderable11(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
7567
- this.keyHints = new TextRenderable10(renderer2, { id: "omnius-key-hints", content: "F1 HELP TAB FOCUS", height: 1, flexShrink: 0, attributes: TextAttributes11.DIM, fg: colors.foreground, selectable: false });
8310
+ this.metricsRow = new BoxRenderable12(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
8311
+ this.keyHints = new TextRenderable11(renderer2, { id: "omnius-key-hints", content: "F1 HELP TAB FOCUS", height: 1, flexShrink: 0, attributes: TextAttributes11.DIM, fg: colors.foreground, selectable: false });
7568
8312
  this.metricsRow.add(this.keyHints);
7569
- this.metricsStartActions = new BoxRenderable11(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
8313
+ this.metricsStartActions = new BoxRenderable12(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
7570
8314
  this.metricsRow.add(this.metricsStartActions);
7571
- this.metrics = new TextRenderable10(renderer2, { id: "omnius-metrics", content: "", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, flexShrink: 1, flexGrow: 1, minWidth: 0 });
8315
+ this.metrics = new TextRenderable11(renderer2, { id: "omnius-metrics", content: "", height: 1, wrapMode: "none", truncate: true, fg: colors.dim, flexShrink: 1, flexGrow: 1, minWidth: 0 });
7572
8316
  this.metricsRow.add(this.metrics);
7573
- this.metricsEndActions = new BoxRenderable11(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
8317
+ this.metricsEndActions = new BoxRenderable12(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
7574
8318
  this.metricsRow.add(this.metricsEndActions);
7575
8319
  this.root.add(this.metricsRow);
7576
8320
  this.modals = new NativeModalStack(this.context, () => this.restoreFocus());
@@ -7769,7 +8513,7 @@ var NativeFrontend = class {
7769
8513
  for (const action of actions) {
7770
8514
  const colors = this.context.colors;
7771
8515
  const attributes = action.disabled ? TextAttributes11.DIM : action.active ? TextAttributes11.BOLD | TextAttributes11.UNDERLINE : 0;
7772
- const button = new TextRenderable10(this.renderer, {
8516
+ const button = new TextRenderable11(this.renderer, {
7773
8517
  id: `${prefix}:${action.id}`,
7774
8518
  content: styledText(action.label),
7775
8519
  flexShrink: 0,
@@ -8006,7 +8750,9 @@ var NativeFrontend = class {
8006
8750
  const height = Math.max(1, Math.min(8, this.editor.editorView.getTotalVirtualLineCount(), this.renderer.terminalHeight - 8));
8007
8751
  if (height !== this.editor.height) this.editor.height = height;
8008
8752
  this.layoutTabStrips();
8009
- 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);
8753
+ 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 && !this.modals.active);
8754
+ for (const model of this.context.modelViews ?? []) model.reconcile(this.scroll.viewport, !this.paused && !this.modals.active);
8755
+ this.modals.reconcileMedia();
8010
8756
  const offset2 = this.offset;
8011
8757
  if (offset2 === 0) this.newEntries = 0;
8012
8758
  this.liveButton.visible = offset2 > 0;
@@ -8451,6 +9197,8 @@ var NativeFrontend = class {
8451
9197
  }
8452
9198
  pauseMedia() {
8453
9199
  for (const media of this.context.mediaViews ?? []) media.pausePlayback();
9200
+ for (const model of this.context.modelViews ?? []) model.pauseRendering();
9201
+ this.modals.pauseMedia();
8454
9202
  }
8455
9203
  captureInterfaceState() {
8456
9204
  this.flushLatestInput();
@@ -8515,10 +9263,16 @@ var NativeFrontend = class {
8515
9263
  media.pausePlayback();
8516
9264
  return media.closed;
8517
9265
  });
9266
+ mediaClosed.push(...[...this.context.modelViews ?? []].map((model) => {
9267
+ model.pauseRendering();
9268
+ return model.closed;
9269
+ }));
9270
+ mediaClosed.push(...this.modals.pauseMedia());
8518
9271
  this.modals.destroy();
8519
9272
  this.content.destroy();
8520
9273
  this.tasks.destroy();
8521
9274
  await Promise.allSettled(mediaClosed);
9275
+ await this.modals.settleMedia();
8522
9276
  this.root.destroyRecursively();
8523
9277
  this.context.syntax.destroy();
8524
9278
  await this.clipboard.dispose();
@@ -8663,7 +9417,7 @@ try {
8663
9417
  screenMode: "alternate-screen",
8664
9418
  consoleMode: "disabled",
8665
9419
  externalOutputMode: "passthrough",
8666
- backgroundColor: RGBA3.defaultBackground(),
9420
+ backgroundColor: RGBA4.defaultBackground(),
8667
9421
  openConsoleOnError: false,
8668
9422
  targetFps: 60,
8669
9423
  maxFps: 60,