@h-rig/cli 0.0.6-alpha.81 → 0.0.6-alpha.83

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.
Files changed (100) hide show
  1. package/dist/bin/build-rig-binaries.js +40 -8
  2. package/dist/bin/rig.js +2807 -1443
  3. package/dist/src/app/board.js +62 -13
  4. package/dist/src/app-opentui/adapters/command.d.ts +2 -0
  5. package/dist/src/app-opentui/adapters/command.js +329 -0
  6. package/dist/src/app-opentui/adapters/common.js +2 -2
  7. package/dist/src/app-opentui/adapters/doctor.js +63 -14
  8. package/dist/src/app-opentui/adapters/fleet.js +84 -20
  9. package/dist/src/app-opentui/adapters/inbox.js +83 -19
  10. package/dist/src/app-opentui/adapters/init.js +87 -23
  11. package/dist/src/app-opentui/adapters/pi-attach.js +102 -36
  12. package/dist/src/app-opentui/adapters/run-detail.js +83 -19
  13. package/dist/src/app-opentui/adapters/server.js +100 -23
  14. package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
  15. package/dist/src/app-opentui/adapters/tasks.js +742 -94
  16. package/dist/src/app-opentui/bootstrap.d.ts +1 -6
  17. package/dist/src/app-opentui/bootstrap.js +13776 -12369
  18. package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
  19. package/dist/src/app-opentui/command-pty-host.js +248 -0
  20. package/dist/src/app-opentui/events.js +1 -1
  21. package/dist/src/app-opentui/focus-manager.d.ts +14 -0
  22. package/dist/src/app-opentui/focus-manager.js +24 -0
  23. package/dist/src/app-opentui/index.js +3806 -2878
  24. package/dist/src/app-opentui/intent.js +154 -48
  25. package/dist/src/app-opentui/keymap.d.ts +20 -0
  26. package/dist/src/app-opentui/keymap.js +707 -0
  27. package/dist/src/app-opentui/launch-routing.d.ts +16 -0
  28. package/dist/src/app-opentui/launch-routing.js +55 -0
  29. package/dist/src/app-opentui/layout.js +2 -2
  30. package/dist/src/app-opentui/pi-host-child.js +66 -16
  31. package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
  32. package/dist/src/app-opentui/pi-pty-host.js +15 -13
  33. package/dist/src/app-opentui/registry.js +3228 -2396
  34. package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
  35. package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
  36. package/dist/src/app-opentui/render/graphics.d.ts +1 -1
  37. package/dist/src/app-opentui/render/graphics.js +131 -16
  38. package/dist/src/app-opentui/render/image-visual-layer-worker.js +413 -958
  39. package/dist/src/app-opentui/render/image-visual-layer.d.ts +19 -10
  40. package/dist/src/app-opentui/render/image-visual-layer.js +391 -357
  41. package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
  42. package/dist/src/app-opentui/render/panel-layout.js +48 -0
  43. package/dist/src/app-opentui/render/panels.d.ts +2 -0
  44. package/dist/src/app-opentui/render/panels.js +62 -29
  45. package/dist/src/app-opentui/render/preloader.d.ts +10 -0
  46. package/dist/src/app-opentui/render/preloader.js +163 -0
  47. package/dist/src/app-opentui/render/scene.d.ts +3 -0
  48. package/dist/src/app-opentui/render/scene.js +12 -0
  49. package/dist/src/app-opentui/render/text.js +5 -1
  50. package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
  51. package/dist/src/app-opentui/render/type-bar.js +51 -16
  52. package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
  53. package/dist/src/app-opentui/runtime-resources.js +62 -0
  54. package/dist/src/app-opentui/runtime.js +2329 -1512
  55. package/dist/src/app-opentui/scenes/command.d.ts +3 -0
  56. package/dist/src/app-opentui/scenes/command.js +103 -0
  57. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
  58. package/dist/src/app-opentui/scenes/doctor.js +53 -14
  59. package/dist/src/app-opentui/scenes/error.js +44 -14
  60. package/dist/src/app-opentui/scenes/fleet.js +40 -21
  61. package/dist/src/app-opentui/scenes/handoff.js +142 -27
  62. package/dist/src/app-opentui/scenes/help.js +63 -48
  63. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
  64. package/dist/src/app-opentui/scenes/inbox.js +64 -17
  65. package/dist/src/app-opentui/scenes/init.d.ts +2 -1
  66. package/dist/src/app-opentui/scenes/init.js +62 -21
  67. package/dist/src/app-opentui/scenes/main.d.ts +2 -1
  68. package/dist/src/app-opentui/scenes/main.js +80 -24
  69. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
  70. package/dist/src/app-opentui/scenes/run-detail.js +39 -25
  71. package/dist/src/app-opentui/scenes/server.d.ts +2 -1
  72. package/dist/src/app-opentui/scenes/server.js +71 -20
  73. package/dist/src/app-opentui/scenes/tasks.js +44 -22
  74. package/dist/src/app-opentui/state.js +70 -30
  75. package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
  76. package/dist/src/app-opentui/terminal-capabilities.js +15 -0
  77. package/dist/src/app-opentui/types.d.ts +10 -2
  78. package/dist/src/commands/_doctor-checks.js +62 -13
  79. package/dist/src/commands/_operator-view.js +63 -13
  80. package/dist/src/commands/_pi-frontend.js +63 -13
  81. package/dist/src/commands/_preflight.js +64 -14
  82. package/dist/src/commands/_server-client.js +82 -18
  83. package/dist/src/commands/_snapshot-upload.js +62 -13
  84. package/dist/src/commands/connect.js +7 -1
  85. package/dist/src/commands/doctor.js +62 -13
  86. package/dist/src/commands/github.js +144 -23
  87. package/dist/src/commands/inbox.js +62 -13
  88. package/dist/src/commands/init.js +82 -18
  89. package/dist/src/commands/inspect.js +62 -13
  90. package/dist/src/commands/run.js +66 -13
  91. package/dist/src/commands/server.js +69 -14
  92. package/dist/src/commands/setup.js +62 -13
  93. package/dist/src/commands/stats.js +62 -13
  94. package/dist/src/commands/task-run-driver.js +62 -13
  95. package/dist/src/commands/task.js +65 -14
  96. package/dist/src/commands.js +227 -92
  97. package/dist/src/index.js +234 -99
  98. package/package.json +8 -9
  99. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
  100. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1480
@@ -1,48 +1,141 @@
1
1
  // @bun
2
- var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true,
12
- configurable: true,
13
- set: __exportSetter.bind(all, name)
14
- });
15
- };
16
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
2
+ // packages/cli/src/app-opentui/render/image-visual-layer-worker.ts
3
+ import { parentPort } from "worker_threads";
17
4
 
18
5
  // packages/cli/src/app-opentui/render/image-visual-layer.ts
19
6
  import { Worker } from "worker_threads";
20
7
  import { deflateSync } from "zlib";
21
- import { allocateImageId, deleteKittyImage, getCapabilities } from "@earendil-works/pi-tui";
8
+ import { allocateImageId, deleteKittyImage } from "@earendil-works/pi-tui";
9
+
10
+ // packages/cli/src/app-opentui/terminal-capabilities.ts
11
+ import { getCapabilities } from "@earendil-works/pi-tui";
12
+ function getRigTerminalCapabilities() {
13
+ const imageProtocol = getCapabilities().images;
14
+ const supportsKittyImages = imageProtocol === "kitty";
15
+ return {
16
+ imageProtocol,
17
+ supportsKittyImages,
18
+ visualMode: supportsKittyImages ? "kitty-images" : "framebuffer-fallback"
19
+ };
20
+ }
21
+
22
+ // packages/cli/src/app-opentui/render/ascii-fleet.ts
23
+ var LEAD_DRONE = [
24
+ " .-=-. .-=-. ",
25
+ " ( !!! ) ( !!! ) ",
26
+ " '-=-'._ _.'-=-' ",
27
+ " '._ _.' ",
28
+ " '=$$$$$$$=.' ",
29
+ " =$$$$$$$$$$$= ",
30
+ " $$$@@@@@@@@@@$$$ ",
31
+ " $$$@@ @@$$$ ",
32
+ " $$@ ? @$$$ ",
33
+ " $$$@ '-' @$$$ ",
34
+ " $$$@@ @@$$$ ",
35
+ " $$$@@@@@@@@@@$$$ ",
36
+ " =$$$$$$$$$$$= ",
37
+ " '=$$$$$$$=.' ",
38
+ " _.' '._ ",
39
+ " .-=-.' '.-=-. ",
40
+ " ( !!! ) ( !!! ) ",
41
+ " '-=-' '-=-' "
42
+ ];
43
+ var MINI_DRONE = [
44
+ "(!!!) (!!!)",
45
+ " \\%==%/ ",
46
+ " %%?%% ",
47
+ " /%==%\\ ",
48
+ "(!!!) (!!!)"
49
+ ];
50
+ var ROTORS = ["---", "\\\\\\", "|||", "///"];
51
+ var FLEET_GRID_WIDTH = 52;
52
+ var FLEET_GRID_HEIGHT = 27;
53
+ var FLEET = [
54
+ { art: MINI_DRONE, x: 0, y: 2, amp: 1, speed: 0.05, phase: 0, dx: 1, driftSpeed: 0.02 },
55
+ { art: MINI_DRONE, x: 39, y: 2, amp: 1, speed: 0.044, phase: 2.1, dx: 1, driftSpeed: 0.017 },
56
+ { art: MINI_DRONE, x: 1, y: 20, amp: 1, speed: 0.048, phase: 4.2, dx: 1, driftSpeed: 0.023 },
57
+ { art: MINI_DRONE, x: 38, y: 20, amp: 1, speed: 0.041, phase: 1.3, dx: 1, driftSpeed: 0.015 },
58
+ { art: LEAD_DRONE, x: 10, y: 5, amp: 2, speed: 0.04, phase: 0, dx: 0, driftSpeed: 0.011 }
59
+ ];
60
+ function fleetRows(tick, options = {}) {
61
+ const animate = options.animate ?? true;
62
+ const t = animate ? tick : 0;
63
+ const blade = ROTORS[Math.floor(t / 4) % ROTORS.length];
64
+ const pulse = Math.sin(t * 0.07);
65
+ const eye = pulse > 0.45 ? "@" : pulse > -0.1 ? "o" : ".";
66
+ const grid = Array.from({ length: FLEET_GRID_HEIGHT }, () => Array.from({ length: FLEET_GRID_WIDTH }, () => " "));
67
+ for (const drone of FLEET) {
68
+ const yOffset = drone.y + Math.round(drone.amp * Math.sin(t * drone.speed + drone.phase));
69
+ const xOffset = drone.x + Math.round(drone.dx * Math.sin(t * drone.driftSpeed + drone.phase));
70
+ for (let row = 0;row < drone.art.length; row += 1) {
71
+ const source = drone.art[row];
72
+ const targetY = yOffset + row;
73
+ if (targetY < 0 || targetY >= FLEET_GRID_HEIGHT)
74
+ continue;
75
+ for (let col = 0;col < source.length; col += 1) {
76
+ let char = source[col];
77
+ if (char === " ")
78
+ continue;
79
+ const targetX = xOffset + col;
80
+ if (targetX < 0 || targetX >= FLEET_GRID_WIDTH)
81
+ continue;
82
+ if (source.slice(col, col + 3) === "!!!") {
83
+ for (let rotorIndex = 0;rotorIndex < 3; rotorIndex += 1) {
84
+ if (targetX + rotorIndex < FLEET_GRID_WIDTH)
85
+ grid[targetY][targetX + rotorIndex] = blade[rotorIndex];
86
+ }
87
+ col += 2;
88
+ continue;
89
+ }
90
+ if (char === "?")
91
+ char = eye;
92
+ grid[targetY][targetX] = char;
93
+ }
94
+ }
95
+ }
96
+ return grid.map((row) => row.join("").replace(/\s+$/, ""));
97
+ }
98
+
99
+ // packages/cli/src/app-opentui/render/panel-layout.ts
100
+ function panelPixelPlacement(layout, panel, size, minPixels = 8) {
101
+ const cellW = size.width / layout.width;
102
+ const cellH = size.height / layout.height;
103
+ const leftCells = layout.centerLeft + (panel.left ?? 0);
104
+ const topCells = layout.centerTop + panel.top;
105
+ const widthCells = panel.width ?? layout.centerWidth;
106
+ const heightCells = panel.height;
107
+ const left = Math.round(leftCells * cellW);
108
+ const top = Math.round(topCells * cellH);
109
+ const width = Math.round(widthCells * cellW);
110
+ const height = Math.round(heightCells * cellH);
111
+ return width > minPixels && height > minPixels ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
112
+ }
113
+
114
+ // packages/cli/src/app-opentui/render/image-visual-layer.ts
115
+ var BG = [7, 8, 9, 255];
116
+ var PANEL = [16, 17, 21, 184];
117
+ var PANEL_HEADER = [16, 17, 21, 190];
118
+ var PANEL_LINE = [255, 255, 255, 24];
119
+ var LIME = [204, 255, 77, 255];
120
+ var LIME_DIM = [169, 214, 63, 255];
121
+ var CYAN = [86, 216, 255, 255];
122
+ var MAGENTA = [255, 121, 176, 255];
123
+ var INK_DIM = [108, 110, 121, 255];
124
+ var PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
125
+ var CRC_TABLE = makeCrcTable();
126
+ var MAX_IMAGE_BYTES = 48 * 1024 * 1024;
127
+ var KITTY_CHUNK_SIZE = 4096;
128
+ var PANEL_BLUR_SCALE = 1;
129
+ var PANEL_MASK_CACHE_LIMIT = 16;
130
+ var ZLIB_LEVEL = 1;
131
+ var panelMaskCache = new Map;
132
+ var BG_WORD = rgbaWord(BG);
22
133
  function imageTransport() {
23
134
  const forced = process.env.RIG_OPENTUI_IMAGE_TRANSPORT;
24
135
  if (forced === "rgba-zlib")
25
136
  return "rgba-zlib";
26
137
  return "png";
27
138
  }
