omnius 1.0.881 → 1.0.882

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
@@ -254,12 +254,12 @@ async function validateMediaFile(path, root) {
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))) 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();
@@ -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";
@@ -738,6 +741,630 @@ 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, 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.NONE, backgroundColor: background2 });
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 });
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, { id: `model3d-surface:${modelId}`, width: 1, height: 12, flexShrink: 0, visible: false, ...pointer });
1084
+ this.surface.focusable = true;
1085
+ this.controls = new BoxRenderable2(renderer2, { id: `model3d-controls:${modelId}`, width: "100%", flexDirection: "row", flexWrap: "wrap", gap: 2, flexShrink: 0 });
1086
+ for (const [id, label3, action] of [
1087
+ ["fit", "Fit", () => this.fit()],
1088
+ ["reset", "Reset", () => this.reset()],
1089
+ ["in", "Zoom +", () => this.zoom(-1)],
1090
+ ["out", "Zoom −", () => this.zoom(1)]
1091
+ ]) {
1092
+ const control = new TextRenderable2(renderer2, {
1093
+ id: `model3d-control:${modelId}:${id}`,
1094
+ content: label3,
1095
+ fg: colors.foreground,
1096
+ selectable: false,
1097
+ onMouseDown: (event) => {
1098
+ if (event.button === 0) {
1099
+ action();
1100
+ event.stopPropagation();
1101
+ }
1102
+ },
1103
+ onKeyDown: (key) => {
1104
+ if (key.eventType !== "release" && (key.name === "return" || key.sequence === " ")) {
1105
+ action();
1106
+ key.preventDefault();
1107
+ key.stopPropagation();
1108
+ }
1109
+ }
1110
+ });
1111
+ control.focusable = true;
1112
+ this.controls.add(control);
1113
+ }
1114
+ this.add(this.location);
1115
+ this.add(this.cpu);
1116
+ this.add(this.surface);
1117
+ this.add(this.status);
1118
+ this.add(this.controls);
1119
+ 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 });
1120
+ this.add(this.hints);
1121
+ renderer2.setFrameCallback(this.frame);
1122
+ }
1123
+ renderer;
1124
+ modelId;
1125
+ colors;
1126
+ options;
1127
+ spec;
1128
+ cameraState = initialModel3DCamera();
1129
+ location;
1130
+ status;
1131
+ cpu;
1132
+ surface;
1133
+ controls;
1134
+ hints;
1135
+ model;
1136
+ edges;
1137
+ scene;
1138
+ camera;
1139
+ engine;
1140
+ loading;
1141
+ drawing;
1142
+ cleanup;
1143
+ resolveClosed;
1144
+ closed;
1145
+ closing = false;
1146
+ active = false;
1147
+ dirty = true;
1148
+ epoch = 0;
1149
+ pixelWidth = 0;
1150
+ pixelHeight = 0;
1151
+ drag;
1152
+ mode = "loading";
1153
+ frame = async () => {
1154
+ this.draw();
1155
+ };
1156
+ update(spec) {
1157
+ if (spec.path !== this.spec.path || spec.format !== this.spec.format) return;
1158
+ this.spec = spec;
1159
+ this.location.content = plainText(spec.displayName ?? spec.path);
1160
+ }
1161
+ applyTheme(colors) {
1162
+ this.colors = colors;
1163
+ this.location.fg = colors.dim;
1164
+ this.status.fg = colors.dim;
1165
+ this.cpu.fg = colors.accent;
1166
+ for (const child of this.controls.getChildren()) if (child instanceof TextRenderable2) child.fg = colors.foreground;
1167
+ this.changed();
1168
+ }
1169
+ async load() {
1170
+ const epoch = this.epoch;
1171
+ try {
1172
+ const bytes = await readCapture(this.spec.path);
1173
+ if (this.closing || epoch !== this.epoch) return;
1174
+ const model = await parseModel3D(bytes, this.spec.format);
1175
+ if (this.closing || epoch !== this.epoch) return;
1176
+ this.model = model;
1177
+ this.edges = model3DWireEdges(model);
1178
+ this.mode = "cpu";
1179
+ this.fit();
1180
+ if ((this.options.mode ?? process.env["OMNIUS_TUI_3D_RENDERER"]) !== "cpu") {
1181
+ try {
1182
+ const THREE = await import("three");
1183
+ if (this.closing) return;
1184
+ this.scene = new THREE.Scene();
1185
+ this.camera = new THREE.PerspectiveCamera(45, 1, 0.01, 100);
1186
+ this.scene.add(new THREE.HemisphereLight(16777215, 2631720, 2));
1187
+ const light = new THREE.DirectionalLight(16777215, 2);
1188
+ light.position.set(3, 4, 5);
1189
+ this.scene.add(light);
1190
+ const geometry = new THREE.BufferGeometry();
1191
+ geometry.setAttribute("position", new THREE.BufferAttribute(model.positions, 3));
1192
+ if (model.triangleCount) {
1193
+ geometry.setIndex(new THREE.BufferAttribute(model.triangles, 1));
1194
+ geometry.computeVertexNormals();
1195
+ this.scene.add(new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 12369084, roughness: 0.75, metalness: 0.05, side: THREE.DoubleSide })));
1196
+ } else if (model.segments.length) {
1197
+ geometry.setIndex(new THREE.BufferAttribute(model.segments, 1));
1198
+ this.scene.add(new THREE.LineSegments(geometry, new THREE.LineBasicMaterial({ color: 12369084 })));
1199
+ } else this.scene.add(new THREE.Points(geometry, new THREE.PointsMaterial({ color: 12369084, size: 0.025 })));
1200
+ 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);
1201
+ await this.engine.init();
1202
+ if (this.closing) return;
1203
+ this.engine.setActiveCamera(this.camera);
1204
+ this.mode = "webgpu";
1205
+ this.surface.visible = true;
1206
+ this.cpu.visible = false;
1207
+ } catch {
1208
+ this.releaseScene();
1209
+ this.engine?.destroy();
1210
+ this.engine = void 0;
1211
+ this.mode = "cpu";
1212
+ }
1213
+ } else this.mode = "cpu";
1214
+ if (!this.closing) this.changed();
1215
+ } catch (error) {
1216
+ if (!this.closing) {
1217
+ this.mode = "error";
1218
+ this.status.content = `3D preview unavailable · ${plainText(error instanceof Error ? error.message : "Unable to read model").slice(0, 200)}`;
1219
+ this.renderer.requestRender();
1220
+ }
1221
+ }
1222
+ }
1223
+ reconcile(viewport, active = true) {
1224
+ if (this.closing || this.isDestroyed) return;
1225
+ let ancestor = this;
1226
+ while (ancestor && active) {
1227
+ active = ancestor.visible;
1228
+ ancestor = ancestor.parent;
1229
+ }
1230
+ this.active = active && this.y + this.height > viewport.y && this.y < viewport.y + viewport.height;
1231
+ if (!this.active) return;
1232
+ const chrome = [this.location, this.status, this.controls, this.hints].reduce((rows, node) => rows + (node.visible ? Math.max(1, node.height) : 0), 0);
1233
+ const width = Math.max(1, Math.min(240, Math.floor(this.width))), height = Math.max(3, Math.min(30, this.renderer.terminalHeight - 7 - chrome));
1234
+ if (width !== this.pixelWidth || height !== this.pixelHeight) {
1235
+ this.pixelWidth = width;
1236
+ this.pixelHeight = height;
1237
+ this.cpu.height = height;
1238
+ this.surface.width = width;
1239
+ this.surface.height = height;
1240
+ this.changed();
1241
+ }
1242
+ if (!this.loading && !this.model && this.mode !== "error") this.loading = this.load();
1243
+ if (this.dirty) this.draw();
1244
+ }
1245
+ changed() {
1246
+ this.dirty = true;
1247
+ if (!this.closing) {
1248
+ this.draw();
1249
+ this.renderer.requestRender();
1250
+ }
1251
+ }
1252
+ draw() {
1253
+ if (!this.active || !this.dirty || !this.model || this.closing || this.drawing) return;
1254
+ this.dirty = false;
1255
+ const model = this.model, width = Math.max(1, this.pixelWidth), height = Math.max(1, this.pixelHeight);
1256
+ const stats = `${model.vertexCount.toLocaleString("en-US")} ${model.vertexCount === 1 ? "vertex" : "vertices"} · ${model.triangleCount.toLocaleString("en-US")} ${model.triangleCount === 1 ? "triangle" : "triangles"}`;
1257
+ if (this.mode === "cpu") {
1258
+ const frame = projectModel3DWireframe(model, this.cameraState, width, height, this.edges);
1259
+ this.cpu.visible = true;
1260
+ this.surface.visible = false;
1261
+ this.cpu.content = frame.text;
1262
+ this.status.content = `CPU wireframe · ${stats}${frame.displayedEdges < frame.totalEdges ? ` · ${frame.displayedEdges}/${frame.totalEdges} edges sampled` : ""}${model.warnings.length ? `
1263
+ ${model.warnings.join(" ")}` : ""}`;
1264
+ } else if (this.mode === "webgpu" && this.engine && this.scene && this.camera) {
1265
+ this.camera.aspect = width / (height * 2);
1266
+ this.camera.position.set(...model3DCameraPosition(this.cameraState));
1267
+ this.camera.lookAt(0, 0, 0);
1268
+ this.camera.updateProjectionMatrix();
1269
+ this.status.content = `WebGPU · ${stats}${model.warnings.length ? `
1270
+ ${model.warnings.join(" ")}` : ""}`;
1271
+ const staging = OptimizedBuffer.create(width, height, this.renderer.widthMethod, { owner: this.renderer.nativeScene, id: `model3d-stage:${this.modelId}` });
1272
+ this.engine.setSize(width, height);
1273
+ this.drawing = this.engine.drawScene(this.scene, staging, 0).then(() => {
1274
+ if (!this.closing && !this.surface.isDestroyed && width === this.pixelWidth && height === this.pixelHeight) this.surface.frameBuffer.drawFrameBuffer(0, 0, staging);
1275
+ else if (!this.closing) this.dirty = true;
1276
+ }).catch(() => {
1277
+ if (!this.closing) {
1278
+ this.releaseScene();
1279
+ this.engine?.destroy();
1280
+ this.engine = void 0;
1281
+ this.mode = "cpu";
1282
+ this.dirty = true;
1283
+ }
1284
+ }).finally(() => {
1285
+ staging.destroy();
1286
+ this.drawing = void 0;
1287
+ if (!this.closing) {
1288
+ this.draw();
1289
+ this.renderer.requestRender();
1290
+ }
1291
+ });
1292
+ }
1293
+ }
1294
+ fit() {
1295
+ this.cameraState = fitModel3D(this.cameraState, Math.max(1, this.pixelWidth) / (Math.max(1, this.pixelHeight) * 2));
1296
+ this.changed();
1297
+ }
1298
+ reset() {
1299
+ this.cameraState = initialModel3DCamera();
1300
+ this.fit();
1301
+ }
1302
+ zoom(direction) {
1303
+ this.cameraState = zoomModel3D(this.cameraState, direction);
1304
+ this.changed();
1305
+ }
1306
+ handleKey(key) {
1307
+ if (!this.active || this.closing || key.eventType === "release") return false;
1308
+ let focused = this.renderer.currentFocusedRenderable;
1309
+ while (focused && focused !== this) focused = focused.parent;
1310
+ if (focused !== this) return false;
1311
+ 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);
1312
+ else if (key.sequence === "+" || key.sequence === "=" || key.sequence === "-") this.zoom(key.sequence === "-" ? 1 : -1);
1313
+ else if (key.name === "f") this.fit();
1314
+ else if (key.name === "r") this.reset();
1315
+ else return false;
1316
+ this.changed();
1317
+ key.preventDefault();
1318
+ key.stopPropagation();
1319
+ return true;
1320
+ }
1321
+ pauseRendering() {
1322
+ this.active = false;
1323
+ this.drag = void 0;
1324
+ }
1325
+ releaseScene() {
1326
+ const scene = this.scene;
1327
+ this.scene = void 0;
1328
+ this.camera = void 0;
1329
+ scene?.traverse((node) => {
1330
+ const mesh = node;
1331
+ mesh.geometry?.dispose();
1332
+ for (const material of Array.isArray(mesh.material) ? mesh.material : mesh.material ? [mesh.material] : []) material.dispose();
1333
+ });
1334
+ }
1335
+ dispose() {
1336
+ if (this.cleanup) return this.cleanup;
1337
+ this.closing = true;
1338
+ this.active = false;
1339
+ this.epoch++;
1340
+ this.renderer.removeFrameCallback(this.frame);
1341
+ this.cleanup = (async () => {
1342
+ try {
1343
+ await this.loading;
1344
+ await this.drawing;
1345
+ } finally {
1346
+ try {
1347
+ this.releaseScene();
1348
+ } finally {
1349
+ try {
1350
+ this.engine?.destroy();
1351
+ } finally {
1352
+ this.engine = void 0;
1353
+ this.model = void 0;
1354
+ this.edges = void 0;
1355
+ this.resolveClosed();
1356
+ }
1357
+ }
1358
+ }
1359
+ })();
1360
+ return this.cleanup;
1361
+ }
1362
+ destroySelf() {
1363
+ void this.dispose();
1364
+ super.destroySelf();
1365
+ }
1366
+ };
1367
+
741
1368
  // packages/cli/src/tui/native/components/code.ts