28
- function droneLayerScale() {
29
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_DRONE_SCALE ?? "");
30
- if (Number.isFinite(raw) && raw > 0)
31
- return Math.max(0.15, Math.min(1, raw));
32
- return DEFAULT_DRONE_LAYER_SCALE;
33
- }
34
- function scaledPixelSize(size, scale, layout) {
35
- return {
36
- width: Math.max(layout.width, Math.floor(size.width * scale)),
37
- height: Math.max(layout.height, Math.floor(size.height * scale))
38
- };
39
- }
40
- function imageFps() {
41
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_IMAGE_FPS ?? "");
42
- if (Number.isFinite(raw) && raw > 0)
43
- return Math.max(10, Math.min(60, raw));
44
- return DEFAULT_IMAGE_FPS;
45
- }
46
139
  function rgbaWord(color) {
47
140
  return (color[3] << 24 | color[2] << 16 | color[1] << 8 | color[0]) >>> 0;
48
141
  }
@@ -58,6 +151,109 @@ function fillRgba(data, color, word = rgbaWord(color)) {
58
151
  data[i + 3] = color[3];
59
152
  }
60
153
  }
154
+ var ASCII_GLYPHS = {
155
+ ".": ["00000", "00000", "00000", "00000", "00000", "00100", "00100"],
156
+ "'": ["00100", "00100", "00000", "00000", "00000", "00000", "00000"],
157
+ "-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
158
+ _: ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
159
+ "=": ["00000", "11111", "00000", "00000", "11111", "00000", "00000"],
160
+ "|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
161
+ "/": ["00001", "00010", "00010", "00100", "01000", "01000", "10000"],
162
+ "\\": ["10000", "01000", "01000", "00100", "00010", "00010", "00001"],
163
+ "(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
164
+ ")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
165
+ "!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
166
+ "%": ["11001", "11010", "00100", "01000", "10110", "00110", "00000"],
167
+ $: ["00100", "11110", "10100", "11110", "00101", "11110", "00100"],
168
+ "@": ["01110", "10001", "10111", "10101", "10111", "10000", "01111"],
169
+ o: ["00000", "01110", "10001", "10001", "10001", "01110", "00000"],
170
+ "*": ["00100", "10101", "01110", "11111", "01110", "10101", "00100"]
171
+ };
172
+ function sceneStaticTick(scene) {
173
+ let hash = 0;
174
+ for (let index = 0;index < scene.length; index += 1)
175
+ hash = hash * 33 + scene.charCodeAt(index) >>> 0;
176
+ return 11 + hash % 97;
177
+ }
178
+ function charColor(char, row, totalRows) {
179
+ if (char === "@" || char === "$" || char === "o")
180
+ return LIME;
181
+ if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
182
+ return CYAN;
183
+ if (char === "." || char === "'" || char === "_" || row < 3 || row > totalRows - 4)
184
+ return INK_DIM;
185
+ if (char === "-" || char === "=" || char === "(" || char === ")")
186
+ return LIME_DIM;
187
+ return MAGENTA;
188
+ }
189
+ function fillRectAlpha(data, width, height, left, top, rectWidth, rectHeight, color, alpha) {
190
+ const x0 = Math.max(0, Math.floor(left));
191
+ const y0 = Math.max(0, Math.floor(top));
192
+ const x1 = Math.min(width, Math.ceil(left + rectWidth));
193
+ const y1 = Math.min(height, Math.ceil(top + rectHeight));
194
+ for (let y = y0;y < y1; y += 1) {
195
+ for (let x = x0;x < x1; x += 1)
196
+ blendOver(data, (y * width + x) * 4, color, alpha);
197
+ }
198
+ }
199
+ function drawAsciiGlyph(data, width, height, x, y, cellWidth, cellHeight, char, color, alpha) {
200
+ const pattern = ASCII_GLYPHS[char] ?? ASCII_GLYPHS["*"];
201
+ const glyphWidth = Math.max(1, cellWidth * 0.56);
202
+ const glyphHeight = Math.max(1, cellHeight * 0.7);
203
+ const originX = x + (cellWidth - glyphWidth) * 0.5;
204
+ const originY = y + (cellHeight - glyphHeight) * 0.5;
205
+ const pixelWidth = Math.max(1, glyphWidth / pattern[0].length);
206
+ const pixelHeight = Math.max(1, glyphHeight / pattern.length);
207
+ for (let row = 0;row < pattern.length; row += 1) {
208
+ const bits = pattern[row];
209
+ for (let col = 0;col < bits.length; col += 1) {
210
+ if (bits[col] !== "1")
211
+ continue;
212
+ fillRectAlpha(data, width, height, originX + col * pixelWidth, originY + row * pixelHeight, pixelWidth * 0.72, pixelHeight * 0.72, color, alpha);
213
+ }
214
+ }
215
+ }
216
+ function drawStaticAsciiFleetBackground(data, width, height, scene, layout) {
217
+ const rows = fleetRows(sceneStaticTick(scene), { animate: true });
218
+ const cellWidth = width / Math.max(1, layout.width);
219
+ const cellHeight = height / Math.max(1, layout.height);
220
+ const leftCells = Math.floor((layout.width - FLEET_GRID_WIDTH) / 2);
221
+ const topCells = Math.floor((layout.height - FLEET_GRID_HEIGHT) / 2);
222
+ const fleetLeft = leftCells * cellWidth;
223
+ const fleetTop = topCells * cellHeight;
224
+ drawGlow(data, width, height, width * 0.5, height * 0.5, Math.min(width, height) * 0.4, LIME, 0.03);
225
+ drawGlow(data, width, height, width * 0.18, height * 0.22, Math.min(width, height) * 0.28, CYAN, 0.022);
226
+ drawGlow(data, width, height, width * 0.82, height * 0.78, Math.min(width, height) * 0.22, MAGENTA, 0.014);
227
+ for (let row = 0;row < rows.length; row += 1) {
228
+ const line = rows[row];
229
+ for (let col = 0;col < line.length; col += 1) {
230
+ const char = line[col];
231
+ if (char === " ")
232
+ continue;
233
+ const x = fleetLeft + col * cellWidth;
234
+ const y = fleetTop + row * cellHeight;
235
+ const color = charColor(char, row, rows.length);
236
+ drawAsciiGlyph(data, width, height, x + cellWidth * 0.07, y + cellHeight * 0.05, cellWidth, cellHeight, char, color, 0.52);
237
+ if (char === "@" || char === "$" || char === "%") {
238
+ drawGlow(data, width, height, x + cellWidth * 0.5, y + cellHeight * 0.5, Math.max(cellWidth, cellHeight) * 0.95, color, 0.02);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ function applyStaticAtmosphere(data, width, height) {
244
+ const cx = width * 0.5;
245
+ const cy = height * 0.42;
246
+ const radius = Math.max(width, height) * 0.78;
247
+ for (let y = 0;y < height; y += 1) {
248
+ for (let x = 0;x < width; x += 1) {
249
+ const distance = Math.hypot(x - cx, y - cy) / radius;
250
+ const alpha = Math.max(0.1, Math.min(0.82, 0.12 + Math.pow(distance, 1.65) * 0.62));
251
+ blendOver(data, (y * width + x) * 4, BG, alpha);
252
+ }
253
+ }
254
+ drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.2, LIME, 0.03);
255
+ drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.16, CYAN, 0.022);
256
+ }
61
257
  function makeCrcTable() {
62
258
  const table = new Uint32Array(256);
63
259
  for (let n = 0;n < 256; n += 1) {
@@ -128,9 +324,6 @@ function kittyImageSequence(base64, options) {
128
324
  }
129
325
  return chunks.join("");
130
326
  }
131
- function isModernImageTerminal(protocol) {
132
- return protocol === "kitty";
133
- }
134
327
  function clampByte(value) {
135
328
  return Math.max(0, Math.min(255, Math.round(value)));
136
329
  }
@@ -142,23 +335,6 @@ function blendOver(data, index, color, alphaScale = 1) {
142
335
  data[index + 2] = clampByte(color[2] * alpha + data[index + 2] * inv);
143
336
  data[index + 3] = 255;
144
337
  }
145
- function drawDisc(data, width, height, cx, cy, radius, color, alpha = 1) {
146
- const minX = Math.max(0, Math.floor(cx - radius));
147
- const maxX = Math.min(width - 1, Math.ceil(cx + radius));
148
- const minY = Math.max(0, Math.floor(cy - radius));
149
- const maxY = Math.min(height - 1, Math.ceil(cy + radius));
150
- for (let y = minY;y <= maxY; y += 1) {
151
- for (let x = minX;x <= maxX; x += 1) {
152
- const dx = x + 0.5 - cx;
153
- const dy = y + 0.5 - cy;
154
- const d = Math.sqrt(dx * dx + dy * dy);
155
- if (d > radius)
156
- continue;
157
- const edge = Math.max(0, Math.min(1, radius - d));
158
- blendOver(data, (y * width + x) * 4, color, alpha * Math.min(1, 0.35 + edge));
159
- }
160
- }
161
- }
162
338
  function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
163
339
  const minX = Math.max(0, Math.floor(cx - radius));
164
340
  const maxX = Math.min(width - 1, Math.ceil(cx + radius));
@@ -176,30 +352,6 @@ function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
176
352
  }
177
353
  }
178
354
  }
179
- function drawLine(data, width, height, x0, y0, x1, y1, color, alpha = 0.5, thickness = 1.4) {
180
- const dx = x1 - x0;
181
- const dy = y1 - y0;
182
- const steps = Math.max(1, Math.ceil(Math.hypot(dx, dy) * 1.2));
183
- for (let i = 0;i <= steps; i += 1) {
184
- const t = i / steps;
185
- drawDisc(data, width, height, x0 + dx * t, y0 + dy * t, thickness, color, alpha);
186
- }
187
- }
188
- function drawDrone(data, width, height, x, y, scale, tick, color) {
189
- const wobble = Math.sin(tick * 0.055) * scale * 0.35;
190
- const cy = y + wobble;
191
- drawGlow(data, width, height, x, cy, scale * 7, color, 0.18);
192
- drawDisc(data, width, height, x, cy, scale * 1.45, color, 0.92);
193
- drawDisc(data, width, height, x, cy, scale * 0.55, [235, 255, 176, 255], 0.85);
194
- const arm = scale * 4.1;
195
- const rotor = scale * 1.6;
196
- const arms = [[-arm, -arm * 0.72], [arm, -arm * 0.72], [-arm, arm * 0.72], [arm, arm * 0.72]];
197
- for (const [dx, dy] of arms) {
198
- drawLine(data, width, height, x, cy, x + dx, cy + dy, color, 0.42, Math.max(1, scale * 0.22));
199
- drawGlow(data, width, height, x + dx, cy + dy, rotor * 2.6, color, 0.1);
200
- drawDisc(data, width, height, x + dx, cy + dy, rotor, color, 0.34 + 0.12 * Math.sin(tick * 0.15));
201
- }
202
- }
203
355
  function roundRectAlpha(px, py, x, y, w, h, r) {
204
356
  const rx = Math.max(x + r, Math.min(px, x + w - r));
205
357
  const ry = Math.max(y + r, Math.min(py, y + h - r));
@@ -209,17 +361,7 @@ function roundRectAlpha(px, py, x, y, w, h, r) {
209
361
  return Math.max(0, Math.min(1, r + 0.75 - dist));
210
362
  }
211
363
  function panelRect(layout, panel, size) {
212
- const cellW = size.width / layout.width;
213
- const cellH = size.height / layout.height;
214
- const leftCells = layout.centerLeft + (panel.left ?? 0);
215
- const topCells = layout.centerTop + panel.top;
216
- const widthCells = panel.width ?? layout.centerWidth;
217
- const heightCells = panel.height;
218
- const left = Math.round(leftCells * cellW);
219
- const top = Math.round(topCells * cellH);
220
- const width = Math.round(widthCells * cellW);
221
- const height = Math.round(heightCells * cellH);
222
- return width > 8 && height > 8 ? { left, top, width, height } : null;
364
+ return panelPixelPlacement(layout, panel, size);
223
365
  }
224
366
  function downsampleRegion(data, canvasW, canvasH, rect, scale) {
225
367
  const w = Math.max(1, Math.ceil(rect.width / scale));
@@ -368,7 +510,8 @@ function getPanelMask(width, height, rect, headerPx, blurScale) {
368
510
  function compositePanel(data, width, height, rect, headerPx) {
369
511
  const blurScale = PANEL_BLUR_SCALE;
370
512
  const sampled = downsampleRegion(data, width, height, rect, blurScale);
371
- const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, 4);
513
+ const blurRadius = Math.max(8, Math.min(28, Math.round(Math.min(rect.width, rect.height) * 0.018)));
514
+ const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, blurRadius);
372
515
  const mask = getPanelMask(width, height, rect, headerPx, blurScale);
373
516
  for (let i = 0;i < mask.indices.length; i += 1) {
374
517
  const idx = mask.indices[i];
@@ -386,161 +529,10 @@ function compositePanel(data, width, height, rect, headerPx) {
386
529
  blendOver(data, mask.dividerIndices[i], PANEL_LINE, 0.28);
387
530
  }
388
531
  }
389
- function visualSceneKind(scene) {
390
- if (scene === "tasks")
391
- return "loop";
392
- if (scene === "help")
393
- return "carrier";
394
- return "dispatch";
395
- }
396
- function seededUnit(seed) {
397
- let value = seed >>> 0;
398
- value = value * 1664525 + 1013904223 >>> 0;
399
- return value / 4294967295;
400
- }
401
- function sceneCore(width, height, kind) {
402
- if (kind === "dispatch")
403
- return { x: width * 0.3, y: height * 0.45 };
404
- if (kind === "carrier")
405
- return { x: width * 0.5, y: height * 0.54 };
406
- return { x: width * 0.09, y: height * 0.56 };
407
- }
408
- function dispatchLane(width, index, count) {
409
- const a = seededUnit(28672 + index * 101);
410
- const b = seededUnit(36864 + index * 131);
411
- const c = seededUnit(45056 + index * 151);
412
- const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
413
- return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
414
- }
415
- function lanePoint(width, height, lane, p, kind = "dispatch") {
416
- const core = sceneCore(width, height, kind);
417
- const dist = lane.len * p;
418
- const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
419
- const ux = Math.cos(lane.ang);
420
- const uy = Math.sin(lane.ang);
421
- return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
422
- }
423
- function drawMarketingRails(data, width, height, kind, tick) {
424
- const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
425
- ys.forEach((ratio, index) => {
426
- const y = height * ratio + Math.sin(tick * 0.018 + index) * Math.max(2, height * 0.0025);
427
- const color = index === 2 ? CYAN : index === 1 ? LIME : LIME_DIM;
428
- drawLine(data, width, height, width * 0.06, y, width * 0.94, y + Math.sin(index) * height * 0.002, color, index === 1 ? 0.11 : 0.07, Math.max(0.8, height * 0.0012));
429
- for (let p = (tick * 0.004 + index * 0.21) % 1;p < 1; p += 0.18 + index * 0.02) {
430
- const x = width * (0.06 + 0.88 * p);
431
- drawDisc(data, width, height, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.24);
432
- drawGlow(data, width, height, x, y, Math.max(5, height * 0.006), color, 0.035);
433
- }
434
- });
435
- }
436
- function drawDispatchScene(data, width, height, tick) {
437
- const core = sceneCore(width, height, "dispatch");
438
- const min = Math.min(width, height);
439
- drawGlow(data, width, height, core.x, core.y, min * 0.085, LIME, 0.12);
440
- drawDisc(data, width, height, core.x, core.y, min * 0.0065, LIME, 0.85);
441
- const laneCount = width < 1000 ? 7 : 12;
442
- const lanes = Array.from({ length: laneCount }, (_, index) => dispatchLane(width, index, laneCount));
443
- lanes.forEach((lane) => {
444
- let prev = lanePoint(width, height, lane, 0);
445
- for (let step = 1;step <= 32; step += 1) {
446
- const next = lanePoint(width, height, lane, step / 32);
447
- drawLine(data, width, height, prev.x, prev.y, next.x, next.y, lane.phase > Math.PI ? CYAN : LIME_DIM, 0.045, Math.max(0.7, min * 0.0009));
448
- prev = next;
449
- }
450
- });
451
- const packets = width < 1000 ? 70 : 150;
452
- for (let i = 0;i < packets; i += 1) {
453
- const lane = lanes[i % lanes.length];
454
- const speed = 0.003 + seededUnit(12288 + i * 17) * 0.009;
455
- const p = (seededUnit(16384 + i * 19) + tick * speed) % 1;
456
- const pt = lanePoint(width, height, lane, p);
457
- const wobble = (seededUnit(20480 + i * 23) - 0.5) * min * 0.012;
458
- const fade = p < 0.1 ? p / 0.1 : p > 0.88 ? (1 - p) / 0.12 : 1;
459
- const color = seededUnit(24576 + i * 29) < 0.22 ? CYAN : LIME_DIM;
460
- const y = pt.y + wobble * Math.sin(p * Math.PI * 2);
461
- drawLine(data, width, height, pt.x - min * 0.012, y - min * 0.002, pt.x, y, color, 0.08 * fade, Math.max(0.7, min * 0.0008));
462
- drawDisc(data, width, height, pt.x, y, Math.max(1.3, min * 0.0022), color, 0.5 * fade);
463
- drawGlow(data, width, height, pt.x, y, Math.max(4, min * 0.008), color, 0.045 * fade);
464
- }
465
- for (let i = 0;i < (width < 1000 ? 4 : 8); i += 1) {
466
- const lane = lanes[i % lanes.length];
467
- const p = (tick * 0.002 + i * 0.13) % 1;
468
- const pt = lanePoint(width, height, lane, p);
469
- drawDrone(data, width, height, pt.x, pt.y + Math.sin(tick * 0.04 + i) * min * 0.004, min * (0.006 + i % 2 * 0.0015), tick + i * 11, i % 3 === 0 ? CYAN : LIME_DIM);
470
- }
471
- }
472
- function drawLoopScene(data, width, height, tick) {
473
- const min = Math.min(width, height);
474
- const y = height * 0.56;
475
- const x0 = width * 0.09;
476
- const x1 = width * 0.91;
477
- drawLine(data, width, height, x0, y, x1, y, LIME_DIM, 0.12, Math.max(0.8, min * 0.001));
478
- const gates = width < 1000 ? 4 : 5;
479
- const active = Math.floor(tick / 16) % gates;
480
- for (let i = 0;i < gates; i += 1) {
481
- const x = x0 + (x1 - x0) * (i / Math.max(1, gates - 1));
482
- const color = i === active ? LIME : i <= active ? LIME_DIM : INK_DIM;
483
- drawLine(data, width, height, x, y - min * 0.05, x, y + min * 0.05, color, i === active ? 0.25 : 0.08, Math.max(0.8, min * 0.001));
484
- drawDisc(data, width, height, x, y, i === active ? min * 0.006 : min * 0.0035, color, i === active ? 0.72 : 0.25);
485
- drawGlow(data, width, height, x, y, i === active ? min * 0.018 : min * 0.01, color, i === active ? 0.1 : 0.035);
486
- }
487
- const packets = width < 1000 ? 36 : 84;
488
- for (let i = 0;i < packets; i += 1) {
489
- const p = (seededUnit(33024 + i * 13) + tick * (0.0022 + seededUnit(33280 + i * 17) * 0.004)) % 1;
490
- const x = x0 + (x1 - x0) * p;
491
- const segment = Math.min(gates - 1, Math.floor(p * gates));
492
- const color = segment < active ? LIME_DIM : segment === active ? CYAN : INK_DIM;
493
- const py = y + (seededUnit(33536 + i * 19) - 0.5) * min * 0.018 * Math.sin(p * Math.PI * 2);
494
- drawLine(data, width, height, x - min * 0.014, py, x, py, color, 0.1, Math.max(0.7, min * 0.0008));
495
- drawDisc(data, width, height, x, py, Math.max(1.2, min * 0.002), color, color === INK_DIM ? 0.22 : 0.58);
496
- }
497
- drawDrone(data, width, height, x0 + (x1 - x0) * (tick * 0.003 % 1), y - min * 0.04, min * 0.0065, tick, LIME_DIM);
498
- drawDrone(data, width, height, x0 + (x1 - x0) * ((tick * 0.002 + 0.5) % 1), y + min * 0.036, min * 0.0055, tick + 40, MAGENTA);
499
- }
500
- function drawCarrierScene(data, width, height, tick) {
501
- const min = Math.min(width, height);
502
- const core = sceneCore(width, height, "carrier");
503
- drawGlow(data, width, height, core.x, core.y, min * 0.075, LIME, 0.12);
504
- drawDisc(data, width, height, core.x, core.y, min * 0.0065, LIME, 0.92);
505
- const nodes = width < 1000 ? 5 : 7;
506
- const rx = width * 0.36;
507
- const ry = height * 0.22;
508
- for (let i = 0;i < nodes; i += 1) {
509
- const a = i / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006;
510
- const x = core.x + Math.cos(a) * rx;
511
- const y = core.y + Math.sin(a) * ry;
512
- const color = i % 2 ? CYAN : LIME_DIM;
513
- drawLine(data, width, height, core.x, core.y, x, y, color, 0.055, Math.max(0.7, min * 0.0008));
514
- drawGlow(data, width, height, x, y, min * 0.018, color, 0.055);
515
- drawDisc(data, width, height, x, y, min * 0.0048, color, 0.62);
516
- drawDrone(data, width, height, x, y - min * 0.018, min * 0.005, tick + i * 9, color);
517
- }
518
- const packets = width < 1000 ? 30 : 72;
519
- for (let i = 0;i < packets; i += 1) {
520
- const lane = i % nodes;
521
- const a = lane / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006;
522
- const nx = core.x + Math.cos(a) * rx;
523
- const ny = core.y + Math.sin(a) * ry;
524
- const p = (seededUnit(37120 + i * 37) + tick * (0.003 + seededUnit(37376 + i * 43) * 0.009)) % 1;
525
- const x = core.x + (nx - core.x) * p;
526
- const y = core.y + (ny - core.y) * p;
527
- const color = seededUnit(37632 + i * 47) < 0.35 ? CYAN : LIME_DIM;
528
- drawDisc(data, width, height, x, y, Math.max(1.2, min * 0.002), color, 0.48);
529
- drawGlow(data, width, height, x, y, min * 0.008, color, 0.035);
530
- }
531
- }
532
- function drawBackground(data, width, height, tick, scene) {
532
+ function drawBackground(data, width, height, _tick, scene, layout) {
533
533
  fillRgba(data, BG, BG_WORD);
534
- const kind = visualSceneKind(scene);
535
- drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME, 0.035);
536
- drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN, 0.025);
537
- drawMarketingRails(data, width, height, kind, tick);
538
- if (kind === "loop")
539
- drawLoopScene(data, width, height, tick);
540
- else if (kind === "carrier")
541
- drawCarrierScene(data, width, height, tick);
542
- else
543
- drawDispatchScene(data, width, height, tick);
534
+ drawStaticAsciiFleetBackground(data, width, height, scene, layout);
535
+ applyStaticAtmosphere(data, width, height);
544
536
  }
545
537
  function imageVisualFrameKey(input) {
546
538
  const width = Math.max(1, Math.floor(input.pixelSize.width));
@@ -565,14 +557,76 @@ function encodeKittyFrame(width, height, data, input) {
565
557
  compressed: true
566
558
  });
567
559
  }