742
1369
  var aliases = {
743
1370
  js: "javascript",
@@ -972,7 +1599,7 @@ var NativeBlockView = class {
972
1599
  this.inline = inline;
973
1600
  this.spec = spec;
974
1601
  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, {
1602
+ this.root = new BoxRenderable3(context.renderer, {
976
1603
  id: `block:${spec.id}`,
977
1604
  flexDirection: "column",
978
1605
  flexShrink: inline && !this.pixel ? 1 : 0,
@@ -997,6 +1624,7 @@ var NativeBlockView = class {
997
1624
  progress;
998
1625
  image;
999
1626
  media;
1627
+ model3d;
1000
1628
  terminal;
1001
1629
  collapsed = false;
1002
1630
  previousModelCollapsed;
@@ -1016,7 +1644,7 @@ var NativeBlockView = class {
1016
1644
  this.root.marginBottom = 0;
1017
1645
  this.root.flexDirection = spec.kind === "row" ? "row" : "column";
1018
1646
  if (spec.title) {
1019
- this.title = new TextRenderable2(renderer2, {
1647
+ this.title = new TextRenderable3(renderer2, {
1020
1648
  id: `block-title:${spec.id}`,
1021
1649
  content: "",
1022
1650
  fg: colors.foreground,
@@ -1040,7 +1668,7 @@ var NativeBlockView = class {
1040
1668
  nativeControlFocus(this.title, 1);
1041
1669
  this.root.add(this.title);
1042
1670
  }
1043
- this.body = new BoxRenderable2(renderer2, {
1671
+ this.body = new BoxRenderable3(renderer2, {
1044
1672
  id: `block-body:${spec.id}`,
1045
1673
  flexDirection: spec.kind === "row" ? "row" : "column",
1046
1674
  width: this.pixel ? "auto" : "100%",
@@ -1064,6 +1692,10 @@ var NativeBlockView = class {
1064
1692
  this.media = new NativeMediaView(renderer2, spec.id, spec.media, colors, this.context.emit);
1065
1693
  this.context.mediaViews?.add(this.media);
1066
1694
  this.body.add(this.media);
1695
+ } else if (spec.kind === "model3d" && spec.model3d) {
1696
+ this.model3d = new NativeModel3DView(renderer2, spec.id, spec.model3d, colors);
1697
+ this.context.modelViews?.add(this.model3d);
1698
+ this.body.add(this.model3d);
1067
1699
  } else if (spec.kind === "image" && spec.imagePath) {
1068
1700
  this.image = new ImageRenderable2(renderer2, {
1069
1701
  id: `image:${spec.id}`,
@@ -1076,7 +1708,7 @@ var NativeBlockView = class {
1076
1708
  if (this.image) this.image.visible = false;
1077
1709
  if (this.text) {
1078
1710
  this.text.visible = true;
1079
- if (this.text instanceof TextRenderable2) this.text.content = styledText(this.spec.text ?? "Image unavailable");
1711
+ if (this.text instanceof TextRenderable3) this.text.content = styledText(this.spec.text ?? "Image unavailable");
1080
1712
  }
1081
1713
  this.context.emit({ type: "action", action: "image-error", id: spec.id, text: String(error) });
1082
1714
  },
@@ -1086,7 +1718,7 @@ var NativeBlockView = class {
1086
1718
  }
1087
1719
  });
1088
1720
  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 });
1721
+ this.text = new TextRenderable3(renderer2, { id: `image-fallback:${spec.id}`, content: "", visible: false, width: "100%", wrapMode: "word", fg: colors.dim, selectable: true });
1090
1722
  this.body.add(this.text);
1091
1723
  } else if (spec.kind === "markdown") {
1092
1724
  this.text = new MarkdownRenderable(renderer2, {
@@ -1144,7 +1776,7 @@ var NativeBlockView = class {
1144
1776
  });
1145
1777
  this.body.add(this.text);
1146
1778
  } else if (spec.text !== void 0) {
1147
- this.text = new TextRenderable2(renderer2, {
1779
+ this.text = new TextRenderable3(renderer2, {
1148
1780
  id: `text:${spec.id}`,
1149
1781
  content: "",
1150
1782
  width: this.pixel ? "auto" : "100%",
@@ -1157,7 +1789,7 @@ var NativeBlockView = class {
1157
1789
  this.body.add(this.text);
1158
1790
  }
1159
1791
  if (spec.kind === "progress") {
1160
- this.progress = new TextRenderable2(renderer2, {
1792
+ this.progress = new TextRenderable3(renderer2, {
1161
1793
  id: `progress-track:${spec.id}`,
1162
1794
  width: "100%",
1163
1795
  height: 1,
@@ -1171,7 +1803,7 @@ var NativeBlockView = class {
1171
1803
  this.body.add(this.progress);
1172
1804
  }
1173
1805
  this.children = new NativeBlockList(this.context, this.body);
1174
- this.actions = new BoxRenderable2(renderer2, {
1806
+ this.actions = new BoxRenderable3(renderer2, {
1175
1807
  id: `block-actions:${spec.id}`,
1176
1808
  flexDirection: "row",
1177
1809
  gap: 1,
@@ -1185,6 +1817,11 @@ var NativeBlockView = class {
1185
1817
  this.media.destroy();
1186
1818
  this.media = void 0;
1187
1819
  }
1820
+ if (this.model3d) {
1821
+ this.context.modelViews?.delete(this.model3d);
1822
+ this.model3d.destroy();
1823
+ this.model3d = void 0;
1824
+ }
1188
1825
  this.children?.destroy();
1189
1826
  this.markdownSections?.destroy();
1190
1827
  this.markdownSections = void 0;
@@ -1222,7 +1859,7 @@ var NativeBlockView = class {
1222
1859
  this.markdownSections.destroy();
1223
1860
  this.markdownSections = void 0;
1224
1861
  }
1225
- if (this.text instanceof TextRenderable2) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
1862
+ if (this.text instanceof TextRenderable3) this.text.content = styledText(spec.kind === "image" ? spec.text ?? "Image unavailable" : spec.text);
1226
1863
  else if (this.text instanceof MarkdownRenderable || this.text instanceof CodeRenderable) {
1227
1864
  const code = this.text instanceof CodeRenderable ? nativeCodeContent(plainText(spec.text), spec.language) : void 0;
1228
1865
  this.text.content = sections ? "" : code?.content ?? plainText(spec.text);
@@ -1231,6 +1868,7 @@ var NativeBlockView = class {
1231
1868
  else keepMarkdownFallback(this.text);
1232
1869
  }
1233
1870
  if (this.media && spec.media) this.media.update(spec.media);
1871
+ if (this.model3d && spec.model3d) this.model3d.update(spec.model3d);
1234
1872
  if (this.image && spec.imagePath && this.image.source !== spec.imagePath) this.image.source = spec.imagePath;
1235
1873
  if (this.terminal) this.terminal.height = spec.height ?? 14;
1236
1874
  if (this.image) this.image.height = spec.height ?? 12;
@@ -1241,7 +1879,7 @@ var NativeBlockView = class {
1241
1879
  this.actionsSignature = actionsSignature;
1242
1880
  for (const child of [...this.actions.getChildren()]) child.destroy();
1243
1881
  for (const action of spec.actions ?? []) {
1244
- const button = new TextRenderable2(this.context.renderer, {
1882
+ const button = new TextRenderable3(this.context.renderer, {
1245
1883
  id: `block-action:${spec.id}:${action.id}`,
1246
1884
  content: plainText(action.label),
1247
1885
  fg: this.context.colors.foreground,
@@ -1310,11 +1948,12 @@ var NativeBlockView = class {
1310
1948
  this.progress.fg = colors.foreground;
1311
1949
  this.progress.bg = colors.background;
1312
1950
  }
1313
- for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable2) {
1951
+ for (const action of this.actions?.getChildren() ?? []) if (action instanceof TextRenderable3) {
1314
1952
  action.fg = colors.foreground;
1315
1953
  action.bg = colors.background;
1316
1954
  }
1317
1955
  this.media?.applyTheme(colors);
1956
+ this.model3d?.applyTheme(colors);
1318
1957
  this.children?.applyTheme();
1319
1958
  this.markdownSections?.applyTheme();
1320
1959
  }
@@ -1324,6 +1963,11 @@ var NativeBlockView = class {
1324
1963
  this.media.destroy();
1325
1964
  this.media = void 0;
1326
1965
  }
1966
+ if (this.model3d) {
1967
+ this.context.modelViews?.delete(this.model3d);
1968
+ this.model3d.destroy();
1969
+ this.model3d = void 0;
1970
+ }
1327
1971
  this.children?.destroy();
1328
1972
  this.markdownSections?.destroy();
1329
1973
  if (this.terminal) this.context.terminals.delete(this.spec.id);
@@ -1395,13 +2039,13 @@ var NativeBlockList = class {
1395
2039
 
1396
2040
  // packages/cli/src/tui/native/components/modals.ts
1397
2041
  import {
1398
- BoxRenderable as BoxRenderable4,
2042
+ BoxRenderable as BoxRenderable5,
1399
2043
  CliRenderEvents as CliRenderEvents3,
1400
2044
  InputRenderable,
1401
2045
  ScrollBoxRenderable as ScrollBoxRenderable2,
1402
2046
  TextareaRenderable,
1403
2047
  TextAttributes as TextAttributes4,
1404
- TextRenderable as TextRenderable3
2048
+ TextRenderable as TextRenderable4
1405
2049
  } from "@opentui/core";
1406
2050
 
1407
2051
  // packages/cli/src/tui/native/components/select.ts
@@ -1554,7 +2198,7 @@ var NativeSelectRenderable = class extends SelectRenderable {
1554
2198
  };
1555
2199
 
1556
2200
  // 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";
2201
+ import { BoxRenderable as BoxRenderable4, CliRenderEvents as CliRenderEvents2, RenderableEvents as RenderableEvents2, TextAttributes as TextAttributes3 } from "@opentui/core";
1558
2202
  function drawCornerFrame(buffer, panel, colors, progress = 1) {
1559
2203
  const { x, y, width, height } = panel;
1560
2204
  if (width <= 0 || height <= 0) return;
@@ -1593,7 +2237,7 @@ var NativeCornerFrame = class {
1593
2237
  this.colors = colors;
1594
2238
  this.renderer = renderer2;
1595
2239
  this.container = container;
1596
- this.chrome = new BoxRenderable3(renderer2, {
2240
+ this.chrome = new BoxRenderable4(renderer2, {
1597
2241
  id: `${panel.id}:corners`,
1598
2242
  position: "absolute",
1599
2243
  left: 0,
@@ -1808,7 +2452,9 @@ function nativeModalWidth(spec, columns) {
1808
2452
  const blockWidth = (block, depth) => {
1809
2453
  if (depth > 16) return 0;
1810
2454
  const children = (block.children ?? []).slice(0, 256).map((child) => blockWidth(child, depth + 1));
2455
+ const mediaWidth = block.kind === "model3d" ? 96 : block.kind === "media" ? block.media?.kind === "image" ? 96 : 64 : 0;
1811
2456
  return Math.min(limit, Math.max(
2457
+ mediaWidth,
1812
2458
  longest(plainText(block.text)),
1813
2459
  longest(block.title ?? ""),
1814
2460
  actions(block.actions),
@@ -1840,7 +2486,7 @@ var NativeModalView = class {
1840
2486
  this.close = close2;
1841
2487
  this.spec = spec;
1842
2488
  const { renderer: renderer2, colors } = context;
1843
- this.root = new BoxRenderable4(renderer2, {
2489
+ this.root = new BoxRenderable5(renderer2, {
1844
2490
  id: `modal:${spec.id}`,
1845
2491
  position: "absolute",
1846
2492
  top: 0,
@@ -1862,7 +2508,7 @@ var NativeModalView = class {
1862
2508
  }
1863
2509
  }
1864
2510
  });
1865
- this.panel = new BoxRenderable4(renderer2, {
2511
+ this.panel = new BoxRenderable5(renderer2, {
1866
2512
  id: `modal-panel:${spec.id}`,
1867
2513
  flexDirection: "column",
1868
2514
  width: nativeModalWidth(spec, renderer2.terminalWidth),
@@ -1892,10 +2538,10 @@ var NativeModalView = class {
1892
2538
  this.panel.add(this.body);
1893
2539
  styleNativeScrollbars(this.body, colors);
1894
2540
  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 });
2541
+ const header = new BoxRenderable5(renderer2, { id: `modal-heading:${spec.id}`, width: "100%", flexDirection: "row", gap: 1, flexShrink: 0 });
2542
+ this.heading = new TextRenderable4(renderer2, { id: `modal-title:${spec.id}`, content: "", width: 0, flexGrow: 1, flexShrink: 1, wrapMode: "word", fg: colors.foreground });
1897
2543
  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%" });
2544
+ this.headerActions = new BoxRenderable5(renderer2, { id: `modal-header-actions:${spec.id}`, flexDirection: "row", gap: 1, flexShrink: 1, minWidth: 0, maxWidth: "60%" });
1899
2545
  header.add(this.heading);
1900
2546
  header.add(this.headerActions);
1901
2547
  this.panel.add(header, 0);
@@ -1904,11 +2550,11 @@ var NativeModalView = class {
1904
2550
  else if (spec.kind === "input") this.buildInput();
1905
2551
  else if (spec.kind === "document") {
1906
2552
  this.document = this.body;
1907
- const documentBlocks = new BoxRenderable4(renderer2, { id: `modal-document-blocks:${spec.id}`, width: "100%", flexDirection: "column", flexShrink: 0 });
2553
+ const documentBlocks = new BoxRenderable5(renderer2, { id: `modal-document-blocks:${spec.id}`, width: "100%", flexDirection: "column", flexShrink: 0 });
1908
2554
  this.body.add(documentBlocks);
1909
2555
  this.blocks = new NativeBlockList(this.modalContext(), documentBlocks);
1910
2556
  } else {
1911
- this.progress = new TextRenderable3(renderer2, {
2557
+ this.progress = new TextRenderable4(renderer2, {
1912
2558
  id: `modal-progress:${spec.id}`,
1913
2559
  width: "100%",
1914
2560
  height: 1,
@@ -1920,14 +2566,14 @@ var NativeModalView = class {
1920
2566
  selectable: false
1921
2567
  });
1922
2568
  this.body.add(this.progress);
1923
- const progressBlocks = new BoxRenderable4(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
2569
+ const progressBlocks = new BoxRenderable5(renderer2, { id: `modal-progress-blocks:${spec.id}`, flexDirection: "column", width: "100%" });
1924
2570
  this.body.add(progressBlocks);
1925
2571
  this.blocks = new NativeBlockList(this.modalContext(), progressBlocks);
1926
2572
  }
1927
2573
  this.error = this.addText(`modal-error:${spec.id}`);
1928
2574
  this.hints = this.addText(`modal-hints:${spec.id}`, this.panel);
1929
2575
  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 });
2576
+ this.actions = new BoxRenderable5(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", flexWrap: "wrap", flexShrink: 0, gap: 1, width: "100%", marginTop: 0 });
1931
2577
  this.panel.add(this.actions);
1932
2578
  this.update(spec);
1933
2579
  renderer2.on(CliRenderEvents3.RESIZE, this.fit);
@@ -1967,15 +2613,17 @@ var NativeModalView = class {
1967
2613
  optionSignature = "";
1968
2614
  disposed = false;
1969
2615
  revealFocused = false;
2616
+ mediaViews = /* @__PURE__ */ new Set();
2617
+ modelViews = /* @__PURE__ */ new Set();
1970
2618
  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 });
2619
+ const text = new TextRenderable4(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
1972
2620
  parent.add(text);
1973
2621
  return text;
1974
2622
  }
1975
2623
  replaceActions(container, actions, prefix = "") {
1976
2624
  for (const child of [...container.getChildren()]) child.destroy();
1977
2625
  for (const action of actions) {
1978
- const button = new TextRenderable3(this.context.renderer, {
2626
+ const button = new TextRenderable4(this.context.renderer, {
1979
2627
  id: `modal-action:${this.spec.id}:${prefix}${action.id}`,
1980
2628
  content: plainText(action.label),
1981
2629
  minWidth: 0,
@@ -2114,7 +2762,10 @@ var NativeModalView = class {
2114
2762
  this.refreshMask();
2115
2763
  this.refreshOptions(patch.selectedIndex);
2116
2764
  this.blocks?.replace(spec.blocks ?? []);
2117
- if (this.progress) this.progress.content = nativeProgressText(spec.progress, true);
2765
+ if (this.progress) {
2766
+ this.progress.visible = spec.progress !== null;
2767
+ this.progress.content = nativeProgressText(spec.progress ?? void 0, true);
2768
+ }
2118
2769
  const headerActionsSignature = JSON.stringify(spec.headerActions ?? []);
2119
2770
  if (headerActionsSignature !== this.headerActionsSignature) {
2120
2771
  this.headerActionsSignature = headerActionsSignature;
@@ -2229,12 +2880,27 @@ var NativeModalView = class {
2229
2880
  }
2230
2881
  modalContext() {
2231
2882
  const scoped = Object.create(this.context);
2883
+ scoped.mediaViews = this.mediaViews;
2884
+ scoped.modelViews = this.modelViews;
2232
2885
  scoped.emit = (event) => {
2233
2886
  if (event.type === "action") this.emitAction(event.action, event.id);
2234
2887
  else this.context.emit(event);
2235
2888
  };
2236
2889
  return scoped;
2237
2890
  }
2891
+ reconcileMedia(active) {
2892
+ for (const media of this.mediaViews) media.reconcile(this.body.viewport, active && !this.frame.closing);
2893
+ for (const model of this.modelViews) model.reconcile(this.body.viewport, active && !this.frame.closing);
2894
+ }
2895
+ pauseMedia() {
2896
+ return [...[...this.mediaViews].map((media) => {
2897
+ media.pausePlayback();
2898
+ return media.closed;
2899
+ }), ...[...this.modelViews].map((model) => {
2900
+ model.pauseRendering();
2901
+ return model.closed;
2902
+ })];
2903
+ }
2238
2904
  emitAction(action, value) {
2239
2905
  const current = this.current();
2240
2906
  this.context.emit({ type: "modal-action", id: this.spec.id, action, key: current?.item.key, index: current?.index, value });
@@ -2307,7 +2973,8 @@ var NativeModalView = class {
2307
2973
  return consume();
2308
2974
  }
2309
2975
  const focusedId = this.context.renderer.currentFocusedRenderable?.id ?? "";
2310
- if ((key.name === "return" || key.sequence === " ") && /^(?:block-title:|block-action:|modal-action:)/.test(focusedId)) return false;
2976
+ for (const model of this.modelViews) if (model.handleKey(key)) return consume();
2977
+ if ((key.name === "return" || key.sequence === " ") && /^(?:block-title:|block-action:|modal-action:|media-control:|media-preview:|model3d-control:)/.test(focusedId)) return false;
2311
2978
  if (this.spec.kind === "select") {
2312
2979
  if (key.name === "up" || key.name === "down" || key.name === "pageup" || key.name === "pagedown") {
2313
2980
  const amount = key.name.startsWith("page") ? Math.max(1, Math.floor((this.select?.height ?? 5) / (this.select?.showDescription ? 2 : 1))) : 1;
@@ -2428,7 +3095,7 @@ var NativeModalView = class {
2428
3095
  editor.placeholderColor = colors.foreground;
2429
3096
  }
2430
3097
  if (this.secretMask) this.secretMask.fg = colors.foreground;
2431
- for (const action of [...this.actions.getChildren(), ...this.headerActions.getChildren()]) if (action instanceof TextRenderable3) {
3098
+ for (const action of [...this.actions.getChildren(), ...this.headerActions.getChildren()]) if (action instanceof TextRenderable4) {
2432
3099
  action.fg = colors.foreground;
2433
3100
  action.bg = colors.background;
2434
3101
  }
@@ -2487,6 +3154,7 @@ var NativeModalStack = class {
2487
3154
  close(id, result, notify = false) {
2488
3155
  const view = this.byId.get(id);
2489
3156
  if (!view) return;
3157
+ view.pauseMedia();
2490
3158
  view.frame.close(() => {
2491
3159
  const wasActive = view === this.active;
2492
3160
  view.destroy();
@@ -2504,6 +3172,12 @@ var NativeModalStack = class {
2504
3172
  applyTheme() {
2505
3173
  for (const view of this.views) view.applyTheme();
2506
3174
  }
3175
+ reconcileMedia() {
3176
+ for (const view of this.views) view.reconcileMedia(view === this.active);
3177
+ }
3178
+ pauseMedia() {
3179
+ return this.views.flatMap((view) => view.pauseMedia());
3180
+ }
2507
3181
  destroy() {
2508
3182
  this.context.renderer.removePostProcessFn(this.dimUnderlay);
2509
3183
  for (const view of this.views) view.destroy();
@@ -2514,12 +3188,12 @@ var NativeModalStack = class {
2514
3188
 
2515
3189
  // packages/cli/src/tui/native/components/instruments.ts
2516
3190
  import {
2517
- BoxRenderable as BoxRenderable8,
2518
- RGBA as RGBA2,
3191
+ BoxRenderable as BoxRenderable9,
3192
+ RGBA as RGBA3,
2519
3193
  RenderableEvents as RenderableEvents6,
2520
3194
  ScrollBoxRenderable as ScrollBoxRenderable6,
2521
3195
  TextAttributes as TextAttributes8,
2522
- TextRenderable as TextRenderable7
3196
+ TextRenderable as TextRenderable8
2523
3197
  } from "@opentui/core";
2524
3198
 
2525
3199
  // packages/cli/src/tui/native/components/device-history.ts
@@ -2576,12 +3250,12 @@ function deviceHistoryGraph(samples, field, width) {
2576
3250
 
2577
3251
  // packages/cli/src/tui/native/components/task-notes.ts
2578
3252
  import {
2579
- BoxRenderable as BoxRenderable5,
3253
+ BoxRenderable as BoxRenderable6,
2580
3254
  CliRenderEvents as CliRenderEvents4,
2581
3255
  RenderableEvents as RenderableEvents3,
2582
3256
  ScrollBoxRenderable as ScrollBoxRenderable3,
2583
3257
  TextAttributes as TextAttributes5,
2584
- TextRenderable as TextRenderable4
3258
+ TextRenderable as TextRenderable5
2585
3259
  } from "@opentui/core";
2586
3260
 
2587
3261
  // packages/cli/src/tui/native/components/instrument-format.ts
@@ -2654,7 +3328,7 @@ var marks = ["▨", "▧", "▥", "▦"];
2654
3328
  var reducedMotion = () => process.env["OMNIUS_TUI_REDUCE_MOTION"] === "1" || process.env["REDUCE_MOTION"] === "1";
2655
3329
  var safe = (value) => typeof value === "string" ? plainText(value) : "";
2656
3330
  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 });
3331
+ var column = (renderer2, id) => new BoxRenderable6(renderer2, { id, width: "100%", flexDirection: "column", flexShrink: 0, minWidth: 0 });
2658
3332
  function visible(node, renderer2) {
2659
3333
  if (node.width < 1 || node.height < 1 || node.y >= renderer2.terminalHeight || node.y + node.height <= 0) return false;
2660
3334
  for (let current = node; current; current = current.parent) {
@@ -2702,7 +3376,7 @@ var ActiveBlocks = class {
2702
3376
  this.renderer.off(CliRenderEvents4.FRAME, this.sync);
2703
3377
  }
2704
3378
  };
2705
- var Disclosure = class extends TextRenderable4 {
3379
+ var Disclosure = class extends TextRenderable5 {
2706
3380
  constructor(renderer2, id, activate, colors, ruled = false) {
2707
3381
  super(renderer2, {
2708
3382
  id,
@@ -2746,9 +3420,9 @@ var Disclosure = class extends TextRenderable4 {
2746
3420
  open = true;
2747
3421
  label = "";
2748
3422
  base = TextAttributes5.BOLD;
2749
- update(label3, open, attributes = TextAttributes5.BOLD) {
3423
+ update(label3, open2, attributes = TextAttributes5.BOLD) {
2750
3424
  this.label = label3;
2751
- this.open = open;
3425
+ this.open = open2;
2752
3426
  this.base = attributes;
2753
3427
  this.refresh();
2754
3428
  this.style();
@@ -2791,13 +3465,13 @@ function layerStatus(layer) {
2791
3465
  if (["verified", "integrated"].includes(status)) return `REPORTED ${status.toUpperCase()} · AUTHORITY NOT ATTESTED`;
2792
3466
  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
3467
  }
2794
- var NativeTaskNotesView = class extends BoxRenderable5 {
3468
+ var NativeTaskNotesView = class extends BoxRenderable6 {
2795
3469
  constructor(renderer2, colors, reveal) {
2796
3470
  super(renderer2, { id: "omnius-task-notes", width: "100%", minWidth: 0, flexShrink: 0, flexDirection: "column", visible: false });
2797
3471
  this.renderer = renderer2;
2798
3472
  this.colors = colors;
2799
3473
  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" });
3474
+ 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
3475
  const heading = (id, label3, key) => {
2802
3476
  const node = new Disclosure(renderer2, id, () => {
2803
3477
  this.presentation[key] = !this.presentation[key];
@@ -2874,7 +3548,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2874
3548
  const saved = state?.sessionId ? this.sessions.get(state.sessionId) : void 0;
2875
3549
  this.presentation = saved ? { open: saved.open, tasks: saved.tasks, notes: saved.notes, layers: saved.layers } : { open: true, tasks: true, notes: true, layers: true };
2876
3550
  this.disclosures.clear();
2877
- if (saved) for (const [id, open] of Object.entries(saved.disclosures)) this.disclosures.set(id, open);
3551
+ if (saved) for (const [id, open2] of Object.entries(saved.disclosures)) this.disclosures.set(id, open2);
2878
3552
  }
2879
3553
  this.state = state;
2880
3554
  this.visible = !!state;
@@ -2907,7 +3581,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2907
3581
  this.refreshRow(row);
2908
3582
  }, this.colors);
2909
3583
  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 });
3584
+ 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
3585
  const detailButton = new Disclosure(this.renderer, `task:${item.id}:details`, () => {
2912
3586
  const key = `meta:${item.id}`;
2913
3587
  this.disclosures.set(key, !this.disclosures.get(key));
@@ -2971,7 +3645,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2971
3645
  const id = String(note.id);
2972
3646
  let row = this.noteRows.get(id);
2973
3647
  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 });
3648
+ 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
3649
  this.noteRows.set(id, row);
2976
3650
  }
2977
3651
  const text = `□ ${safe(note.category).toUpperCase()} · ${safe(note.content)}${note.file ? `
@@ -2992,7 +3666,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
2992
3666
  const id = safe(layer.id);
2993
3667
  let row = this.layerRows.get(id);
2994
3668
  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 });
3669
+ 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
3670
  this.layerRows.set(id, row);
2997
3671
  }
2998
3672
  const progress = typeof layer.progress === "number" && Number.isFinite(layer.progress) && layer.progress >= 0 && layer.progress <= 1 ? ` · ${Math.round(layer.progress * 100)}%` : "";
@@ -3051,7 +3725,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3051
3725
  if (!state) return;
3052
3726
  this.presentation = { open: state.open, tasks: state.tasks, notes: state.notes, layers: state.layers };
3053
3727
  this.disclosures.clear();
3054
- for (const [id, open] of Object.entries(state.disclosures)) this.disclosures.set(id, open);
3728
+ for (const [id, open2] of Object.entries(state.disclosures)) this.disclosures.set(id, open2);
3055
3729
  for (const row of this.rows.values()) this.refreshRow(row);
3056
3730
  this.applyPresentation();
3057
3731
  }
@@ -3059,7 +3733,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3059
3733
  const targets = [];
3060
3734
  const visit = (node) => {
3061
3735
  if (!node.visible) return;
3062
- if (node instanceof TextRenderable4 && node.focusable) targets.push(node);
3736
+ if (node instanceof TextRenderable5 && node.focusable) targets.push(node);
3063
3737
  for (const child of node.getChildren()) visit(child);
3064
3738
  };
3065
3739
  if (this.visible) visit(this);
@@ -3068,7 +3742,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3068
3742
  applyTheme(colors) {
3069
3743
  this.colors = colors;
3070
3744
  const visit = (node) => {
3071
- if (node instanceof TextRenderable4) {
3745
+ if (node instanceof TextRenderable5) {
3072
3746
  node.fg = colors.foreground;
3073
3747
  node.bg = colors.background;
3074
3748
  }
@@ -3082,7 +3756,7 @@ var NativeTaskNotesView = class extends BoxRenderable5 {
3082
3756
  super.destroySelf();
3083
3757
  }
3084
3758
  };
3085
- var NativeTaskStrip = class extends BoxRenderable5 {
3759
+ var NativeTaskStrip = class extends BoxRenderable6 {
3086
3760
  constructor(renderer2, colors, inspect) {
3087
3761
  super(renderer2, {
3088
3762
  id: "omnius-task-strip",
@@ -3101,7 +3775,7 @@ var NativeTaskStrip = class extends BoxRenderable5 {
3101
3775
  this.renderer = renderer2;
3102
3776
  this.colors = colors;
3103
3777
  this.inspect = inspect;
3104
- this.label = new TextRenderable4(renderer2, {
3778
+ this.label = new TextRenderable5(renderer2, {
3105
3779
  id: "omnius-task-strip:heading",
3106
3780
  content: "TASKS ",
3107
3781
  height: 1,
@@ -3200,7 +3874,7 @@ var NativeTaskStrip = class extends BoxRenderable5 {
3200
3874
  for (const [index, item] of items.entries()) {
3201
3875
  let chip = this.chips.get(item.id);
3202
3876
  if (!chip) {
3203
- const label3 = new TextRenderable4(this.renderer, {
3877
+ const label3 = new TextRenderable5(this.renderer, {
3204
3878
  id: `task-strip:${item.id}`,
3205
3879
  content: "",
3206
3880
  height: 1,
@@ -3280,26 +3954,26 @@ var NativeTaskStrip = class extends BoxRenderable5 {
3280
3954
 
3281
3955
  // packages/cli/src/tui/native/components/work-queue.ts
3282
3956
  import {
3283
- BoxRenderable as BoxRenderable6,
3957
+ BoxRenderable as BoxRenderable7,
3284
3958
  CliRenderEvents as CliRenderEvents5,
3285
3959
  RenderableEvents as RenderableEvents4,
3286
3960
  ScrollBoxRenderable as ScrollBoxRenderable4,
3287
3961
  TextAttributes as TextAttributes6,
3288
- TextRenderable as TextRenderable5
3962
+ TextRenderable as TextRenderable6
3289
3963
  } from "@opentui/core";
3290
3964
  var frames = ["▨", "▧", "▥", "▦"];
3291
3965
  var actionLabels = { queue: "QUEUE", apply: "APPLY", override: "OVERRIDE", cancel: "CANCEL", retry: "RETRY", up: "UP", down: "DOWN" };
3292
3966
  var safe2 = (value) => typeof value === "string" ? plainText(value) : "";
3293
3967
  var label = (value) => safe2(value).replace(/[\r\n\t]+/g, " ");
3294
3968
  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 });
3969
+ var column2 = (renderer2, id) => new BoxRenderable7(renderer2, { id, width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0 });
3296
3970
  var historyStates = /* @__PURE__ */ new Set(["completed", "applied", "failed", "cancelled"]);
3297
3971
  var activeStates = /* @__PURE__ */ new Set(["claiming", "running", "admitted"]);
3298
3972
  var receiptTime = (at) => {
3299
3973
  const date = new Date(typeof at === "number" ? at : NaN);
3300
3974
  return Number.isFinite(date.getTime()) ? date.toISOString() : "";
3301
3975
  };
3302
- var WorkControl = class extends TextRenderable5 {
3976
+ var WorkControl = class extends TextRenderable6 {
3303
3977
  constructor(renderer2, id, activate, colors, disclosure = false, ruled = false, fixedWidth) {
3304
3978
  super(renderer2, {
3305
3979
  id,
@@ -3342,9 +4016,9 @@ var WorkControl = class extends TextRenderable5 {
3342
4016
  open = false;
3343
4017
  caption = "";
3344
4018
  base = 0;
3345
- update(caption, open = false, attributes = 0) {
4019
+ update(caption, open2 = false, attributes = 0) {
3346
4020
  this.caption = caption;
3347
- this.open = open;
4021
+ this.open = open2;
3348
4022
  this.base = attributes;
3349
4023
  this.refresh();
3350
4024
  this.style();
@@ -3361,7 +4035,7 @@ var WorkControl = class extends TextRenderable5 {
3361
4035
  if (this.plainText !== value) this.content = value;
3362
4036
  }
3363
4037
  };
3364
- var NativeWorkQueueView = class extends BoxRenderable6 {
4038
+ var NativeWorkQueueView = class extends BoxRenderable7 {
3365
4039
  constructor(renderer2, colors, onAction, reveal) {
3366
4040
  super(renderer2, { id: "omnius-work-queue", width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0, visible: false });
3367
4041
  this.renderer = renderer2;
@@ -3422,7 +4096,7 @@ var NativeWorkQueueView = class extends BoxRenderable6 {
3422
4096
  frame = 0;
3423
4097
  disposed = false;
3424
4098
  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 });
4099
+ 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
4100
  }
3427
4101
  control(id, activate, disclosure = false, ruled = false, width) {
3428
4102
  const node = new WorkControl(this.renderer, id, activate, this.colors, disclosure, ruled, width);
@@ -3504,22 +4178,22 @@ var NativeWorkQueueView = class extends BoxRenderable6 {
3504
4178
  const metadata2 = this.text(`${id}:metadata`, TextAttributes6.DIM), original = this.text(`${id}:original`);
3505
4179
  details.add(original);
3506
4180
  details.add(metadata2);
3507
- const controls = new BoxRenderable6(this.renderer, { id: `${id}:actions`, width: "100%", minWidth: 0, flexDirection: "row", flexWrap: "wrap", flexShrink: 0 });
4181
+ const controls = new BoxRenderable7(this.renderer, { id: `${id}:actions`, width: "100%", minWidth: 0, flexDirection: "row", flexWrap: "wrap", flexShrink: 0 });
3508
4182
  details.add(controls);
3509
4183
  const row = { item, root, title, details, original, metadata: metadata2, controls, actions: /* @__PURE__ */ new Map() };
3510
4184
  this.rows.set(item.id, row);
3511
4185
  return row;
3512
4186
  }
3513
4187
  refreshRow(row) {
3514
- const item = row.item, open = this.presentation.disclosures[item.id] === true;
4188
+ const item = row.item, open2 = this.presentation.disclosures[item.id] === true;
3515
4189
  const state = label(item.state).replace(/_/g, " ").toUpperCase();
3516
4190
  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
4191
  row.title.update(
3518
4192
  `${mark} ${row.position === void 0 ? "" : `${row.position}. `}${state} · ${label(item.title) || "Untitled request"}`,
3519
- open,
4193
+ open2,
3520
4194
  item.state === "queued" ? TextAttributes6.DIM : item.state === "running" || item.state === "needs_review" ? TextAttributes6.BOLD : 0
3521
4195
  );
3522
- row.details.visible = open;
4196
+ row.details.visible = open2;
3523
4197
  const original = `ORIGINAL
3524
4198
  ${safe2(item.originalText)}`;
3525
4199
  if (row.original.plainText !== original) row.original.content = original;
@@ -3607,7 +4281,7 @@ RUN ${label(receipt.runId)}` : ""}`)
3607
4281
  const targets = [];
3608
4282
  const visit = (node) => {
3609
4283
  if (!node.visible) return;
3610
- if (node instanceof TextRenderable5 && node.focusable) targets.push(node);
4284
+ if (node instanceof TextRenderable6 && node.focusable) targets.push(node);
3611
4285
  for (const child of node.getChildren()) visit(child);
3612
4286
  };
3613
4287
  if (this.visible) visit(this);
@@ -3616,7 +4290,7 @@ RUN ${label(receipt.runId)}` : ""}`)
3616
4290
  applyTheme(colors) {
3617
4291
  this.colors = colors;
3618
4292
  const visit = (node) => {
3619
- if (node instanceof TextRenderable5) {
4293
+ if (node instanceof TextRenderable6) {
3620
4294
  node.fg = colors.foreground;
3621
4295
  node.bg = colors.background;
3622
4296
  }
@@ -3635,11 +4309,11 @@ RUN ${label(receipt.runId)}` : ""}`)
3635
4309
 
3636
4310
  // packages/cli/src/tui/native/components/workspace-files.ts
3637
4311
  import {
3638
- BoxRenderable as BoxRenderable7,
4312
+ BoxRenderable as BoxRenderable8,
3639
4313
  RenderableEvents as RenderableEvents5,
3640
4314
  ScrollBoxRenderable as ScrollBoxRenderable5,
3641
4315
  TextAttributes as TextAttributes7,
3642
- TextRenderable as TextRenderable6
4316
+ TextRenderable as TextRenderable7
3643
4317
  } from "@opentui/core";
3644
4318
  var label2 = (value) => plainText(value).replace(/[\r\n\t]/g, " ");
3645
4319
  var identity = (state) => state ? `${state.projectRoot}\0${state.sessionId ?? ""}` : "";
@@ -3651,7 +4325,7 @@ var activityLabel = (item) => {
3651
4325
  const kind = { read: "R", write: "W", create: "+", delete: "−" }[item.activity.kind];
3652
4326
  return kind + (item.activity.phase === "verified" ? "✓" : item.activity.phase === "failed" ? "!" : "…");
3653
4327
  };
3654
- var FileControl = class extends TextRenderable6 {
4328
+ var FileControl = class extends TextRenderable7 {
3655
4329
  constructor(renderer2, id, colors, activate, ruled = false, width) {
3656
4330
  super(renderer2, {
3657
4331
  id,
@@ -3689,9 +4363,9 @@ var FileControl = class extends TextRenderable6 {
3689
4363
  ruled;
3690
4364
  caption = "";
3691
4365
  open = false;
3692
- setLabel(value, open = false) {
4366
+ setLabel(value, open2 = false) {
3693
4367
  this.caption = value;
3694
- this.open = open;
4368
+ this.open = open2;
3695
4369
  this.refresh();
3696
4370
  this.style();
3697
4371
  }
@@ -3706,7 +4380,7 @@ var FileControl = class extends TextRenderable6 {
3706
4380
  this.content = this.ruled ? formatSectionBanner(this.caption, this.width, this.open) : fitInstrumentText(this.caption, this.width);
3707
4381
  }
3708
4382
  };
3709
- var NativeWorkspaceFilesView = class extends BoxRenderable7 {
4383
+ var NativeWorkspaceFilesView = class extends BoxRenderable8 {
3710
4384
  constructor(renderer2, colors, onAction, reveal) {
3711
4385
  super(renderer2, { id: "omnius-workspace-files", width: "100%", minWidth: 0, flexDirection: "column", flexShrink: 0, visible: false });
3712
4386
  this.renderer = renderer2;
@@ -3715,9 +4389,9 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3715
4389
  this.reveal = reveal;
3716
4390
  this.header = new FileControl(renderer2, "omnius-workspace-files:heading", colors, () => this.setOpen(!this.open), true);
3717
4391
  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 });
4392
+ 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
4393
  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 });
4394
+ const heading = new BoxRenderable8(renderer2, { id: "omnius-workspace-files:location", width: "100%", minWidth: 0, height: 1, flexDirection: "row", gap: 1, flexShrink: 0 });
3721
4395
  this.body.add(heading);
3722
4396
  this.rootLabel = this.text("omnius-workspace-files:root");
3723
4397
  this.rootLabel.width = 0;
@@ -3751,7 +4425,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3751
4425
  contentOptions: { flexDirection: "column", paddingRight: 1 }
3752
4426
  });
3753
4427
  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 });
4428
+ this.rootPages = new BoxRenderable8(renderer2, { id: "omnius-workspace-files:pages", width: "100%", height: 1, flexDirection: "row", gap: 2, flexShrink: 0 });
3755
4429
  this.body.add(this.rootPages);
3756
4430
  this.rootStart = new FileControl(renderer2, "omnius-workspace-files:start", colors, () => {
3757
4431
  this.lastClick = void 0;
@@ -3800,7 +4474,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3800
4474
  layoutMinimum = 0;
3801
4475
  layoutExpanded = false;
3802
4476
  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 });
4477
+ return new TextRenderable7(this.renderer, { id, content: "", width: "100%", minWidth: 0, flexShrink: 0, wrapMode: "word", fg: this.colors.foreground, bg: this.colors.background });
3804
4478
  }
3805
4479
  update(state) {
3806
4480
  if (state === this.state) return;
@@ -3861,7 +4535,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3861
4535
  if (!pagePaths.has(item.path)) return;
3862
4536
  let page = this.pagination.get(item.path);
3863
4537
  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 });
4538
+ 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
4539
  const start = new FileControl(this.renderer, `${root.id}:start`, this.colors, () => {
3866
4540
  this.lastClick = void 0;
3867
4541
  this.action("start", item.path);
@@ -3911,7 +4585,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3911
4585
  }
3912
4586
  }
3913
4587
  createRow(item) {
3914
- const root = new BoxRenderable7(this.renderer, { id: rowId(item.path), width: "100%", minWidth: 0, height: 1, flexShrink: 0, flexDirection: "row" });
4588
+ const root = new BoxRenderable8(this.renderer, { id: rowId(item.path), width: "100%", minWidth: 0, height: 1, flexShrink: 0, flexDirection: "row" });
3915
4589
  const disclosure = new FileControl(this.renderer, `${root.id}:disclosure`, this.colors, () => {
3916
4590
  const current = this.rows.get(item.path)?.item;
3917
4591
  this.lastClick = void 0;
@@ -3922,7 +4596,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
3922
4596
  }, false, 2);
3923
4597
  disclosure.focusable = false;
3924
4598
  root.add(disclosure);
3925
- const name = new TextRenderable6(this.renderer, {
4599
+ const name = new TextRenderable7(this.renderer, {
3926
4600
  id: `${root.id}:name`,
3927
4601
  content: "",
3928
4602
  width: 0,
@@ -4026,12 +4700,12 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
4026
4700
  if (!state) return;
4027
4701
  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
4702
  }
4029
- setOpen(open) {
4030
- this.open = open;
4703
+ setOpen(open2) {
4704
+ this.open = open2;
4031
4705
  this.lastClick = void 0;
4032
4706
  this.applyPresentation();
4033
4707
  this.action("visibility", "");
4034
- if (open) this.reveal?.(this.header);
4708
+ if (open2) this.reveal?.(this.header);
4035
4709
  }
4036
4710
  get isOpen() {
4037
4711
  return this.open;
@@ -4074,7 +4748,7 @@ var NativeWorkspaceFilesView = class extends BoxRenderable7 {
4074
4748
  applyTheme(colors) {
4075
4749
  this.colors = colors;
4076
4750
  const visit = (node) => {
4077
- if (node instanceof TextRenderable6) {
4751
+ if (node instanceof TextRenderable7) {
4078
4752
  node.fg = colors.foreground;
4079
4753
  node.bg = colors.background;
4080
4754
  }
@@ -4151,9 +4825,9 @@ var NativeWorkspaceReferenceRow = class extends ScrollBoxRenderable5 {
4151
4825
  };
4152
4826
 
4153
4827
  // packages/cli/src/tui/native/components/instruments.ts
4154
- var foreground = RGBA2.defaultForeground();
4155
- var background = RGBA2.defaultBackground();
4156
- var SectionBanner = class extends TextRenderable7 {
4828
+ var foreground = RGBA3.defaultForeground();
4829
+ var background = RGBA3.defaultBackground();
4830
+ var SectionBanner = class extends TextRenderable8 {
4157
4831
  constructor(renderer2, title, id, activate) {
4158
4832
  super(renderer2, {
4159
4833
  id,
@@ -4208,7 +4882,7 @@ var SectionBanner = class extends TextRenderable7 {
4208
4882
  if (this.plainText !== next) this.content = next;
4209
4883
  }
4210
4884
  };
4211
- var TelemetryRow = class extends TextRenderable7 {
4885
+ var TelemetryRow = class extends TextRenderable8 {
4212
4886
  constructor(renderer2, label3, id, reserved = 12) {
4213
4887
  super(renderer2, {
4214
4888
  id,
@@ -4254,7 +4928,7 @@ var SignalMeter = class extends TelemetryRow {
4254
4928
  this.update(formatSignalMeter(value, maximum));
4255
4929
  }
4256
4930
  };
4257
- var InstrumentDivider = class extends TextRenderable7 {
4931
+ var InstrumentDivider = class extends TextRenderable8 {
4258
4932
  constructor(renderer2, actions) {
4259
4933
  super(renderer2, {
4260
4934
  id: "omnius-instrument-divider",
@@ -4351,17 +5025,17 @@ var InstrumentDivider = class extends TextRenderable7 {
4351
5025
  this.attributes = this.focused ? TextAttributes8.INVERSE : TextAttributes8.DIM;
4352
5026
  }
4353
5027
  };
4354
- var DeviceMonitor = class extends BoxRenderable8 {
5028
+ var DeviceMonitor = class extends BoxRenderable9 {
4355
5029
  constructor(renderer2, action) {
4356
5030
  super(renderer2, { id: "omnius-devices", width: "100%", height: 5, minHeight: 3, flexShrink: 0, flexDirection: "column", visible: false });
4357
5031
  this.action = action;
4358
5032
  this.heading = new SectionBanner(renderer2, "DEVICES", "omnius-devices:heading", () => this.toggle());
4359
5033
  this.heading.update("DEVICES", false);
4360
5034
  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 });
5035
+ 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
5036
  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 });
5037
+ 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 });
5038
+ 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
5039
  this.add(this.compactLoad);
4366
5040
  this.add(this.compactMemory);
4367
5041
  this.scroll = new ScrollBoxRenderable6(renderer2, {
@@ -4381,7 +5055,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4381
5055
  this.add(this.scroll);
4382
5056
  for (const [key, title] of [["cpu", "CPU"], ["memory", "MEMORY"], ["disk", "DISK"], ["network", "NETWORK"], ["gpu", "GPU"]])
4383
5057
  this.createSection(renderer2, key, title);
4384
- this.notes = new TextRenderable7(renderer2, {
5058
+ this.notes = new TextRenderable8(renderer2, {
4385
5059
  id: "omnius-devices:notes",
4386
5060
  content: "",
4387
5061
  width: "100%",
@@ -4393,7 +5067,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4393
5067
  truncate: true
4394
5068
  });
4395
5069
  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 });
5070
+ const controls = new BoxRenderable9(renderer2, { id: "omnius-devices:controls", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2 });
4397
5071
  this.add(controls);
4398
5072
  this.policy = this.button(renderer2, "omnius-devices:policy", "GPU POLICY", () => {
4399
5073
  if (this.canSelect()) this.action?.("/gpu");
@@ -4422,7 +5096,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4422
5096
  historySource = "";
4423
5097
  rows = /* @__PURE__ */ new Map();
4424
5098
  createSection(renderer2, key, title) {
4425
- const root = new BoxRenderable8(renderer2, {
5099
+ const root = new BoxRenderable9(renderer2, {
4426
5100
  id: `omnius-devices:${key}`,
4427
5101
  width: "100%",
4428
5102
  minWidth: 0,
@@ -4438,7 +5112,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4438
5112
  });
4439
5113
  const header = new SectionBanner(renderer2, title, `${root.id}:heading`, () => this.toggleDeviceSection(key));
4440
5114
  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] });
5115
+ const body = new BoxRenderable9(renderer2, { id: `${root.id}:body`, width: "100%", minWidth: 0, flexShrink: 0, flexDirection: "column", visible: this.sectionState[key] });
4442
5116
  root.add(header);
4443
5117
  root.add(body);
4444
5118
  this.scroll.add(root);
@@ -4449,7 +5123,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4449
5123
  readings.set(label3, row);
4450
5124
  body.add(row);
4451
5125
  }
4452
- const detail = key !== "gpu" ? new TextRenderable7(renderer2, {
5126
+ const detail = key !== "gpu" ? new TextRenderable8(renderer2, {
4453
5127
  id: `${root.id}:detail`,
4454
5128
  content: "",
4455
5129
  width: "100%",
@@ -4529,7 +5203,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4529
5203
  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
5204
  }
4531
5205
  button(renderer2, id, label3, activate) {
4532
- const button = new TextRenderable7(renderer2, {
5206
+ const button = new TextRenderable8(renderer2, {
4533
5207
  id,
4534
5208
  content: label3,
4535
5209
  height: 1,
@@ -4592,7 +5266,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4592
5266
  for (const device of snapshot?.devices ?? []) {
4593
5267
  let row = this.rows.get(device.uuid);
4594
5268
  if (!row) {
4595
- const root = new BoxRenderable8(this._ctx, {
5269
+ const root = new BoxRenderable9(this._ctx, {
4596
5270
  id: `omnius-device:${device.uuid}`,
4597
5271
  width: "100%",
4598
5272
  flexDirection: "column",
@@ -4611,9 +5285,9 @@ var DeviceMonitor = class extends BoxRenderable8 {
4611
5285
  title.on(RenderableEvents6.FOCUSED, () => this.revealReading(title));
4612
5286
  const load = new TelemetryRow(this._ctx, "LOAD", `${root.id}:load`, 16);
4613
5287
  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 });
5288
+ 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
5289
  const loadGraph = plot("load"), memoryGraph = plot("memory"), history = new DeviceHistory();
4616
- const reason = new TextRenderable7(this._ctx, {
5290
+ const reason = new TextRenderable8(this._ctx, {
4617
5291
  id: `${root.id}:reason`,
4618
5292
  content: "",
4619
5293
  width: "100%",
@@ -4757,7 +5431,7 @@ var DeviceMonitor = class extends BoxRenderable8 {
4757
5431
  ];
4758
5432
  }
4759
5433
  };
4760
- var ChannelMap = class extends BoxRenderable8 {
5434
+ var ChannelMap = class extends BoxRenderable9 {
4761
5435
  constructor(renderer2, id, inspect, reveal) {
4762
5436
  super(renderer2, {
4763
5437
  id,
@@ -4789,7 +5463,7 @@ var ChannelMap = class extends BoxRenderable8 {
4789
5463
  if (!this.channels.has(agent.id))
4790
5464
  this.channels.set(agent.id, this.channels.size + 1);
4791
5465
  if (!this.rows.has(agent.id)) {
4792
- const row = new TextRenderable7(this._ctx, {
5466
+ const row = new TextRenderable8(this._ctx, {
4793
5467
  id: `${this.id}:channel:${agent.id}`,
4794
5468
  content: "",
4795
5469
  width: "100%",
@@ -4855,7 +5529,7 @@ var ChannelMap = class extends BoxRenderable8 {
4855
5529
  }
4856
5530
  }
4857
5531
  };
4858
- var InstrumentRail = class extends BoxRenderable8 {
5532
+ var InstrumentRail = class extends BoxRenderable9 {
4859
5533
  workQueue;
4860
5534
  workspaceFiles;
4861
5535
  taskNotes;
@@ -4942,7 +5616,7 @@ var InstrumentRail = class extends BoxRenderable8 {
4942
5616
  () => this.reveal(this.inferenceHeader)
4943
5617
  );
4944
5618
  this.scroll.add(this.inferenceHeader);
4945
- this.inference = new BoxRenderable8(renderer2, {
5619
+ this.inference = new BoxRenderable9(renderer2, {
4946
5620
  id: `${id}:inference-body`,
4947
5621
  width: "100%",
4948
5622
  flexDirection: "column",
@@ -4957,7 +5631,7 @@ var InstrumentRail = class extends BoxRenderable8 {
4957
5631
  };
4958
5632
  addRow(this.inference, "route", "ROUTE");
4959
5633
  addRow(this.inference, "backend", "BACKEND");
4960
- this.model = new TextRenderable7(renderer2, {
5634
+ this.model = new TextRenderable8(renderer2, {
4961
5635
  id: `${id}:model`,
4962
5636
  content: "MODEL --",
4963
5637
  width: "100%",
@@ -4986,7 +5660,7 @@ var InstrumentRail = class extends BoxRenderable8 {
4986
5660
  });
4987
5661
  this.contextHeader.on(RenderableEvents6.FOCUSED, () => this.reveal(this.contextHeader));
4988
5662
  this.inference.add(this.contextHeader);
4989
- this.contextDetails = new BoxRenderable8(renderer2, { id: `${id}:context-detail-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
5663
+ this.contextDetails = new BoxRenderable9(renderer2, { id: `${id}:context-detail-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
4990
5664
  this.inference.add(this.contextDetails);
4991
5665
  this.contextHeader.update("CONTEXT DETAILS", false);
4992
5666
  addRow(this.contextDetails, "messages", "MESSAGES");
@@ -5002,7 +5676,7 @@ var InstrumentRail = class extends BoxRenderable8 {
5002
5676
  this.routeAttemptsHeader.visible = false;
5003
5677
  this.routeAttemptsHeader.on(RenderableEvents6.FOCUSED, () => this.reveal(this.routeAttemptsHeader));
5004
5678
  this.inference.add(this.routeAttemptsHeader);
5005
- this.routeAttempts = new BoxRenderable8(renderer2, { id: `${id}:route-attempt-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
5679
+ this.routeAttempts = new BoxRenderable9(renderer2, { id: `${id}:route-attempt-body`, width: "100%", flexDirection: "column", flexShrink: 0, visible: false });
5006
5680
  this.inference.add(this.routeAttempts);
5007
5681
  this.sessionHeader = new SectionBanner(
5008
5682
  renderer2,
@@ -5015,7 +5689,7 @@ var InstrumentRail = class extends BoxRenderable8 {
5015
5689
  () => this.reveal(this.sessionHeader)
5016
5690
  );
5017
5691
  this.scroll.add(this.sessionHeader);
5018
- this.session = new BoxRenderable8(renderer2, {
5692
+ this.session = new BoxRenderable9(renderer2, {
5019
5693
  id: `${id}:session-body`,
5020
5694
  width: "100%",
5021
5695
  flexDirection: "column",
@@ -5113,9 +5787,9 @@ var InstrumentRail = class extends BoxRenderable8 {
5113
5787
  let row = this.routeAttemptRows[index];
5114
5788
  if (!row) {
5115
5789
  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 });
5790
+ const box = new BoxRenderable9(this.ctx, { id, width: "100%", flexDirection: "column", flexShrink: 0 });
5791
+ const title2 = new TextRenderable8(this.ctx, { id: `${id}:title`, width: "100%", height: 1, content: "", fg: foreground, bg: background, wrapMode: "none", truncate: true, flexShrink: 0 });
5792
+ const detail2 = new TextRenderable8(this.ctx, { id: `${id}:detail`, width: "100%", content: "", fg: foreground, bg: background, attributes: TextAttributes8.DIM, wrapMode: "word", flexShrink: 0 });
5119
5793
  box.add(title2);
5120
5794
  box.add(detail2);
5121
5795
  this.routeAttempts.add(box);
@@ -5138,7 +5812,7 @@ var InstrumentRail = class extends BoxRenderable8 {
5138
5812
  applyTheme(colors) {
5139
5813
  this.backgroundColor = colors.background;
5140
5814
  const visit = (node) => {
5141
- if (node instanceof TextRenderable7) {
5815
+ if (node instanceof TextRenderable8) {
5142
5816
  node.fg = colors.foreground;
5143
5817
  node.bg = colors.background;
5144
5818
  }
@@ -6691,7 +7365,7 @@ var VimComposer = class {
6691
7365
  };
6692
7366
 
6693
7367
  // 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";
7368
+ import { BoxRenderable as BoxRenderable10, CliRenderEvents as CliRenderEvents6, TextAttributes as TextAttributes9, TextRenderable as TextRenderable9 } from "@opentui/core";
6695
7369
  var NativeSelectionMenu = class {
6696
7370
  constructor(renderer2, colors, copy, restoreFocus) {
6697
7371
  this.renderer = renderer2;
@@ -6719,7 +7393,7 @@ var NativeSelectionMenu = class {
6719
7393
  this.selectedText = text;
6720
7394
  this.anchor = { x, y };
6721
7395
  this.previousFocus = this.renderer.currentFocusedRenderable;
6722
- this.overlay = new BoxRenderable9(this.renderer, {
7396
+ this.overlay = new BoxRenderable10(this.renderer, {
6723
7397
  id: "omnius-selection-menu",
6724
7398
  position: "absolute",
6725
7399
  left: 0,
@@ -6735,7 +7409,7 @@ var NativeSelectionMenu = class {
6735
7409
  event.stopPropagation();
6736
7410
  }
6737
7411
  });
6738
- this.panel = new BoxRenderable9(this.renderer, {
7412
+ this.panel = new BoxRenderable10(this.renderer, {
6739
7413
  id: "omnius-selection-menu-panel",
6740
7414
  position: "absolute",
6741
7415
  width: 14,
@@ -6749,7 +7423,7 @@ var NativeSelectionMenu = class {
6749
7423
  event.stopPropagation();
6750
7424
  }
6751
7425
  });
6752
- const action = new TextRenderable8(this.renderer, {
7426
+ const action = new TextRenderable9(this.renderer, {
6753
7427
  id: "omnius-selection-copy",
6754
7428
  content: "COPY",
6755
7429
  height: 1,
@@ -6798,7 +7472,7 @@ var NativeSelectionMenu = class {
6798
7472
  if (!this.panel) return;
6799
7473
  this.panel.backgroundColor = colors.background;
6800
7474
  this.frame?.setColors(colors);
6801
- for (const child of this.panel.getChildren()) if (child instanceof TextRenderable8) {
7475
+ for (const child of this.panel.getChildren()) if (child instanceof TextRenderable9) {
6802
7476
  child.fg = colors.foreground;
6803
7477
  child.bg = colors.background;
6804
7478
  }
@@ -6824,8 +7498,8 @@ var NativeSelectionMenu = class {
6824
7498
  };
6825
7499
 
6826
7500
  // 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 {
7501
+ import { BoxRenderable as BoxRenderable11, CliRenderEvents as CliRenderEvents7, ScrollBoxRenderable as ScrollBoxRenderable7, SliderRenderable, TextAttributes as TextAttributes10, TextRenderable as TextRenderable10 } from "@opentui/core";
7502
+ var NativeTabStrip = class extends BoxRenderable11 {
6829
7503
  constructor(renderer2, options) {
6830
7504
  super(renderer2, {
6831
7505
  id: options.id,
@@ -6908,7 +7582,7 @@ var NativeTabStrip = class extends BoxRenderable10 {
6908
7582
  for (const action of items) {
6909
7583
  let entry = this.buttons.get(action.id);
6910
7584
  if (!entry) {
6911
- const button = new TextRenderable9(this.renderer, {
7585
+ const button = new TextRenderable10(this.renderer, {
6912
7586
  id: `${this.options.prefix}:${action.id}`,
6913
7587
  content: "",
6914
7588
  height: 1,
@@ -6936,7 +7610,7 @@ var NativeTabStrip = class extends BoxRenderable10 {
6936
7610
  });
6937
7611
  button.on("blurred", () => this.style(entry));
6938
7612
  if (this.options.close) {
6939
- const close2 = new TextRenderable9(this.renderer, {
7613
+ const close2 = new TextRenderable10(this.renderer, {
6940
7614
  id: `${this.options.prefix}-close:${action.id}`,
6941
7615
  content: "×",
6942
7616
  width: 1,
@@ -7071,13 +7745,13 @@ var NativeFrontend = class {
7071
7745
  this.renderer = renderer2;
7072
7746
  this.emit = emit;
7073
7747
  const colors = palette(initialTheme);
7074
- this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map(), mediaViews: /* @__PURE__ */ new Set() };
7748
+ this.context = { renderer: renderer2, colors, syntax: SyntaxStyle2.fromStyles({}, renderer2.nativeScene), emit, terminals: /* @__PURE__ */ new Map(), mediaViews: /* @__PURE__ */ new Set(), modelViews: /* @__PURE__ */ new Set() };
7075
7749
  this.disposeSelectionContrast = installDefaultSelectionContrast(renderer2);
7076
7750
  this.clipboard = options.clipboard ?? createClipboard({ host: createHostClipboard({ timeoutMs: 1500 }), terminal: createRendererClipboardAdapter(renderer2) });
7077
7751
  this.selectionMenu = new NativeSelectionMenu(renderer2, colors, (text) => {
7078
7752
  void this.copy(text);
7079
7753
  }, () => this.restoreFocus());
7080
- this.root = new BoxRenderable11(renderer2, {
7754
+ this.root = new BoxRenderable12(renderer2, {
7081
7755
  id: "omnius-native",
7082
7756
  width: "100%",
7083
7757
  height: "100%",
@@ -7085,7 +7759,7 @@ var NativeFrontend = class {
7085
7759
  backgroundColor: colors.background
7086
7760
  });
7087
7761
  renderer2.root.add(this.root);
7088
- this.header = new BoxRenderable11(renderer2, {
7762
+ this.header = new BoxRenderable12(renderer2, {
7089
7763
  id: "omnius-header",
7090
7764
  width: "100%",
7091
7765
  minWidth: 0,
@@ -7109,9 +7783,9 @@ var NativeFrontend = class {
7109
7783
  TextAttributes11.DIM
7110
7784
  );
7111
7785
  };
7112
- const headerRow = new BoxRenderable11(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
7786
+ const headerRow = new BoxRenderable12(renderer2, { id: "omnius-header-row", width: "100%", minWidth: 0, minHeight: 1, flexDirection: "row", gap: 1 });
7113
7787
  this.header.add(headerRow);
7114
- this.identityMarker = new TextRenderable10(renderer2, {
7788
+ this.identityMarker = new TextRenderable11(renderer2, {
7115
7789
  id: "omnius-marker",
7116
7790
  content: "■",
7117
7791
  width: 1,
@@ -7121,7 +7795,7 @@ var NativeFrontend = class {
7121
7795
  selectable: false
7122
7796
  });
7123
7797
  headerRow.add(this.identityMarker);
7124
- this.identity = new TextRenderable10(renderer2, {
7798
+ this.identity = new TextRenderable11(renderer2, {
7125
7799
  id: "omnius-identity",
7126
7800
  content: "OMNIUS",
7127
7801
  fg: colors.foreground,
@@ -7159,9 +7833,9 @@ var NativeFrontend = class {
7159
7833
  verticalScrollbarOptions: { visible: false }
7160
7834
  });
7161
7835
  headerRow.add(this.headerActions);
7162
- this.headerPanel = new TextRenderable10(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
7836
+ this.headerPanel = new TextRenderable11(renderer2, { id: "omnius-header-panel", content: "", width: "100%", wrapMode: "word", visible: false });
7163
7837
  this.header.add(this.headerPanel);
7164
- this.detailButton = new TextRenderable10(renderer2, {
7838
+ this.detailButton = new TextRenderable11(renderer2, {
7165
7839
  id: "omnius-details-toggle",
7166
7840
  content: "+ DETAILS",
7167
7841
  height: 1,
@@ -7202,7 +7876,7 @@ var NativeFrontend = class {
7202
7876
  }, 600);
7203
7877
  this.identityMarkerTimer.unref?.();
7204
7878
  }
7205
- this.sessionRow = new BoxRenderable11(renderer2, {
7879
+ this.sessionRow = new BoxRenderable12(renderer2, {
7206
7880
  id: "omnius-sessions-row",
7207
7881
  width: "100%",
7208
7882
  height: 1,
@@ -7221,7 +7895,7 @@ var NativeFrontend = class {
7221
7895
  close: (id) => this.emitAction("session-close", id)
7222
7896
  });
7223
7897
  this.sessionRow.add(this.sessions);
7224
- this.newSession = new TextRenderable10(renderer2, {
7898
+ this.newSession = new TextRenderable11(renderer2, {
7225
7899
  id: "omnius-session-new",
7226
7900
  content: "+ NEW",
7227
7901
  width: 5,
@@ -7250,7 +7924,7 @@ var NativeFrontend = class {
7250
7924
  this.newSession.on("focused", styleNew);
7251
7925
  this.newSession.on("blurred", styleNew);
7252
7926
  this.sessionRow.add(this.newSession);
7253
- this.sessionPicker = new TextRenderable10(renderer2, {
7927
+ this.sessionPicker = new TextRenderable11(renderer2, {
7254
7928
  id: "omnius-session-picker",
7255
7929
  content: "SESSIONS",
7256
7930
  width: 8,
@@ -7279,9 +7953,9 @@ var NativeFrontend = class {
7279
7953
  this.sessionPicker.on("focused", stylePicker);
7280
7954
  this.sessionPicker.on("blurred", stylePicker);
7281
7955
  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 });
7956
+ 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
7957
  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 });
7958
+ this.taskPane = new BoxRenderable12(renderer2, { id: "omnius-tasks", width: "100%", flexDirection: "column", maxHeight: 6, flexShrink: 0, overflow: "hidden", visible: false });
7285
7959
  this.tasks = new NativeBlockList(this.context, this.taskPane);
7286
7960
  this.root.add(this.taskPane);
7287
7961
  this.taskStrip = new NativeTaskStrip(renderer2, colors, (id) => {
@@ -7292,9 +7966,9 @@ var NativeFrontend = class {
7292
7966
  renderer2.requestRender();
7293
7967
  });
7294
7968
  this.root.add(this.taskStrip);
7295
- this.body = new BoxRenderable11(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
7969
+ this.body = new BoxRenderable12(renderer2, { id: "omnius-body", width: "100%", flexDirection: "row", flexGrow: 1, minHeight: 1 });
7296
7970
  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 });
7971
+ this.transcriptPane = new BoxRenderable12(renderer2, { id: "omnius-transcript-pane", flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, minHeight: 1 });
7298
7972
  this.body.add(this.transcriptPane);
7299
7973
  this.scroll = new ScrollBoxRenderable8(renderer2, {
7300
7974
  id: "omnius-conversation",
@@ -7381,7 +8055,7 @@ var NativeFrontend = class {
7381
8055
  }
7382
8056
  });
7383
8057
  this.body.add(this.instruments);
7384
- this.embeddedPane = new BoxRenderable11(renderer2, {
8058
+ this.embeddedPane = new BoxRenderable12(renderer2, {
7385
8059
  id: "omnius-editor-pane",
7386
8060
  width: "50%",
7387
8061
  flexShrink: 0,
@@ -7392,7 +8066,7 @@ var NativeFrontend = class {
7392
8066
  visible: false
7393
8067
  });
7394
8068
  this.body.add(this.embeddedPane);
7395
- this.liveButton = new TextRenderable10(renderer2, {
8069
+ this.liveButton = new TextRenderable11(renderer2, {
7396
8070
  id: "omnius-live-button",
7397
8071
  content: "↓ Live",
7398
8072
  width: "100%",
@@ -7415,7 +8089,7 @@ var NativeFrontend = class {
7415
8089
  });
7416
8090
  this.liveButton.focusable = true;
7417
8091
  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 });
8092
+ 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
8093
  this.transcriptPane.add(this.queue);
7420
8094
  this.suggestions = new NativeSelectRenderable(renderer2, {
7421
8095
  id: "omnius-suggestions",
@@ -7434,9 +8108,9 @@ var NativeFrontend = class {
7434
8108
  this.suggestions.onActivate = () => this.acceptSuggestion();
7435
8109
  this.suggestions.on("itemSelected", () => this.acceptSuggestion());
7436
8110
  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 });
8111
+ this.status = new BoxRenderable12(renderer2, { id: "omnius-live-status", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 2, visible: false });
7438
8112
  this.transcriptPane.add(this.status);
7439
- this.footer = new BoxRenderable11(renderer2, {
8113
+ this.footer = new BoxRenderable12(renderer2, {
7440
8114
  id: "omnius-input-box",
7441
8115
  width: "100%",
7442
8116
  flexShrink: 0,
@@ -7445,7 +8119,7 @@ var NativeFrontend = class {
7445
8119
  flexDirection: "column"
7446
8120
  });
7447
8121
  this.root.add(this.footer);
7448
- this.viewTabsRow = new BoxRenderable11(renderer2, {
8122
+ this.viewTabsRow = new BoxRenderable12(renderer2, {
7449
8123
  id: "omnius-view-tabs-row",
7450
8124
  width: "100%",
7451
8125
  height: 1,
@@ -7478,7 +8152,7 @@ var NativeFrontend = class {
7478
8152
  TextAttributes11.DIM
7479
8153
  );
7480
8154
  };
7481
- this.footerRule = new BoxRenderable11(renderer2, {
8155
+ this.footerRule = new BoxRenderable12(renderer2, {
7482
8156
  id: "omnius-input-rule",
7483
8157
  width: "100%",
7484
8158
  height: 1,
@@ -7499,9 +8173,9 @@ var NativeFrontend = class {
7499
8173
  this.emit(event);
7500
8174
  });
7501
8175
  this.footer.add(this.workspaceReferences);
7502
- const inputRow = new BoxRenderable11(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
8176
+ const inputRow = new BoxRenderable12(renderer2, { id: "omnius-input-row", width: "100%", flexDirection: "row", gap: 1 });
7503
8177
  this.footer.add(inputRow);
7504
- this.prompt = new TextRenderable10(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
8178
+ this.prompt = new TextRenderable11(renderer2, { id: "omnius-prompt", content: "❯", fg: colors.accent, wrapMode: "none", selectable: false, flexShrink: 0 });
7505
8179
  inputRow.add(this.prompt);
7506
8180
  this.editor = new TextareaRenderable2(renderer2, {
7507
8181
  id: "omnius-input",
@@ -7521,7 +8195,7 @@ var NativeFrontend = class {
7521
8195
  onCursorChange: () => this.editorChanged(),
7522
8196
  onSubmit: () => this.submit()
7523
8197
  });
7524
- this.vimMode = new TextRenderable10(renderer2, {
8198
+ this.vimMode = new TextRenderable11(renderer2, {
7525
8199
  id: "omnius-vim-mode",
7526
8200
  content: "INSERT",
7527
8201
  height: 1,
@@ -7542,17 +8216,17 @@ var NativeFrontend = class {
7542
8216
  this.renderer.requestRender();
7543
8217
  });
7544
8218
  inputRow.add(this.editor);
7545
- this.enhance = new BoxRenderable11(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
8219
+ this.enhance = new BoxRenderable12(renderer2, { id: "omnius-enhance", flexDirection: "row", gap: 1, flexShrink: 0, visible: false });
7546
8220
  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 });
8221
+ 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
8222
  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 });
8223
+ this.media = new BoxRenderable12(renderer2, { id: "omnius-media", height: 1, minWidth: 0, flexShrink: 1, flexDirection: "row", gap: 1, visible: false });
8224
+ this.mediaText = new TextRenderable11(renderer2, { id: "omnius-media-text", content: "", flexGrow: 1, minWidth: 0, wrapMode: "none", truncate: true, fg: colors.accent });
7551
8225
  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 });
8226
+ this.micTrack = new TextRenderable11(renderer2, { id: "omnius-mic-meter", width: 12, height: 1, flexShrink: 0, fg: colors.foreground, visible: false });
7553
8227
  this.media.add(this.micTrack);
7554
8228
  this.status.add(this.media);
7555
- this.copyFeedback = new TextRenderable10(renderer2, {
8229
+ this.copyFeedback = new TextRenderable11(renderer2, {
7556
8230
  id: "omnius-copy-feedback",
7557
8231
  content: "",
7558
8232
  height: 1,
@@ -7563,14 +8237,14 @@ var NativeFrontend = class {
7563
8237
  visible: false
7564
8238
  });
7565
8239
  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 });
8240
+ this.metricsRow = new BoxRenderable12(renderer2, { id: "omnius-metrics-row", width: "100%", height: 1, flexShrink: 0, flexDirection: "row", gap: 1 });
8241
+ 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
8242
  this.metricsRow.add(this.keyHints);
7569
- this.metricsStartActions = new BoxRenderable11(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
8243
+ this.metricsStartActions = new BoxRenderable12(renderer2, { id: "omnius-metrics-start-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
7570
8244
  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 });
8245
+ 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
8246
  this.metricsRow.add(this.metrics);
7573
- this.metricsEndActions = new BoxRenderable11(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
8247
+ this.metricsEndActions = new BoxRenderable12(renderer2, { id: "omnius-metrics-end-actions", flexDirection: "row", gap: 1, flexShrink: 0 });
7574
8248
  this.metricsRow.add(this.metricsEndActions);
7575
8249
  this.root.add(this.metricsRow);
7576
8250
  this.modals = new NativeModalStack(this.context, () => this.restoreFocus());
@@ -7769,7 +8443,7 @@ var NativeFrontend = class {
7769
8443
  for (const action of actions) {
7770
8444
  const colors = this.context.colors;
7771
8445
  const attributes = action.disabled ? TextAttributes11.DIM : action.active ? TextAttributes11.BOLD | TextAttributes11.UNDERLINE : 0;
7772
- const button = new TextRenderable10(this.renderer, {
8446
+ const button = new TextRenderable11(this.renderer, {
7773
8447
  id: `${prefix}:${action.id}`,
7774
8448
  content: styledText(action.label),
7775
8449
  flexShrink: 0,
@@ -8006,7 +8680,9 @@ var NativeFrontend = class {
8006
8680
  const height = Math.max(1, Math.min(8, this.editor.editorView.getTotalVirtualLineCount(), this.renderer.terminalHeight - 8));
8007
8681
  if (height !== this.editor.height) this.editor.height = height;
8008
8682
  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);
8683
+ 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);
8684
+ for (const model of this.context.modelViews ?? []) model.reconcile(this.scroll.viewport, !this.paused && !this.modals.active);
8685
+ this.modals.reconcileMedia();
8010
8686
  const offset2 = this.offset;
8011
8687
  if (offset2 === 0) this.newEntries = 0;
8012
8688
  this.liveButton.visible = offset2 > 0;
@@ -8451,6 +9127,8 @@ var NativeFrontend = class {
8451
9127
  }
8452
9128
  pauseMedia() {
8453
9129
  for (const media of this.context.mediaViews ?? []) media.pausePlayback();
9130
+ for (const model of this.context.modelViews ?? []) model.pauseRendering();
9131
+ this.modals.pauseMedia();
8454
9132
  }
8455
9133
  captureInterfaceState() {
8456
9134
  this.flushLatestInput();
@@ -8515,6 +9193,11 @@ var NativeFrontend = class {
8515
9193
  media.pausePlayback();
8516
9194
  return media.closed;
8517
9195
  });
9196
+ mediaClosed.push(...[...this.context.modelViews ?? []].map((model) => {
9197
+ model.pauseRendering();
9198
+ return model.closed;
9199
+ }));
9200
+ mediaClosed.push(...this.modals.pauseMedia());
8518
9201
  this.modals.destroy();
8519
9202
  this.content.destroy();
8520
9203
  this.tasks.destroy();
@@ -8663,7 +9346,7 @@ try {
8663
9346
  screenMode: "alternate-screen",
8664
9347
  consoleMode: "disabled",
8665
9348
  externalOutputMode: "passthrough",
8666
- backgroundColor: RGBA3.defaultBackground(),
9349
+ backgroundColor: RGBA4.defaultBackground(),
8667
9350
  openConsoleOnError: false,
8668
9351
  targetFps: 60,
8669
9352
  maxFps: 60,