560
+ function cropRgba(data, sourceWidth, rect) {
561
+ const out = new Uint8ClampedArray(rect.width * rect.height * 4);
562
+ for (let y = 0;y < rect.height; y += 1) {
563
+ const sourceStart = ((rect.top + y) * sourceWidth + rect.left) * 4;
564
+ out.set(data.subarray(sourceStart, sourceStart + rect.width * 4), y * rect.width * 4);
565
+ }
566
+ return out;
567
+ }
568
+ function encodePanelPatch(data, sourceWidth, placement, imageId, zIndex) {
569
+ const patch = cropRgba(data, sourceWidth, placement);
570
+ const png = encodePng(placement.width, placement.height, patch);
571
+ const sequence = kittyImageSequence(png.toString("base64"), {
572
+ columns: Math.max(1, Math.round(placement.widthCells)),
573
+ rows: Math.max(1, Math.round(placement.heightCells)),
574
+ imageId,
575
+ zIndex,
576
+ format: 100
577
+ });
578
+ return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
579
+ }
568
580
  function renderImageVisualFrame(input) {
569
581
  const width = Math.max(1, Math.floor(input.pixelSize.width));
570
582
  const height = Math.max(1, Math.floor(input.pixelSize.height));
571
583
  const byteCount = width * height * 4;
572
584
  if (byteCount > MAX_IMAGE_BYTES)
573
585
  return null;
586
+ const layer = input.layer ?? "full";
574
587
  const data = new Uint8ClampedArray(byteCount);
575
- drawBackground(data, width, height, input.tick, input.scene);
588
+ drawBackground(data, width, height, 0, input.scene, input.layout);
589
+ if (layer === "split") {
590
+ const sequences = [encodeKittyFrame(width, height, data, input)];
591
+ let panelIndex = 0;
592
+ for (const panel of input.panels) {
593
+ if (panel.chrome !== "ad-terminal")
594
+ continue;
595
+ const placement = panelPixelPlacement(input.layout, panel, { width, height });
596
+ if (!placement)
597
+ continue;
598
+ const cellH = height / input.layout.height;
599
+ const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
600
+ compositePanel(data, width, height, placement, headerPx);
601
+ const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
602
+ sequences.push(encodePanelPatch(data, width, placement, imageId, -12));
603
+ panelIndex += 1;
604
+ }
605
+ return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
606
+ }
607
+ if (layer === "panel-chrome")
608
+ return { key: imageVisualFrameKey(input), sequence: "" };
609
+ if (layer === "background") {
610
+ return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
611
+ }
612
+ if (layer === "panel-patches") {
613
+ const sequences = [];
614
+ let panelIndex = 0;
615
+ for (const panel of input.panels) {
616
+ if (panel.chrome !== "ad-terminal")
617
+ continue;
618
+ const placement = panelPixelPlacement(input.layout, panel, { width, height });
619
+ if (!placement)
620
+ continue;
621
+ const cellH = height / input.layout.height;
622
+ const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
623
+ compositePanel(data, width, height, placement, headerPx);
624
+ const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
625
+ sequences.push(encodePanelPatch(data, width, placement, imageId, input.zIndex));
626
+ panelIndex += 1;
627
+ }
628
+ return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
629
+ }
576
630
  for (const panel of input.panels) {
577
631
  if (panel.chrome !== "ad-terminal")
578
632
  continue;
@@ -585,8 +639,11 @@ function renderImageVisualFrame(input) {
585
639
  }
586
640
  return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
587
641
  }
642
+ function shouldRenderImagesInline() {
643
+ return process.env.RIG_OPENTUI_IMAGE_WORKER === "0" || import.meta.url.includes("$bunfs");
644
+ }
588
645
  function createImageWorker() {
589
- if (process.env.RIG_OPENTUI_IMAGE_WORKER === "0")
646
+ if (shouldRenderImagesInline())
590
647
  return null;
591
648
  const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
592
649
  const workerUrl = new URL(isTypeScriptRuntime ? "./image-visual-layer-worker.ts" : "./image-visual-layer-worker.js", import.meta.url);
@@ -601,18 +658,9 @@ function createImageWorker() {
601
658
  return null;
602
659
  }
603
660
  }
604
- function panelFps() {
605
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_PANEL_FPS ?? "");
606
- if (Number.isFinite(raw) && raw > 0)
607
- return Math.max(4, Math.min(60, raw));
608
- return DEFAULT_PANEL_FPS;
609
- }
610
- function panelLayerScale() {
611
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_PANEL_SCALE ?? "");
612
- if (Number.isFinite(raw) && raw > 0)
613
- return Math.max(0.18, Math.min(1, raw));
614
- return DEFAULT_PANEL_LAYER_SCALE;
615
- }
661
+ var RECENT_WRITTEN_LIMIT = 24;
662
+ var DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
663
+ var DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
616
664
  function imageOutputBacklogBytes() {
617
665
  const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BACKLOG_BYTES ?? "", 10);
618
666
  if (Number.isFinite(raw) && raw > 0)
@@ -634,52 +682,43 @@ class ImageVisualLayer {
634
682
  panelImageIds = Array.from({ length: 8 }, () => allocateImageId());
635
683
  panelChromeImageIds = Array.from({ length: 8 }, () => allocateImageId());
636
684
  stdout;
685
+ onFrameReady;
637
686
  protocol;
638
687
  transport = imageTransport();
639
- frameIntervalMs = 1000 / imageFps();
640
- panelIntervalMs = 1000 / panelFps();
641
688
  maxOutputBacklogBytes = imageOutputBacklogBytes();
642
689
  outputBytesPerSecond = imageOutputBytesPerSecond();
643
690
  outputBudgetBytes = imageOutputBytesPerSecond();
644
691
  lastBudgetAtMs = Date.now();
645
692
  motionSlot = this.createSlot();
646
- panelSlot = this.createSlot();
647
- chromeSlot = this.createSlot();
693
+ workersStarted = false;
648
694
  pendingFrames = new Map;
649
695
  recentWrittenKeys = [];
696
+ imagesVisible = false;
697
+ flushPaused = false;
650
698
  requestId = 0;
651
- lastMotionFrameIndex = -1;
652
- lastPanelFrameIndex = -1;
653
- lastChromeKey = "";
699
+ lastBackgroundKey = "";
700
+ fatalWorkerError = null;
654
701
  destroyed = false;
655
- constructor(stdout = process.stdout) {
702
+ constructor(stdout = process.stdout, onFrameReady) {
656
703
  this.stdout = stdout;
657
- this.protocol = getCapabilities().images;
658
- if (this.enabled) {
659
- this.startWorker(this.motionSlot);
660
- this.startWorker(this.panelSlot);
661
- this.startWorker(this.chromeSlot);
662
- }
704
+ this.onFrameReady = onFrameReady;
705
+ this.protocol = getRigTerminalCapabilities().imageProtocol;
663
706
  }
664
707
  get enabled() {
665
- return isModernImageTerminal(this.protocol);
708
+ return this.protocol === "kitty";
666
709
  }
667
710
  clear() {
668
711
  if (this.enabled) {
669
- this.stdout.write([this.imageId, ...this.panelImageIds, ...this.panelChromeImageIds].map((id) => deleteKittyImage(id)).join("") + "\x1B[?25h");
712
+ if (this.imagesVisible)
713
+ this.stdout.write([this.imageId, ...this.panelImageIds, ...this.panelChromeImageIds].map((id) => deleteKittyImage(id)).join("") + "\x1B[?25h");
714
+ this.imagesVisible = false;
670
715
  this.pendingFrames.clear();
671
716
  this.recentWrittenKeys.length = 0;
672
717
  this.resetSlot(this.motionSlot);
673
- this.resetSlot(this.panelSlot);
674
- this.resetSlot(this.chromeSlot);
675
- this.lastMotionFrameIndex = -1;
676
- this.lastPanelFrameIndex = -1;
677
- this.lastChromeKey = "";
718
+ this.lastBackgroundKey = "";
678
719
  this.outputBudgetBytes = this.outputBytesPerSecond;
679
720
  this.lastBudgetAtMs = Date.now();
680
721
  this.motionSlot.clearedThroughRequestId = this.requestId;
681
- this.panelSlot.clearedThroughRequestId = this.requestId;
682
- this.chromeSlot.clearedThroughRequestId = this.requestId;
683
722
  }
684
723
  }
685
724
  destroy() {
@@ -687,19 +726,31 @@ class ImageVisualLayer {
687
726
  return;
688
727
  this.clear();
689
728
  this.destroyed = true;
690
- const workers = [this.motionSlot.worker, this.panelSlot.worker, this.chromeSlot.worker];
729
+ const workers = [this.motionSlot.worker];
691
730
  this.motionSlot.worker = null;
692
- this.panelSlot.worker = null;
693
- this.chromeSlot.worker = null;
694
731
  for (const worker of workers)
695
732
  if (worker)
696
733
  worker.terminate().catch(() => {
697
734
  return;
698
735
  });
699
736
  }
737
+ setFlushPaused(paused) {
738
+ this.flushPaused = paused;
739
+ }
740
+ isReady(input) {
741
+ if (!this.enabled || this.destroyed)
742
+ return true;
743
+ this.throwIfFatal();
744
+ const splitKey = imageVisualFrameKey({ ...input, tick: 0, layer: "split" });
745
+ return this.recentWrittenKeys.includes(splitKey) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === splitKey);
746
+ }
700
747
  render(input) {
701
748
  if (!this.enabled || this.destroyed)
702
749
  return;
750
+ this.throwIfFatal();
751
+ if (this.transport !== "png")
752
+ this.failWorker("renderer", new Error("OpenTUI image visuals require PNG transport for static-background rendering."));
753
+ this.ensureWorkersStarted();
703
754
  const width = Math.max(1, Math.floor(input.pixelSize.width));
704
755
  const height = Math.max(1, Math.floor(input.pixelSize.height));
705
756
  if (width * height * 4 > MAX_IMAGE_BYTES)
@@ -707,54 +758,32 @@ class ImageVisualLayer {
707
758
  if (this.isOutputBackedUp())
708
759
  return;
709
760
  this.refillOutputBudget();
710
- const now = Date.now();
711
- const visualTick = Math.floor(now / (1000 / 60));
712
- const useDynamicSplit = Boolean(this.motionSlot.worker && this.panelSlot.worker && this.chromeSlot.worker && this.transport === "png" && process.env.RIG_OPENTUI_DYNAMIC_SPLIT !== "0");
713
- if (!useDynamicSplit) {
714
- const frameIndex = Math.floor(now / this.frameIntervalMs);
715
- if (!input.force && frameIndex === this.lastMotionFrameIndex)
716
- return;
717
- this.scheduleRender(this.motionSlot, { ...input, tick: visualTick, layer: "full" }, this.imageId, -10, input.force);
718
- this.lastMotionFrameIndex = frameIndex;
719
- return;
720
- }
721
- const motionFrameIndex = Math.floor(now / this.frameIntervalMs);
722
- if ((input.force || motionFrameIndex !== this.lastMotionFrameIndex) && this.outputBudgetBytes >= MIN_BACKGROUND_BUDGET_BYTES) {
723
- this.scheduleRender(this.motionSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, droneLayerScale(), input.layout), panels: [], tick: visualTick, layer: "background" }, this.imageId, -30, input.force);
724
- this.lastMotionFrameIndex = motionFrameIndex;
725
- }
726
- const panelFrameIndex = Math.floor(now / this.panelIntervalMs);
727
- if ((input.force || panelFrameIndex !== this.lastPanelFrameIndex) && this.outputBudgetBytes >= MIN_PANEL_BUDGET_BYTES) {
728
- this.scheduleRender(this.panelSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, panelLayerScale(), input.layout), tick: visualTick, layer: "panel-patches" }, this.imageId, -12, input.force, this.panelImageIds);
729
- this.lastPanelFrameIndex = panelFrameIndex;
730
- }
731
- const chromeInput = { ...input, tick: 0, layer: "panel-chrome" };
732
- const chromeKey = imageVisualFrameKey(chromeInput);
733
- if (input.force || chromeKey !== this.lastChromeKey) {
734
- this.scheduleRender(this.chromeSlot, chromeInput, this.imageId, -8, input.force, this.panelChromeImageIds);
735
- this.lastChromeKey = chromeKey;
761
+ const splitInput = { ...input, tick: 0, layer: "split" };
762
+ const splitKey = imageVisualFrameKey(splitInput);
763
+ if (input.force || splitKey !== this.lastBackgroundKey) {
764
+ this.scheduleRender(this.motionSlot, splitInput, this.imageId, -30, input.force, this.panelImageIds);
765
+ this.lastBackgroundKey = splitKey;
736
766
  }
737
767
  }
738
768
  flush() {
769
+ this.throwIfFatal();
770
+ if (this.flushPaused)
771
+ return false;
739
772
  if (this.pendingFrames.size === 0)
740
773
  return false;
741
774
  this.refillOutputBudget();
742
775
  if (this.isOutputBackedUp()) {
743
- const chrome = this.pendingFrames.get("panel-chrome");
776
+ const retainedSplit = this.pendingFrames.get("split");
744
777
  this.pendingFrames.clear();
745
- if (chrome)
746
- this.pendingFrames.set("panel-chrome", chrome);
778
+ if (retainedSplit)
779
+ this.pendingFrames.set("split", retainedSplit);
747
780
  return false;
748
781
  }
749
782
  const framePriority = (frame) => {
750
783
  const layer = pendingLayerKey(frame.key);
751
- if (layer === "panel-chrome")
752
- return 0;
753
- if (layer === "background")
784
+ if (layer === "split")
754
785
  return 1;
755
- if (layer === "panel-patches")
756
- return 2;
757
- return 2;
786
+ return 4;
758
787
  };
759
788
  const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
760
789
  const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
@@ -766,12 +795,14 @@ class ImageVisualLayer {
766
795
  for (const frame of frames) {
767
796
  const candidate = sequence + frame.sequence;
768
797
  const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
769
- if (candidateBytes <= this.outputBudgetBytes) {
798
+ const layer = pendingLayerKey(frame.key);
799
+ const allowOneStaticFrame = selected.length === 0 && layer === "split";
800
+ if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
770
801
  selected.push(frame);
771
802
  sequence = candidate;
772
803
  byteLength = candidateBytes;
773
- } else if (pendingLayerKey(frame.key) === "panel-chrome") {
774
- retained.set(pendingLayerKey(frame.key), frame);
804
+ } else if (layer === "split") {
805
+ retained.set(layer, frame);
775
806
  }
776
807
  }
777
808
  this.pendingFrames.clear();
@@ -786,10 +817,11 @@ class ImageVisualLayer {
786
817
  this.stdout.cork?.();
787
818
  const accepted = this.stdout.write(payload);
788
819
  this.stdout.uncork?.();
820
+ this.imagesVisible = true;
789
821
  return accepted;
790
822
  }
791
823
  createSlot() {
792
- return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0 };
824
+ return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0, latestRequestId: 0 };
793
825
  }
794
826
  resetSlot(slot) {
795
827
  slot.busy = false;
@@ -817,7 +849,23 @@ class ImageVisualLayer {
817
849
  isKnownKey(slot, key) {
818
850
  return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
819
851
  }
852
+ ensureWorkersStarted() {
853
+ if (this.workersStarted)
854
+ return;
855
+ this.workersStarted = true;
856
+ this.startWorker(this.motionSlot, "static-visuals");
857
+ }
858
+ failWorker(name, error) {
859
+ const detail = error instanceof Error ? error.message : String(error);
860
+ this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${detail}`);
861
+ throw this.fatalWorkerError;
862
+ }
863
+ throwIfFatal() {
864
+ if (this.fatalWorkerError)
865
+ throw this.fatalWorkerError;
866
+ }
820
867
  scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
868
+ this.throwIfFatal();
821
869
  const key = imageVisualFrameKey(input);
822
870
  if (!force && this.isKnownKey(slot, key))
823
871
  return;
@@ -830,38 +878,41 @@ class ImageVisualLayer {
830
878
  requestId: ++this.requestId,
831
879
  key
832
880
  };
833
- if (slot.worker) {
834
- this.enqueueWorkerRender(slot, request);
881
+ slot.latestRequestId = request.requestId;
882
+ if (!slot.worker) {
883
+ this.renderInline(slot, request);
835
884
  return;
836
885
  }
837
- this.renderSync(slot, request);
886
+ this.enqueueWorkerRender(slot, request);
838
887
  }
839
- startWorker(slot) {
888
+ startWorker(slot, name) {
840
889
  const worker = createImageWorker();
841
890
  if (!worker)
842
891
  return;
843
892
  slot.worker = worker;
844
893
  worker.on("message", (message) => this.handleWorkerMessage(slot, message));
845
- worker.once("error", () => this.disableWorker(slot));
846
- worker.once("exit", () => this.disableWorker(slot));
894
+ worker.once("error", (error) => {
895
+ if (!this.destroyed)
896
+ this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${error instanceof Error ? error.message : String(error)}`);
897
+ });
898
+ worker.once("exit", (code) => {
899
+ if (!this.destroyed)
900
+ this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} exited unexpectedly with code ${code}.`);
901
+ });
847
902
  }
848
- disableWorker(slot) {
849
- if (this.destroyed)
850
- return;
851
- slot.worker = null;
852
- slot.busy = false;
853
- slot.inFlightKey = "";
854
- const queued = slot.queuedRequest;
855
- slot.queuedRequest = null;
856
- slot.queuedKey = "";
857
- if (queued?.layer === "full")
858
- this.renderSync(slot, queued);
903
+ renderInline(slot, request) {
904
+ try {
905
+ const result = renderImageVisualFrame(request);
906
+ if (result?.sequence && !this.isKnownKey(slot, result.key) && (!this.isOutputBackedUp() || pendingLayerKey(result.key) === "split")) {
907
+ this.stageFrame({ key: result.key, sequence: result.sequence });
908
+ this.onFrameReady?.();
909
+ }
910
+ } catch (error) {
911
+ this.failWorker(String(request.layer ?? "visual"), error);
912
+ }
859
913
  }
860
914
  enqueueWorkerRender(slot, request) {
861
- if (!slot.worker) {
862
- this.renderSync(slot, request);
863
- return;
864
- }
915
+ this.throwIfFatal();
865
916
  if (slot.busy) {
866
917
  slot.queuedRequest = request;
867
918
  slot.queuedKey = request.key;
@@ -870,13 +921,16 @@ class ImageVisualLayer {
870
921
  this.dispatchWorkerRender(slot, request);
871
922
  }
872
923
  dispatchWorkerRender(slot, request) {
873
- if (!slot.worker) {
874
- this.renderSync(slot, request);
875
- return;
876
- }
924
+ this.throwIfFatal();
925
+ if (!slot.worker)
926
+ this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
877
927
  slot.busy = true;
878
928
  slot.inFlightKey = request.key;
879
- slot.worker.postMessage(request);
929
+ try {
930
+ slot.worker.postMessage(request);
931
+ } catch (error) {
932
+ this.failWorker(String(request.layer ?? "visual"), error);
933
+ }
880
934
  }
881
935
  handleWorkerMessage(slot, message) {
882
936
  if (this.destroyed || !message || typeof message !== "object")
@@ -884,14 +938,14 @@ class ImageVisualLayer {
884
938
  const response = message;
885
939
  slot.busy = false;
886
940
  slot.inFlightKey = "";
887
- if (response.requestId <= slot.clearedThroughRequestId)
888
- return;
889
- if (response.error) {
890
- this.disableWorker(slot);
891
- return;
892
- }
893
- if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "panel-chrome")) {
894
- this.stageFrame({ key: response.key, sequence: response.sequence });
941
+ const isStale = response.requestId <= slot.clearedThroughRequestId || response.requestId < slot.latestRequestId;
942
+ if (!isStale) {
943
+ if (response.error)
944
+ this.failWorker(pendingLayerKey(response.key), new Error(response.error));
945
+ if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "split")) {
946
+ this.stageFrame({ key: response.key, sequence: response.sequence });
947
+ this.onFrameReady?.();
948
+ }
895
949
  }
896
950
  const next = slot.queuedRequest;
897
951
  slot.queuedRequest = null;
@@ -899,611 +953,12 @@ class ImageVisualLayer {
899
953
  if (next && !this.isKnownKey(slot, next.key))
900
954
  this.dispatchWorkerRender(slot, next);
901
955
  }
902
- renderSync(slot, request) {
903
- const result = renderImageVisualFrame(request);
904
- if (!result || this.isKnownKey(slot, result.key) || this.isOutputBackedUp())
905
- return;
906
- this.stageFrame(result);
907
- }
908
- }
909
- var BG, PANEL, PANEL_HEADER, PANEL_LINE, LIME, LIME_DIM, CYAN, MAGENTA, INK_DIM, PNG_SIGNATURE, CRC_TABLE, MAX_IMAGE_BYTES, KITTY_CHUNK_SIZE = 4096, PANEL_BLUR_SCALE = 5, PANEL_MASK_CACHE_LIMIT = 16, ZLIB_LEVEL = 1, DEFAULT_DRONE_LAYER_SCALE = 0.18, DEFAULT_IMAGE_FPS = 24, panelMaskCache, BG_WORD, RECENT_WRITTEN_LIMIT = 24, DEFAULT_PANEL_FPS = 24, DEFAULT_PANEL_LAYER_SCALE = 0.22, DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES, DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND, MIN_BACKGROUND_BUDGET_BYTES, MIN_PANEL_BUDGET_BYTES;
910
- var init_image_visual_layer = __esm(() => {
911
- BG = [7, 8, 9, 255];
912
- PANEL = [16, 17, 21, 184];
913
- PANEL_HEADER = [16, 17, 21, 190];
914
- PANEL_LINE = [255, 255, 255, 24];
915
- LIME = [204, 255, 77, 255];
916
- LIME_DIM = [169, 214, 63, 255];
917
- CYAN = [86, 216, 255, 255];
918
- MAGENTA = [255, 121, 176, 255];
919
- INK_DIM = [108, 110, 121, 255];
920
- PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
921
- CRC_TABLE = makeCrcTable();
922
- MAX_IMAGE_BYTES = 48 * 1024 * 1024;
923
- panelMaskCache = new Map;
924
- BG_WORD = rgbaWord(BG);
925
- DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
926
- DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
927
- MIN_BACKGROUND_BUDGET_BYTES = 24 * 1024;
928
- MIN_PANEL_BUDGET_BYTES = 56 * 1024;
929
- });
930
-
931
- // packages/cli/src/app-opentui/render/image-visual-layer-native-canvas.ts
932
- var exports_image_visual_layer_native_canvas = {};
933
- __export(exports_image_visual_layer_native_canvas, {
934
- renderNativeCanvasImageVisualFrame: () => renderNativeCanvasImageVisualFrame
935
- });
936
- import { mkdirSync, writeFileSync } from "fs";
937
- import { join } from "path";
938
- import { tmpdir } from "os";
939
- import { clearAllCache, createCanvas } from "@napi-rs/canvas";
940
- function droneLayerScale2() {
941
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_DRONE_SCALE ?? "");
942
- if (Number.isFinite(raw) && raw > 0)
943
- return Math.max(0.15, Math.min(1, raw));
944
- return DEFAULT_DRONE_LAYER_SCALE2;
945
- }
946
- function kittyTransmission() {
947
- return process.env.RIG_OPENTUI_KITTY_TRANSMISSION === "temp-file" ? "temp-file" : "direct";
948
- }
949
- function motionDensity() {
950
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_MOTION_DENSITY ?? "");
951
- if (Number.isFinite(raw) && raw > 0)
952
- return Math.max(0.2, Math.min(1.4, raw));
953
- return DEFAULT_MOTION_DENSITY;
954
- }
955
- function motionSpeed() {
956
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_MOTION_SPEED ?? "");
957
- if (Number.isFinite(raw) && raw > 0)
958
- return Math.max(0.25, Math.min(1.5, raw));
959
- return DEFAULT_MOTION_SPEED;
960
- }
961
- function resetContext(ctx, width, height) {
962
- ctx.reset();
963
- ctx.clearRect(0, 0, width, height);
964
- ctx.imageSmoothingEnabled = true;
965
- ctx.imageSmoothingQuality = "high";
966
- }
967
- function reusableCanvas(width, height) {
968
- if (!frameCanvas || !frameCtx || frameCanvas.width !== width || frameCanvas.height !== height) {
969
- frameCanvas = createCanvas(width, height);
970
- frameCtx = frameCanvas.getContext("2d");
971
- }
972
- resetContext(frameCtx, width, height);
973
- return { canvas: frameCanvas, ctx: frameCtx };
974
- }
975
- function reusableSceneCanvas(width, height) {
976
- if (!sceneCanvas || !sceneCtx || sceneCanvas.width !== width || sceneCanvas.height !== height) {
977
- sceneCanvas = createCanvas(width, height);
978
- sceneCtx = sceneCanvas.getContext("2d");
979
- }
980
- resetContext(sceneCtx, width, height);
981
- return { canvas: sceneCanvas, ctx: sceneCtx };
982
- }
983
- function reusablePatchCanvas(width, height) {
984
- if (!patchCanvas || !patchCtx || patchCanvas.width !== width || patchCanvas.height !== height) {
985
- patchCanvas = createCanvas(width, height);
986
- patchCtx = patchCanvas.getContext("2d");
987
- }
988
- resetContext(patchCtx, width, height);
989
- return { canvas: patchCanvas, ctx: patchCtx };
990
- }
991
- function css(color, alphaScale = 1) {
992
- return `rgba(${color[0]},${color[1]},${color[2]},${Math.max(0, Math.min(1, color[3] / 255 * alphaScale))})`;
993
- }
994
- function visualSceneKind2(scene) {
995
- if (scene === "tasks")
996
- return "loop";
997
- if (scene === "help")
998
- return "carrier";
999
- return "dispatch";
1000
- }
1001
- function seededUnit2(seed) {
1002
- let value = seed >>> 0;
1003
- value = value * 1664525 + 1013904223 >>> 0;
1004
- return value / 4294967295;
1005
- }
1006
- function sceneCore2(width, height, kind) {
1007
- if (kind === "dispatch")
1008
- return { x: width * 0.3, y: height * 0.45 };
1009
- if (kind === "carrier")
1010
- return { x: width * 0.5, y: height * 0.54 };
1011
- return { x: width * 0.09, y: height * 0.56 };
1012
- }
1013
- function dispatchLane2(width, index, count) {
1014
- const a = seededUnit2(28672 + index * 101);
1015
- const b = seededUnit2(36864 + index * 131);
1016
- const c = seededUnit2(45056 + index * 151);
1017
- const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
1018
- return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
1019
- }
1020
- function lanePoint2(width, height, lane, p, kind = "dispatch") {
1021
- const core = sceneCore2(width, height, kind);
1022
- const dist = lane.len * p;
1023
- const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
1024
- const ux = Math.cos(lane.ang);
1025
- const uy = Math.sin(lane.ang);
1026
- return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
1027
- }
1028
- function drawGlow2(ctx, cx, cy, radius, color, alpha = 0.18) {
1029
- if (radius <= 0)
1030
- return;
1031
- const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, radius);
1032
- gradient.addColorStop(0, css(color, alpha));
1033
- gradient.addColorStop(0.45, css(color, alpha * 0.28));
1034
- gradient.addColorStop(1, css(color, 0));
1035
- ctx.fillStyle = gradient;
1036
- ctx.beginPath();
1037
- ctx.arc(cx, cy, radius, 0, Math.PI * 2);
1038
- ctx.fill();
1039
- }
1040
- function drawDisc2(ctx, cx, cy, radius, color, alpha = 1) {
1041
- if (radius <= 0)
1042
- return;
1043
- ctx.fillStyle = css(color, alpha);
1044
- ctx.beginPath();
1045
- ctx.arc(cx, cy, radius, 0, Math.PI * 2);
1046
- ctx.fill();
1047
- }
1048
- function drawLine2(ctx, x0, y0, x1, y1, color, alpha = 0.5, thickness = 1.4) {
1049
- ctx.save();
1050
- ctx.strokeStyle = css(color, alpha);
1051
- ctx.lineWidth = thickness * 2;
1052
- ctx.lineCap = "round";
1053
- ctx.lineJoin = "round";
1054
- ctx.beginPath();
1055
- ctx.moveTo(x0, y0);
1056
- ctx.lineTo(x1, y1);
1057
- ctx.stroke();
1058
- ctx.restore();
1059
- }
1060
- function drawDrone2(ctx, x, y, scale, tick, color) {
1061
- const speed = motionSpeed();
1062
- const wobble = Math.sin(tick * 0.055 * speed) * scale * 0.35;
1063
- const cy = y + wobble;
1064
- drawGlow2(ctx, x, cy, scale * 7, color, 0.18);
1065
- drawDisc2(ctx, x, cy, scale * 1.45, color, 0.92);
1066
- drawDisc2(ctx, x, cy, scale * 0.55, [235, 255, 176, 255], 0.85);
1067
- const arm = scale * 4.1;
1068
- const rotor = scale * 1.6;
1069
- const arms = [[-arm, -arm * 0.72], [arm, -arm * 0.72], [-arm, arm * 0.72], [arm, arm * 0.72]];
1070
- for (const [dx, dy] of arms) {
1071
- drawLine2(ctx, x, cy, x + dx, cy + dy, color, 0.42, Math.max(1, scale * 0.22));
1072
- drawGlow2(ctx, x + dx, cy + dy, rotor * 2.6, color, 0.1);
1073
- drawDisc2(ctx, x + dx, cy + dy, rotor, color, 0.34 + 0.12 * Math.sin(tick * 0.15 * speed));
1074
- }
1075
- }
1076
- function drawMarketingRails2(ctx, width, height, kind, tick) {
1077
- const density = motionDensity();
1078
- const speed = motionSpeed();
1079
- const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
1080
- ys.forEach((ratio, index) => {
1081
- const y = height * ratio + Math.sin(tick * 0.018 * speed + index) * Math.max(2, height * 0.0025);
1082
- const color = index === 2 ? CYAN2 : index === 1 ? LIME2 : LIME_DIM2;
1083
- drawLine2(ctx, width * 0.06, y, width * 0.94, y + Math.sin(index) * height * 0.002, color, index === 1 ? 0.09 : 0.055, Math.max(0.8, height * 0.0012));
1084
- const spacing = (0.18 + index * 0.02) / density;
1085
- for (let p = (tick * 0.004 * speed + index * 0.21) % 1;p < 1; p += spacing) {
1086
- const x = width * (0.06 + 0.88 * p);
1087
- drawDisc2(ctx, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.18);
1088
- drawGlow2(ctx, x, y, Math.max(5, height * 0.006), color, 0.026);
1089
- }
1090
- });
1091
- }
1092
- function drawDispatchScene2(ctx, width, height, tick) {
1093
- const density = motionDensity();
1094
- const speed = motionSpeed();
1095
- const core = sceneCore2(width, height, "dispatch");
1096
- const min = Math.min(width, height);
1097
- drawGlow2(ctx, core.x, core.y, min * 0.085, LIME2, 0.12);
1098
- drawDisc2(ctx, core.x, core.y, min * 0.0065, LIME2, 0.85);
1099
- const laneCount = Math.max(4, Math.round((width < 1000 ? 7 : 12) * (0.65 + density * 0.35)));
1100
- const lanes = Array.from({ length: laneCount }, (_, index) => dispatchLane2(width, index, laneCount));
1101
- lanes.forEach((lane) => {
1102
- let prev = lanePoint2(width, height, lane, 0);
1103
- ctx.beginPath();
1104
- ctx.moveTo(prev.x, prev.y);
1105
- for (let step = 1;step <= 32; step += 1) {
1106
- const next = lanePoint2(width, height, lane, step / 32);
1107
- ctx.lineTo(next.x, next.y);
1108
- prev = next;
1109
- }
1110
- ctx.strokeStyle = css(lane.phase > Math.PI ? CYAN2 : LIME_DIM2, 0.045);
1111
- ctx.lineWidth = Math.max(0.7, min * 0.0009) * 2;
1112
- ctx.lineCap = "round";
1113
- ctx.stroke();
1114
- });
1115
- const packets = Math.max(24, Math.round((width < 1000 ? 70 : 150) * density));
1116
- for (let i = 0;i < packets; i += 1) {
1117
- const lane = lanes[i % lanes.length];
1118
- const packetSpeed = (0.003 + seededUnit2(12288 + i * 17) * 0.009) * speed;
1119
- const p = (seededUnit2(16384 + i * 19) + tick * packetSpeed) % 1;
1120
- const pt = lanePoint2(width, height, lane, p);
1121
- const wobble = (seededUnit2(20480 + i * 23) - 0.5) * min * 0.012;
1122
- const fade = p < 0.1 ? p / 0.1 : p > 0.88 ? (1 - p) / 0.12 : 1;
1123
- const color = seededUnit2(24576 + i * 29) < 0.22 ? CYAN2 : LIME_DIM2;
1124
- const y = pt.y + wobble * Math.sin(p * Math.PI * 2);
1125
- drawLine2(ctx, pt.x - min * 0.012, y - min * 0.002, pt.x, y, color, 0.08 * fade, Math.max(0.7, min * 0.0008));
1126
- drawDisc2(ctx, pt.x, y, Math.max(1.3, min * 0.0022), color, 0.5 * fade);
1127
- drawGlow2(ctx, pt.x, y, Math.max(4, min * 0.008), color, 0.045 * fade);
1128
- }
1129
- const droneCount = Math.max(2, Math.round((width < 1000 ? 4 : 8) * density));
1130
- for (let i = 0;i < droneCount; i += 1) {
1131
- const lane = lanes[i % lanes.length];
1132
- const p = (tick * 0.002 * speed + i * 0.13) % 1;
1133
- const pt = lanePoint2(width, height, lane, p);
1134
- drawDrone2(ctx, pt.x, pt.y + Math.sin(tick * 0.04 * speed + i) * min * 0.004, min * (0.006 + i % 2 * 0.0015), tick + i * 11, i % 3 === 0 ? CYAN2 : LIME_DIM2);
1135
- }
1136
- }
1137
- function drawLoopScene2(ctx, width, height, tick) {
1138
- const density = motionDensity();
1139
- const speed = motionSpeed();
1140
- const min = Math.min(width, height);
1141
- const y = height * 0.56;
1142
- const x0 = width * 0.09;
1143
- const x1 = width * 0.91;
1144
- drawLine2(ctx, x0, y, x1, y, LIME_DIM2, 0.12, Math.max(0.8, min * 0.001));
1145
- const gates = width < 1000 ? 4 : 5;
1146
- const active = Math.floor(tick * speed / 20) % gates;
1147
- for (let i = 0;i < gates; i += 1) {
1148
- const x = x0 + (x1 - x0) * (i / Math.max(1, gates - 1));
1149
- const color = i === active ? LIME2 : i <= active ? LIME_DIM2 : INK_DIM2;
1150
- drawLine2(ctx, x, y - min * 0.05, x, y + min * 0.05, color, i === active ? 0.25 : 0.08, Math.max(0.8, min * 0.001));
1151
- drawDisc2(ctx, x, y, i === active ? min * 0.006 : min * 0.0035, color, i === active ? 0.72 : 0.25);
1152
- drawGlow2(ctx, x, y, i === active ? min * 0.018 : min * 0.01, color, i === active ? 0.1 : 0.035);
1153
- }
1154
- const packets = Math.max(16, Math.round((width < 1000 ? 36 : 84) * density));
1155
- for (let i = 0;i < packets; i += 1) {
1156
- const p = (seededUnit2(33024 + i * 13) + tick * (0.0022 + seededUnit2(33280 + i * 17) * 0.004) * speed) % 1;
1157
- const x = x0 + (x1 - x0) * p;
1158
- const segment = Math.min(gates - 1, Math.floor(p * gates));
1159
- const color = segment < active ? LIME_DIM2 : segment === active ? CYAN2 : INK_DIM2;
1160
- const py = y + (seededUnit2(33536 + i * 19) - 0.5) * min * 0.018 * Math.sin(p * Math.PI * 2);
1161
- drawLine2(ctx, x - min * 0.014, py, x, py, color, 0.1, Math.max(0.7, min * 0.0008));
1162
- drawDisc2(ctx, x, py, Math.max(1.2, min * 0.002), color, color === INK_DIM2 ? 0.22 : 0.58);
1163
- }
1164
- drawDrone2(ctx, x0 + (x1 - x0) * (tick * 0.003 * speed % 1), y - min * 0.04, min * 0.0065, tick, LIME_DIM2);
1165
- drawDrone2(ctx, x0 + (x1 - x0) * ((tick * 0.002 * speed + 0.5) % 1), y + min * 0.036, min * 0.0055, tick + 40, MAGENTA2);
1166
- }
1167
- function drawCarrierScene2(ctx, width, height, tick) {
1168
- const density = motionDensity();
1169
- const speed = motionSpeed();
1170
- const min = Math.min(width, height);
1171
- const core = sceneCore2(width, height, "carrier");
1172
- drawGlow2(ctx, core.x, core.y, min * 0.075, LIME2, 0.12);
1173
- drawDisc2(ctx, core.x, core.y, min * 0.0065, LIME2, 0.92);
1174
- const nodes = Math.max(4, Math.round((width < 1000 ? 5 : 7) * (0.75 + density * 0.25)));
1175
- const rx = width * 0.36;
1176
- const ry = height * 0.22;
1177
- for (let i = 0;i < nodes; i += 1) {
1178
- const a = i / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006 * speed;
1179
- const x = core.x + Math.cos(a) * rx;
1180
- const y = core.y + Math.sin(a) * ry;
1181
- const color = i % 2 ? CYAN2 : LIME_DIM2;
1182
- drawLine2(ctx, core.x, core.y, x, y, color, 0.055, Math.max(0.7, min * 0.0008));
1183
- drawGlow2(ctx, x, y, min * 0.018, color, 0.055);
1184
- drawDisc2(ctx, x, y, min * 0.0048, color, 0.62);
1185
- drawDrone2(ctx, x, y - min * 0.018, min * 0.005, tick + i * 9, color);
1186
- }
1187
- const packets = Math.max(14, Math.round((width < 1000 ? 30 : 72) * density));
1188
- for (let i = 0;i < packets; i += 1) {
1189
- const lane = i % nodes;
1190
- const a = lane / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006 * speed;
1191
- const nx = core.x + Math.cos(a) * rx;
1192
- const ny = core.y + Math.sin(a) * ry;
1193
- const p = (seededUnit2(37120 + i * 37) + tick * (0.003 + seededUnit2(37376 + i * 43) * 0.009) * speed) % 1;
1194
- const x = core.x + (nx - core.x) * p;
1195
- const y = core.y + (ny - core.y) * p;
1196
- const color = seededUnit2(37632 + i * 47) < 0.35 ? CYAN2 : LIME_DIM2;
1197
- drawDisc2(ctx, x, y, Math.max(1.2, min * 0.002), color, 0.48);
1198
- drawGlow2(ctx, x, y, min * 0.008, color, 0.035);
1199
- }
1200
- }
1201
- function drawBackground2(ctx, width, height, tick, scene) {
1202
- ctx.fillStyle = css(BG2);
1203
- ctx.fillRect(0, 0, width, height);
1204
- const kind = visualSceneKind2(scene);
1205
- drawGlow2(ctx, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME2, 0.035);
1206
- drawGlow2(ctx, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN2, 0.025);
1207
- drawMarketingRails2(ctx, width, height, kind, tick);
1208
- if (kind === "loop")
1209
- drawLoopScene2(ctx, width, height, tick);
1210
- else if (kind === "carrier")
1211
- drawCarrierScene2(ctx, width, height, tick);
1212
- else
1213
- drawDispatchScene2(ctx, width, height, tick);
1214
- }
1215
- function drawScaledDroneLayer(ctx, width, height, tick, scene) {
1216
- const scale = droneLayerScale2();
1217
- if (scale >= 0.99) {
1218
- drawBackground2(ctx, width, height, tick, scene);
1219
- return;
1220
- }
1221
- const sceneWidth = Math.max(1, Math.round(width * scale));
1222
- const sceneHeight = Math.max(1, Math.round(height * scale));
1223
- const { canvas, ctx: sceneCtx2 } = reusableSceneCanvas(sceneWidth, sceneHeight);
1224
- drawBackground2(sceneCtx2, sceneWidth, sceneHeight, tick, scene);
1225
- ctx.save();
1226
- ctx.imageSmoothingEnabled = true;
1227
- ctx.imageSmoothingQuality = "high";
1228
- ctx.drawImage(canvas, 0, 0, sceneWidth, sceneHeight, 0, 0, width, height);
1229
- ctx.restore();
1230
- }
1231
- function panelPlacement(layout, panel, size) {
1232
- const cellW = size.width / layout.width;
1233
- const cellH = size.height / layout.height;
1234
- const leftCells = layout.centerLeft + (panel.left ?? 0);
1235
- const topCells = layout.centerTop + panel.top;
1236
- const widthCells = panel.width ?? layout.centerWidth;
1237
- const heightCells = panel.height;
1238
- const left = Math.round(leftCells * cellW);
1239
- const top = Math.round(topCells * cellH);
1240
- const width = Math.round(widthCells * cellW);
1241
- const height = Math.round(heightCells * cellH);
1242
- return width > 8 && height > 8 ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
1243
- }
1244
- function panelRect2(layout, panel, size) {
1245
- return panelPlacement(layout, panel, size);
1246
- }
1247
- function clipRoundRect(ctx, rect, radius) {
1248
- ctx.beginPath();
1249
- ctx.roundRect(rect.left, rect.top, rect.width, rect.height, radius);
1250
- ctx.clip();
1251
- }
1252
- function strokeRoundRect(ctx, rect, radius) {
1253
- ctx.beginPath();
1254
- ctx.roundRect(rect.left + 0.75, rect.top + 0.75, rect.width - 1.5, rect.height - 1.5, Math.max(1, radius - 0.75));
1255
- ctx.stroke();
1256
- }
1257
- function compositePanel2(ctx, sourceCanvas, rect, headerPx) {
1258
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(rect.width, rect.height) * 0.035)));
1259
- const { canvas: patch, ctx: patchCtx2 } = reusablePatchCanvas(rect.width, rect.height);
1260
- patchCtx2.drawImage(sourceCanvas, rect.left, rect.top, rect.width, rect.height, 0, 0, rect.width, rect.height);
1261
- ctx.save();
1262
- clipRoundRect(ctx, rect, radius);
1263
- ctx.filter = `blur(${Math.max(10, Math.round(Math.min(rect.width, rect.height) * 0.018))}px)`;
1264
- ctx.drawImage(patch, 0, 0, rect.width, rect.height, rect.left, rect.top, rect.width, rect.height);
1265
- ctx.filter = "none";
1266
- ctx.fillStyle = css(PANEL2);
1267
- ctx.fillRect(rect.left, rect.top, rect.width, rect.height);
1268
- ctx.fillStyle = css(PANEL_HEADER2);
1269
- ctx.fillRect(rect.left, rect.top, rect.width, headerPx);
1270
- ctx.restore();
1271
- ctx.save();
1272
- ctx.strokeStyle = css(PANEL_LINE2, 0.8);
1273
- ctx.lineWidth = 1.5;
1274
- strokeRoundRect(ctx, rect, radius);
1275
- ctx.restore();
1276
- const dividerY = Math.round(rect.top + headerPx);
1277
- if (dividerY > rect.top && dividerY < rect.top + rect.height) {
1278
- drawLine2(ctx, rect.left + radius, dividerY, rect.left + rect.width - radius, dividerY, PANEL_LINE2, 0.28, 0.7);
1279
- }
1280
- }
1281
- function compositePanelPatch(ctx, sourceCanvas, width, height, headerPx) {
1282
- const rect = { left: 0, top: 0, width, height };
1283
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(width, height) * 0.035)));
1284
- ctx.save();
1285
- clipRoundRect(ctx, rect, radius);
1286
- ctx.filter = `blur(${Math.max(10, Math.round(Math.min(width, height) * 0.018))}px)`;
1287
- ctx.drawImage(sourceCanvas, 0, 0, width, height);
1288
- ctx.filter = "none";
1289
- ctx.fillStyle = css(PANEL2);
1290
- ctx.fillRect(0, 0, width, height);
1291
- ctx.fillStyle = css(PANEL_HEADER2);
1292
- ctx.fillRect(0, 0, width, headerPx);
1293
- ctx.restore();
1294
- }
1295
- function compositePanelChromePatch(ctx, width, height, headerPx) {
1296
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(width, height) * 0.035)));
1297
- ctx.clearRect(0, 0, width, height);
1298
- const dividerY = Math.round(headerPx);
1299
- if (dividerY > 0 && dividerY < height)
1300
- drawLine2(ctx, radius, dividerY, width - radius, dividerY, PANEL_LINE2, 0.22, 0.65);
1301
- }
1302
- async function encodePanelPatch(input, placement, imageId, headerPx) {
1303
- const { canvas: sourceCanvas, ctx: sourceCtx } = reusableSceneCanvas(placement.width, placement.height);
1304
- sourceCtx.save();
1305
- sourceCtx.translate(-placement.left, -placement.top);
1306
- drawScaledDroneLayer(sourceCtx, input.pixelSize.width, input.pixelSize.height, input.tick, input.scene);
1307
- sourceCtx.restore();
1308
- const { canvas: patchCanvas2, ctx: patchCtx2 } = reusablePatchCanvas(placement.width, placement.height);
1309
- compositePanelPatch(patchCtx2, sourceCanvas, placement.width, placement.height, headerPx);
1310
- const png = await patchCanvas2.encode("png");
1311
- const sequence = kittyPngFrameSequence(png, { columns: Math.max(1, Math.round(placement.widthCells)), rows: Math.max(1, Math.round(placement.heightCells)), imageId, zIndex: -12 });
1312
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
1313
- }
1314
- async function encodePanelChromePatch(placement, imageId, headerPx) {
1315
- const { canvas: patchCanvas2, ctx: patchCtx2 } = reusablePatchCanvas(placement.width, placement.height);
1316
- compositePanelChromePatch(patchCtx2, placement.width, placement.height, headerPx);
1317
- const png = await patchCanvas2.encode("png");
1318
- const sequence = kittyPngFrameSequence(png, { columns: Math.max(1, Math.round(placement.widthCells)), rows: Math.max(1, Math.round(placement.heightCells)), imageId, zIndex: -8 });
1319
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
1320
- }
1321
- function kittyImageSequence2(base64, options) {
1322
- const params = [`a=T`, `f=100`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
1323
- if (base64.length <= KITTY_CHUNK_SIZE2)
1324
- return `\x1B_G${params.join(",")};${base64}\x1B\\`;
1325
- const chunks = [];
1326
- for (let offset = 0;offset < base64.length; offset += KITTY_CHUNK_SIZE2) {
1327
- const part = base64.slice(offset, offset + KITTY_CHUNK_SIZE2);
1328
- const last = offset + KITTY_CHUNK_SIZE2 >= base64.length;
1329
- chunks.push(`\x1B_G${offset === 0 ? `${params.join(",")},` : ""}m=${last ? 0 : 1};${part}\x1B\\`);
1330
- }
1331
- return chunks.join("");
1332
- }
1333
- function kittyTempFileImageSequence(png, options) {
1334
- mkdirSync(TEMP_GRAPHICS_DIR, { recursive: true });
1335
- const path = join(TEMP_GRAPHICS_DIR, `frame-${process.pid}-${Date.now()}-${encodedFrames}.tty-graphics-protocol.png`);
1336
- writeFileSync(path, png);
1337
- const payload = Buffer.from(path).toString("base64");
1338
- const params = [`a=T`, `f=100`, `t=t`, `S=${png.byteLength}`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
1339
- return `\x1B_G${params.join(",")};${payload}\x1B\\`;
1340
956
  }
1341
- function kittyPngFrameSequence(png, options) {
1342
- if (kittyTransmission() === "temp-file")
1343
- return kittyTempFileImageSequence(png, options);
1344
- return kittyImageSequence2(png.toString("base64"), options);
1345
- }
1346
- async function renderNativeCanvasImageVisualFrame(input) {
1347
- if (input.transport !== "png")
1348
- return null;
1349
- const width = Math.max(1, Math.floor(input.pixelSize.width));
1350
- const height = Math.max(1, Math.floor(input.pixelSize.height));
1351
- if (width * height * 4 > MAX_IMAGE_BYTES2)
1352
- return null;
1353
- const layer = input.layer ?? "full";
1354
- if (layer === "split") {
1355
- const backgroundScale = droneLayerScale2();
1356
- const backgroundWidth = Math.max(input.layout.width, Math.round(width * backgroundScale));
1357
- const backgroundHeight = Math.max(input.layout.height, Math.round(height * backgroundScale));
1358
- const { canvas: backgroundCanvas, ctx: backgroundCtx } = reusableCanvas(backgroundWidth, backgroundHeight);
1359
- drawBackground2(backgroundCtx, backgroundWidth, backgroundHeight, input.tick, input.scene);
1360
- const backgroundPng = await backgroundCanvas.encode("png");
1361
- const sequences = [kittyPngFrameSequence(backgroundPng, { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: -30 })];
1362
- let panelIndex = 0;
1363
- for (const panel of input.panels) {
1364
- if (panel.chrome !== "ad-terminal")
1365
- continue;
1366
- const placement = panelPlacement(input.layout, panel, { width, height });
1367
- if (!placement)
1368
- continue;
1369
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
1370
- const cellH = height / input.layout.height;
1371
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1372
- sequences.push(await encodePanelPatch(input, placement, imageId, headerPx));
1373
- panelIndex += 1;
1374
- }
1375
- encodedFrames += 1;
1376
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1377
- clearAllCache();
1378
- return {
1379
- key: imageVisualFrameKey(input),
1380
- sequence: sequences.join("")
1381
- };
1382
- }
1383
- if (layer === "panel-patches") {
1384
- const sequences = [];
1385
- let panelIndex = 0;
1386
- for (const panel of input.panels) {
1387
- if (panel.chrome !== "ad-terminal")
1388
- continue;
1389
- const placement = panelPlacement(input.layout, panel, { width, height });
1390
- if (!placement)
1391
- continue;
1392
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
1393
- const cellH = height / input.layout.height;
1394
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1395
- sequences.push(await encodePanelPatch(input, placement, imageId, headerPx));
1396
- panelIndex += 1;
1397
- }
1398
- encodedFrames += 1;
1399
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1400
- clearAllCache();
1401
- return {
1402
- key: imageVisualFrameKey(input),
1403
- sequence: sequences.join("")
1404
- };
1405
- }
1406
- if (layer === "panel-chrome") {
1407
- const sequences = [];
1408
- let panelIndex = 0;
1409
- for (const panel of input.panels) {
1410
- if (panel.chrome !== "ad-terminal")
1411
- continue;
1412
- const placement = panelPlacement(input.layout, panel, { width, height });
1413
- if (!placement)
1414
- continue;
1415
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 2000 + panelIndex >>> 0 || panelIndex + 1);
1416
- const cellH = height / input.layout.height;
1417
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1418
- sequences.push(await encodePanelChromePatch(placement, imageId, headerPx));
1419
- panelIndex += 1;
1420
- }
1421
- encodedFrames += 1;
1422
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1423
- clearAllCache();
1424
- return {
1425
- key: imageVisualFrameKey(input),
1426
- sequence: sequences.join("")
1427
- };
1428
- }
1429
- const { canvas, ctx } = reusableCanvas(width, height);
1430
- if (layer === "background") {
1431
- drawBackground2(ctx, width, height, input.tick, input.scene);
1432
- } else if (layer === "panels") {
1433
- const { canvas: sourceCanvas, ctx: sourceCtx } = reusableSceneCanvas(width, height);
1434
- drawScaledDroneLayer(sourceCtx, width, height, input.tick, input.scene);
1435
- for (const panel of input.panels) {
1436
- if (panel.chrome !== "ad-terminal")
1437
- continue;
1438
- const rect = panelRect2(input.layout, panel, { width, height });
1439
- if (!rect)
1440
- continue;
1441
- const cellH = height / input.layout.height;
1442
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1443
- compositePanel2(ctx, sourceCanvas, rect, headerPx);
1444
- }
1445
- } else {
1446
- drawScaledDroneLayer(ctx, width, height, input.tick, input.scene);
1447
- for (const panel of input.panels) {
1448
- if (panel.chrome !== "ad-terminal")
1449
- continue;
1450
- const rect = panelRect2(input.layout, panel, { width, height });
1451
- if (!rect)
1452
- continue;
1453
- const cellH = height / input.layout.height;
1454
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1455
- compositePanel2(ctx, canvas, rect, headerPx);
1456
- }
1457
- }
1458
- const png = await canvas.encode("png");
1459
- encodedFrames += 1;
1460
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1461
- clearAllCache();
1462
- return {
1463
- key: imageVisualFrameKey(input),
1464
- sequence: kittyPngFrameSequence(png, { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: input.zIndex })
1465
- };
1466
- }
1467
- var BG2, PANEL2, PANEL_HEADER2, PANEL_LINE2, LIME2, LIME_DIM2, CYAN2, MAGENTA2, INK_DIM2, KITTY_CHUNK_SIZE2 = 4096, MAX_IMAGE_BYTES2, SKIA_CACHE_CLEAR_INTERVAL = 240, DEFAULT_DRONE_LAYER_SCALE2 = 0.18, DEFAULT_MOTION_DENSITY = 0.42, DEFAULT_MOTION_SPEED = 0.55, TEMP_GRAPHICS_DIR, frameCanvas = null, frameCtx = null, sceneCanvas = null, sceneCtx = null, patchCanvas = null, patchCtx = null, encodedFrames = 0;
1468
- var init_image_visual_layer_native_canvas = __esm(() => {
1469
- init_image_visual_layer();
1470
- BG2 = [7, 8, 9, 255];
1471
- PANEL2 = [16, 17, 21, 184];
1472
- PANEL_HEADER2 = [16, 17, 21, 190];
1473
- PANEL_LINE2 = [255, 255, 255, 24];
1474
- LIME2 = [204, 255, 77, 255];
1475
- LIME_DIM2 = [169, 214, 63, 255];
1476
- CYAN2 = [86, 216, 255, 255];
1477
- MAGENTA2 = [255, 121, 176, 255];
1478
- INK_DIM2 = [108, 110, 121, 255];
1479
- MAX_IMAGE_BYTES2 = 48 * 1024 * 1024;
1480
- TEMP_GRAPHICS_DIR = join(tmpdir(), "rig-tty-graphics-protocol");
1481
- });
1482
957
 
1483
958
  // packages/cli/src/app-opentui/render/image-visual-layer-worker.ts
1484
- init_image_visual_layer();
1485
- import { parentPort } from "worker_threads";
1486
- var nativeCanvasRenderer;
1487
959
  async function renderFrame(request) {
1488
- if (process.env.RIG_OPENTUI_IMAGE_BACKEND !== "ts" && request.transport === "png") {
1489
- if (nativeCanvasRenderer === undefined) {
1490
- try {
1491
- const mod = await Promise.resolve().then(() => (init_image_visual_layer_native_canvas(), exports_image_visual_layer_native_canvas));
1492
- nativeCanvasRenderer = mod.renderNativeCanvasImageVisualFrame;
1493
- } catch {
1494
- nativeCanvasRenderer = null;
1495
- }
1496
- }
1497
- if (nativeCanvasRenderer) {
1498
- try {
1499
- const native = await nativeCanvasRenderer(request);
1500
- if (native)
1501
- return native;
1502
- } catch {
1503
- nativeCanvasRenderer = null;
1504
- }
1505
- }
1506
- }
960
+ if (request.transport !== "png")
961
+ throw new Error("OpenTUI image workers require PNG transport.");
1507
962
  return renderImageVisualFrame(request);
1508
963
  }
1509
964
  function isImageVisualWorkerRequest(value) {