@h-rig/cli 0.0.6-alpha.82 → 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 +2798 -1440
  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 +96 -34
  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 +13769 -12368
  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 +3802 -2882
  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 +9 -11
  33. package/dist/src/app-opentui/registry.js +3231 -2403
  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 +408 -957
  39. package/dist/src/app-opentui/render/image-visual-layer.d.ts +18 -10
  40. package/dist/src/app-opentui/render/image-visual-layer.js +386 -356
  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 +2322 -1513
  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 -1484
@@ -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,36 +682,30 @@ 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 = [];
650
696
  imagesVisible = false;
697
+ flushPaused = false;
651
698
  requestId = 0;
652
- lastMotionFrameIndex = -1;
653
- lastPanelFrameIndex = -1;
654
- lastChromeKey = "";
699
+ lastBackgroundKey = "";
700
+ fatalWorkerError = null;
655
701
  destroyed = false;
656
- constructor(stdout = process.stdout) {
702
+ constructor(stdout = process.stdout, onFrameReady) {
657
703
  this.stdout = stdout;
658
- this.protocol = getCapabilities().images;
659
- if (this.enabled) {
660
- this.startWorker(this.motionSlot);
661
- this.startWorker(this.panelSlot);
662
- this.startWorker(this.chromeSlot);
663
- }
704
+ this.onFrameReady = onFrameReady;
705
+ this.protocol = getRigTerminalCapabilities().imageProtocol;
664
706
  }
665
707
  get enabled() {
666
- return isModernImageTerminal(this.protocol);
708
+ return this.protocol === "kitty";
667
709
  }
668
710
  clear() {
669
711
  if (this.enabled) {
@@ -673,16 +715,10 @@ class ImageVisualLayer {
673
715
  this.pendingFrames.clear();
674
716
  this.recentWrittenKeys.length = 0;
675
717
  this.resetSlot(this.motionSlot);
676
- this.resetSlot(this.panelSlot);
677
- this.resetSlot(this.chromeSlot);
678
- this.lastMotionFrameIndex = -1;
679
- this.lastPanelFrameIndex = -1;
680
- this.lastChromeKey = "";
718
+ this.lastBackgroundKey = "";
681
719
  this.outputBudgetBytes = this.outputBytesPerSecond;
682
720
  this.lastBudgetAtMs = Date.now();
683
721
  this.motionSlot.clearedThroughRequestId = this.requestId;
684
- this.panelSlot.clearedThroughRequestId = this.requestId;
685
- this.chromeSlot.clearedThroughRequestId = this.requestId;
686
722
  }
687
723
  }
688
724
  destroy() {
@@ -690,19 +726,31 @@ class ImageVisualLayer {
690
726
  return;
691
727
  this.clear();
692
728
  this.destroyed = true;
693
- const workers = [this.motionSlot.worker, this.panelSlot.worker, this.chromeSlot.worker];
729
+ const workers = [this.motionSlot.worker];
694
730
  this.motionSlot.worker = null;
695
- this.panelSlot.worker = null;
696
- this.chromeSlot.worker = null;
697
731
  for (const worker of workers)
698
732
  if (worker)
699
733
  worker.terminate().catch(() => {
700
734
  return;
701
735
  });
702
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
+ }
703
747
  render(input) {
704
748
  if (!this.enabled || this.destroyed)
705
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();
706
754
  const width = Math.max(1, Math.floor(input.pixelSize.width));
707
755
  const height = Math.max(1, Math.floor(input.pixelSize.height));
708
756
  if (width * height * 4 > MAX_IMAGE_BYTES)
@@ -710,54 +758,32 @@ class ImageVisualLayer {
710
758
  if (this.isOutputBackedUp())
711
759
  return;
712
760
  this.refillOutputBudget();
713
- const now = Date.now();
714
- const visualTick = Math.floor(now / (1000 / 60));
715
- const useDynamicSplit = Boolean(this.motionSlot.worker && this.panelSlot.worker && this.chromeSlot.worker && this.transport === "png" && process.env.RIG_OPENTUI_DYNAMIC_SPLIT !== "0");
716
- if (!useDynamicSplit) {
717
- const frameIndex = Math.floor(now / this.frameIntervalMs);
718
- if (!input.force && frameIndex === this.lastMotionFrameIndex)
719
- return;
720
- this.scheduleRender(this.motionSlot, { ...input, tick: visualTick, layer: "full" }, this.imageId, -10, input.force);
721
- this.lastMotionFrameIndex = frameIndex;
722
- return;
723
- }
724
- const motionFrameIndex = Math.floor(now / this.frameIntervalMs);
725
- if ((input.force || motionFrameIndex !== this.lastMotionFrameIndex) && this.outputBudgetBytes >= MIN_BACKGROUND_BUDGET_BYTES) {
726
- this.scheduleRender(this.motionSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, droneLayerScale(), input.layout), panels: [], tick: visualTick, layer: "background" }, this.imageId, -30, input.force);
727
- this.lastMotionFrameIndex = motionFrameIndex;
728
- }
729
- const panelFrameIndex = Math.floor(now / this.panelIntervalMs);
730
- if ((input.force || panelFrameIndex !== this.lastPanelFrameIndex) && this.outputBudgetBytes >= MIN_PANEL_BUDGET_BYTES) {
731
- this.scheduleRender(this.panelSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, panelLayerScale(), input.layout), tick: visualTick, layer: "panel-patches" }, this.imageId, -12, input.force, this.panelImageIds);
732
- this.lastPanelFrameIndex = panelFrameIndex;
733
- }
734
- const chromeInput = { ...input, tick: 0, layer: "panel-chrome" };
735
- const chromeKey = imageVisualFrameKey(chromeInput);
736
- if (input.force || chromeKey !== this.lastChromeKey) {
737
- this.scheduleRender(this.chromeSlot, chromeInput, this.imageId, -8, input.force, this.panelChromeImageIds);
738
- 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;
739
766
  }
740
767
  }
741
768
  flush() {
769
+ this.throwIfFatal();
770
+ if (this.flushPaused)
771
+ return false;
742
772
  if (this.pendingFrames.size === 0)
743
773
  return false;
744
774
  this.refillOutputBudget();
745
775
  if (this.isOutputBackedUp()) {
746
- const chrome = this.pendingFrames.get("panel-chrome");
776
+ const retainedSplit = this.pendingFrames.get("split");
747
777
  this.pendingFrames.clear();
748
- if (chrome)
749
- this.pendingFrames.set("panel-chrome", chrome);
778
+ if (retainedSplit)
779
+ this.pendingFrames.set("split", retainedSplit);
750
780
  return false;
751
781
  }
752
782
  const framePriority = (frame) => {
753
783
  const layer = pendingLayerKey(frame.key);
754
- if (layer === "panel-chrome")
755
- return 0;
756
- if (layer === "background")
784
+ if (layer === "split")
757
785
  return 1;
758
- if (layer === "panel-patches")
759
- return 2;
760
- return 2;
786
+ return 4;
761
787
  };
762
788
  const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
763
789
  const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
@@ -769,12 +795,14 @@ class ImageVisualLayer {
769
795
  for (const frame of frames) {
770
796
  const candidate = sequence + frame.sequence;
771
797
  const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
772
- if (candidateBytes <= this.outputBudgetBytes) {
798
+ const layer = pendingLayerKey(frame.key);
799
+ const allowOneStaticFrame = selected.length === 0 && layer === "split";
800
+ if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
773
801
  selected.push(frame);
774
802
  sequence = candidate;
775
803
  byteLength = candidateBytes;
776
- } else if (pendingLayerKey(frame.key) === "panel-chrome") {
777
- retained.set(pendingLayerKey(frame.key), frame);
804
+ } else if (layer === "split") {
805
+ retained.set(layer, frame);
778
806
  }
779
807
  }
780
808
  this.pendingFrames.clear();
@@ -793,7 +821,7 @@ class ImageVisualLayer {
793
821
  return accepted;
794
822
  }
795
823
  createSlot() {
796
- 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 };
797
825
  }
798
826
  resetSlot(slot) {
799
827
  slot.busy = false;
@@ -821,7 +849,23 @@ class ImageVisualLayer {
821
849
  isKnownKey(slot, key) {
822
850
  return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
823
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
+ }
824
867
  scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
868
+ this.throwIfFatal();
825
869
  const key = imageVisualFrameKey(input);
826
870
  if (!force && this.isKnownKey(slot, key))
827
871
  return;
@@ -834,38 +878,41 @@ class ImageVisualLayer {
834
878
  requestId: ++this.requestId,
835
879
  key
836
880
  };
837
- if (slot.worker) {
838
- this.enqueueWorkerRender(slot, request);
881
+ slot.latestRequestId = request.requestId;
882
+ if (!slot.worker) {
883
+ this.renderInline(slot, request);
839
884
  return;
840
885
  }
841
- this.renderSync(slot, request);
886
+ this.enqueueWorkerRender(slot, request);
842
887
  }
843
- startWorker(slot) {
888
+ startWorker(slot, name) {
844
889
  const worker = createImageWorker();
845
890
  if (!worker)
846
891
  return;
847
892
  slot.worker = worker;
848
893
  worker.on("message", (message) => this.handleWorkerMessage(slot, message));
849
- worker.once("error", () => this.disableWorker(slot));
850
- 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
+ });
851
902
  }
852
- disableWorker(slot) {
853
- if (this.destroyed)
854
- return;
855
- slot.worker = null;
856
- slot.busy = false;
857
- slot.inFlightKey = "";
858
- const queued = slot.queuedRequest;
859
- slot.queuedRequest = null;
860
- slot.queuedKey = "";
861
- if (queued?.layer === "full")
862
- 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
+ }
863
913
  }
864
914
  enqueueWorkerRender(slot, request) {
865
- if (!slot.worker) {
866
- this.renderSync(slot, request);
867
- return;
868
- }
915
+ this.throwIfFatal();
869
916
  if (slot.busy) {
870
917
  slot.queuedRequest = request;
871
918
  slot.queuedKey = request.key;
@@ -874,13 +921,16 @@ class ImageVisualLayer {
874
921
  this.dispatchWorkerRender(slot, request);
875
922
  }
876
923
  dispatchWorkerRender(slot, request) {
877
- if (!slot.worker) {
878
- this.renderSync(slot, request);
879
- return;
880
- }
924
+ this.throwIfFatal();
925
+ if (!slot.worker)
926
+ this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
881
927
  slot.busy = true;
882
928
  slot.inFlightKey = request.key;
883
- slot.worker.postMessage(request);
929
+ try {
930
+ slot.worker.postMessage(request);
931
+ } catch (error) {
932
+ this.failWorker(String(request.layer ?? "visual"), error);
933
+ }
884
934
  }
885
935
  handleWorkerMessage(slot, message) {
886
936
  if (this.destroyed || !message || typeof message !== "object")
@@ -888,14 +938,14 @@ class ImageVisualLayer {
888
938
  const response = message;
889
939
  slot.busy = false;
890
940
  slot.inFlightKey = "";
891
- if (response.requestId <= slot.clearedThroughRequestId)
892
- return;
893
- if (response.error) {
894
- this.disableWorker(slot);
895
- return;
896
- }
897
- if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "panel-chrome")) {
898
- 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
+ }
899
949
  }
900
950
  const next = slot.queuedRequest;
901
951
  slot.queuedRequest = null;
@@ -903,611 +953,12 @@ class ImageVisualLayer {
903
953
  if (next && !this.isKnownKey(slot, next.key))
904
954
  this.dispatchWorkerRender(slot, next);
905
955
  }
906
- renderSync(slot, request) {
907
- const result = renderImageVisualFrame(request);
908
- if (!result || this.isKnownKey(slot, result.key) || this.isOutputBackedUp())
909
- return;
910
- this.stageFrame(result);
911
- }
912
- }
913
- 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;
914
- var init_image_visual_layer = __esm(() => {
915
- BG = [7, 8, 9, 255];
916
- PANEL = [16, 17, 21, 184];
917
- PANEL_HEADER = [16, 17, 21, 190];
918
- PANEL_LINE = [255, 255, 255, 24];
919
- LIME = [204, 255, 77, 255];
920
- LIME_DIM = [169, 214, 63, 255];
921
- CYAN = [86, 216, 255, 255];
922
- MAGENTA = [255, 121, 176, 255];
923
- INK_DIM = [108, 110, 121, 255];
924
- PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
925
- CRC_TABLE = makeCrcTable();
926
- MAX_IMAGE_BYTES = 48 * 1024 * 1024;
927
- panelMaskCache = new Map;
928
- BG_WORD = rgbaWord(BG);
929
- DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
930
- DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
931
- MIN_BACKGROUND_BUDGET_BYTES = 24 * 1024;
932
- MIN_PANEL_BUDGET_BYTES = 56 * 1024;
933
- });
934
-
935
- // packages/cli/src/app-opentui/render/image-visual-layer-native-canvas.ts
936
- var exports_image_visual_layer_native_canvas = {};
937
- __export(exports_image_visual_layer_native_canvas, {
938
- renderNativeCanvasImageVisualFrame: () => renderNativeCanvasImageVisualFrame
939
- });
940
- import { mkdirSync, writeFileSync } from "fs";
941
- import { join } from "path";
942
- import { tmpdir } from "os";
943
- import { clearAllCache, createCanvas } from "@napi-rs/canvas";
944
- function droneLayerScale2() {
945
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_DRONE_SCALE ?? "");
946
- if (Number.isFinite(raw) && raw > 0)
947
- return Math.max(0.15, Math.min(1, raw));
948
- return DEFAULT_DRONE_LAYER_SCALE2;
949
- }
950
- function kittyTransmission() {
951
- return process.env.RIG_OPENTUI_KITTY_TRANSMISSION === "temp-file" ? "temp-file" : "direct";
952
- }
953
- function motionDensity() {
954
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_MOTION_DENSITY ?? "");
955
- if (Number.isFinite(raw) && raw > 0)
956
- return Math.max(0.2, Math.min(1.4, raw));
957
- return DEFAULT_MOTION_DENSITY;
958
- }
959
- function motionSpeed() {
960
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_MOTION_SPEED ?? "");
961
- if (Number.isFinite(raw) && raw > 0)
962
- return Math.max(0.25, Math.min(1.5, raw));
963
- return DEFAULT_MOTION_SPEED;
964
- }
965
- function resetContext(ctx, width, height) {
966
- ctx.reset();
967
- ctx.clearRect(0, 0, width, height);
968
- ctx.imageSmoothingEnabled = true;
969
- ctx.imageSmoothingQuality = "high";
970
- }
971
- function reusableCanvas(width, height) {
972
- if (!frameCanvas || !frameCtx || frameCanvas.width !== width || frameCanvas.height !== height) {
973
- frameCanvas = createCanvas(width, height);
974
- frameCtx = frameCanvas.getContext("2d");
975
- }
976
- resetContext(frameCtx, width, height);
977
- return { canvas: frameCanvas, ctx: frameCtx };
978
- }
979
- function reusableSceneCanvas(width, height) {
980
- if (!sceneCanvas || !sceneCtx || sceneCanvas.width !== width || sceneCanvas.height !== height) {
981
- sceneCanvas = createCanvas(width, height);
982
- sceneCtx = sceneCanvas.getContext("2d");
983
- }
984
- resetContext(sceneCtx, width, height);
985
- return { canvas: sceneCanvas, ctx: sceneCtx };
986
- }
987
- function reusablePatchCanvas(width, height) {
988
- if (!patchCanvas || !patchCtx || patchCanvas.width !== width || patchCanvas.height !== height) {
989
- patchCanvas = createCanvas(width, height);
990
- patchCtx = patchCanvas.getContext("2d");
991
- }
992
- resetContext(patchCtx, width, height);
993
- return { canvas: patchCanvas, ctx: patchCtx };
994
- }
995
- function css(color, alphaScale = 1) {
996
- return `rgba(${color[0]},${color[1]},${color[2]},${Math.max(0, Math.min(1, color[3] / 255 * alphaScale))})`;
997
- }
998
- function visualSceneKind2(scene) {
999
- if (scene === "tasks")
1000
- return "loop";
1001
- if (scene === "help")
1002
- return "carrier";
1003
- return "dispatch";
1004
- }
1005
- function seededUnit2(seed) {
1006
- let value = seed >>> 0;
1007
- value = value * 1664525 + 1013904223 >>> 0;
1008
- return value / 4294967295;
1009
- }
1010
- function sceneCore2(width, height, kind) {
1011
- if (kind === "dispatch")
1012
- return { x: width * 0.3, y: height * 0.45 };
1013
- if (kind === "carrier")
1014
- return { x: width * 0.5, y: height * 0.54 };
1015
- return { x: width * 0.09, y: height * 0.56 };
1016
- }
1017
- function dispatchLane2(width, index, count) {
1018
- const a = seededUnit2(28672 + index * 101);
1019
- const b = seededUnit2(36864 + index * 131);
1020
- const c = seededUnit2(45056 + index * 151);
1021
- const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
1022
- return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
1023
- }
1024
- function lanePoint2(width, height, lane, p, kind = "dispatch") {
1025
- const core = sceneCore2(width, height, kind);
1026
- const dist = lane.len * p;
1027
- const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
1028
- const ux = Math.cos(lane.ang);
1029
- const uy = Math.sin(lane.ang);
1030
- return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
1031
- }
1032
- function drawGlow2(ctx, cx, cy, radius, color, alpha = 0.18) {
1033
- if (radius <= 0)
1034
- return;
1035
- const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, radius);
1036
- gradient.addColorStop(0, css(color, alpha));
1037
- gradient.addColorStop(0.45, css(color, alpha * 0.28));
1038
- gradient.addColorStop(1, css(color, 0));
1039
- ctx.fillStyle = gradient;
1040
- ctx.beginPath();
1041
- ctx.arc(cx, cy, radius, 0, Math.PI * 2);
1042
- ctx.fill();
1043
- }
1044
- function drawDisc2(ctx, cx, cy, radius, color, alpha = 1) {
1045
- if (radius <= 0)
1046
- return;
1047
- ctx.fillStyle = css(color, alpha);
1048
- ctx.beginPath();
1049
- ctx.arc(cx, cy, radius, 0, Math.PI * 2);
1050
- ctx.fill();
1051
- }
1052
- function drawLine2(ctx, x0, y0, x1, y1, color, alpha = 0.5, thickness = 1.4) {
1053
- ctx.save();
1054
- ctx.strokeStyle = css(color, alpha);
1055
- ctx.lineWidth = thickness * 2;
1056
- ctx.lineCap = "round";
1057
- ctx.lineJoin = "round";
1058
- ctx.beginPath();
1059
- ctx.moveTo(x0, y0);
1060
- ctx.lineTo(x1, y1);
1061
- ctx.stroke();
1062
- ctx.restore();
1063
- }
1064
- function drawDrone2(ctx, x, y, scale, tick, color) {
1065
- const speed = motionSpeed();
1066
- const wobble = Math.sin(tick * 0.055 * speed) * scale * 0.35;
1067
- const cy = y + wobble;
1068
- drawGlow2(ctx, x, cy, scale * 7, color, 0.18);
1069
- drawDisc2(ctx, x, cy, scale * 1.45, color, 0.92);
1070
- drawDisc2(ctx, x, cy, scale * 0.55, [235, 255, 176, 255], 0.85);
1071
- const arm = scale * 4.1;
1072
- const rotor = scale * 1.6;
1073
- const arms = [[-arm, -arm * 0.72], [arm, -arm * 0.72], [-arm, arm * 0.72], [arm, arm * 0.72]];
1074
- for (const [dx, dy] of arms) {
1075
- drawLine2(ctx, x, cy, x + dx, cy + dy, color, 0.42, Math.max(1, scale * 0.22));
1076
- drawGlow2(ctx, x + dx, cy + dy, rotor * 2.6, color, 0.1);
1077
- drawDisc2(ctx, x + dx, cy + dy, rotor, color, 0.34 + 0.12 * Math.sin(tick * 0.15 * speed));
1078
- }
1079
- }
1080
- function drawMarketingRails2(ctx, width, height, kind, tick) {
1081
- const density = motionDensity();
1082
- const speed = motionSpeed();
1083
- const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
1084
- ys.forEach((ratio, index) => {
1085
- const y = height * ratio + Math.sin(tick * 0.018 * speed + index) * Math.max(2, height * 0.0025);
1086
- const color = index === 2 ? CYAN2 : index === 1 ? LIME2 : LIME_DIM2;
1087
- 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));
1088
- const spacing = (0.18 + index * 0.02) / density;
1089
- for (let p = (tick * 0.004 * speed + index * 0.21) % 1;p < 1; p += spacing) {
1090
- const x = width * (0.06 + 0.88 * p);
1091
- drawDisc2(ctx, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.18);
1092
- drawGlow2(ctx, x, y, Math.max(5, height * 0.006), color, 0.026);
1093
- }
1094
- });
1095
- }
1096
- function drawDispatchScene2(ctx, width, height, tick) {
1097
- const density = motionDensity();
1098
- const speed = motionSpeed();
1099
- const core = sceneCore2(width, height, "dispatch");
1100
- const min = Math.min(width, height);
1101
- drawGlow2(ctx, core.x, core.y, min * 0.085, LIME2, 0.12);
1102
- drawDisc2(ctx, core.x, core.y, min * 0.0065, LIME2, 0.85);
1103
- const laneCount = Math.max(4, Math.round((width < 1000 ? 7 : 12) * (0.65 + density * 0.35)));
1104
- const lanes = Array.from({ length: laneCount }, (_, index) => dispatchLane2(width, index, laneCount));
1105
- lanes.forEach((lane) => {
1106
- let prev = lanePoint2(width, height, lane, 0);
1107
- ctx.beginPath();
1108
- ctx.moveTo(prev.x, prev.y);
1109
- for (let step = 1;step <= 32; step += 1) {
1110
- const next = lanePoint2(width, height, lane, step / 32);
1111
- ctx.lineTo(next.x, next.y);
1112
- prev = next;
1113
- }
1114
- ctx.strokeStyle = css(lane.phase > Math.PI ? CYAN2 : LIME_DIM2, 0.045);
1115
- ctx.lineWidth = Math.max(0.7, min * 0.0009) * 2;
1116
- ctx.lineCap = "round";
1117
- ctx.stroke();
1118
- });
1119
- const packets = Math.max(24, Math.round((width < 1000 ? 70 : 150) * density));
1120
- for (let i = 0;i < packets; i += 1) {
1121
- const lane = lanes[i % lanes.length];
1122
- const packetSpeed = (0.003 + seededUnit2(12288 + i * 17) * 0.009) * speed;
1123
- const p = (seededUnit2(16384 + i * 19) + tick * packetSpeed) % 1;
1124
- const pt = lanePoint2(width, height, lane, p);
1125
- const wobble = (seededUnit2(20480 + i * 23) - 0.5) * min * 0.012;
1126
- const fade = p < 0.1 ? p / 0.1 : p > 0.88 ? (1 - p) / 0.12 : 1;
1127
- const color = seededUnit2(24576 + i * 29) < 0.22 ? CYAN2 : LIME_DIM2;
1128
- const y = pt.y + wobble * Math.sin(p * Math.PI * 2);
1129
- 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));
1130
- drawDisc2(ctx, pt.x, y, Math.max(1.3, min * 0.0022), color, 0.5 * fade);
1131
- drawGlow2(ctx, pt.x, y, Math.max(4, min * 0.008), color, 0.045 * fade);
1132
- }
1133
- const droneCount = Math.max(2, Math.round((width < 1000 ? 4 : 8) * density));
1134
- for (let i = 0;i < droneCount; i += 1) {
1135
- const lane = lanes[i % lanes.length];
1136
- const p = (tick * 0.002 * speed + i * 0.13) % 1;
1137
- const pt = lanePoint2(width, height, lane, p);
1138
- 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);
1139
- }
1140
- }
1141
- function drawLoopScene2(ctx, width, height, tick) {
1142
- const density = motionDensity();
1143
- const speed = motionSpeed();
1144
- const min = Math.min(width, height);
1145
- const y = height * 0.56;
1146
- const x0 = width * 0.09;
1147
- const x1 = width * 0.91;
1148
- drawLine2(ctx, x0, y, x1, y, LIME_DIM2, 0.12, Math.max(0.8, min * 0.001));
1149
- const gates = width < 1000 ? 4 : 5;
1150
- const active = Math.floor(tick * speed / 20) % gates;
1151
- for (let i = 0;i < gates; i += 1) {
1152
- const x = x0 + (x1 - x0) * (i / Math.max(1, gates - 1));
1153
- const color = i === active ? LIME2 : i <= active ? LIME_DIM2 : INK_DIM2;
1154
- 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));
1155
- drawDisc2(ctx, x, y, i === active ? min * 0.006 : min * 0.0035, color, i === active ? 0.72 : 0.25);
1156
- drawGlow2(ctx, x, y, i === active ? min * 0.018 : min * 0.01, color, i === active ? 0.1 : 0.035);
1157
- }
1158
- const packets = Math.max(16, Math.round((width < 1000 ? 36 : 84) * density));
1159
- for (let i = 0;i < packets; i += 1) {
1160
- const p = (seededUnit2(33024 + i * 13) + tick * (0.0022 + seededUnit2(33280 + i * 17) * 0.004) * speed) % 1;
1161
- const x = x0 + (x1 - x0) * p;
1162
- const segment = Math.min(gates - 1, Math.floor(p * gates));
1163
- const color = segment < active ? LIME_DIM2 : segment === active ? CYAN2 : INK_DIM2;
1164
- const py = y + (seededUnit2(33536 + i * 19) - 0.5) * min * 0.018 * Math.sin(p * Math.PI * 2);
1165
- drawLine2(ctx, x - min * 0.014, py, x, py, color, 0.1, Math.max(0.7, min * 0.0008));
1166
- drawDisc2(ctx, x, py, Math.max(1.2, min * 0.002), color, color === INK_DIM2 ? 0.22 : 0.58);
1167
- }
1168
- drawDrone2(ctx, x0 + (x1 - x0) * (tick * 0.003 * speed % 1), y - min * 0.04, min * 0.0065, tick, LIME_DIM2);
1169
- drawDrone2(ctx, x0 + (x1 - x0) * ((tick * 0.002 * speed + 0.5) % 1), y + min * 0.036, min * 0.0055, tick + 40, MAGENTA2);
1170
- }
1171
- function drawCarrierScene2(ctx, width, height, tick) {
1172
- const density = motionDensity();
1173
- const speed = motionSpeed();
1174
- const min = Math.min(width, height);
1175
- const core = sceneCore2(width, height, "carrier");
1176
- drawGlow2(ctx, core.x, core.y, min * 0.075, LIME2, 0.12);
1177
- drawDisc2(ctx, core.x, core.y, min * 0.0065, LIME2, 0.92);
1178
- const nodes = Math.max(4, Math.round((width < 1000 ? 5 : 7) * (0.75 + density * 0.25)));
1179
- const rx = width * 0.36;
1180
- const ry = height * 0.22;
1181
- for (let i = 0;i < nodes; i += 1) {
1182
- const a = i / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006 * speed;
1183
- const x = core.x + Math.cos(a) * rx;
1184
- const y = core.y + Math.sin(a) * ry;
1185
- const color = i % 2 ? CYAN2 : LIME_DIM2;
1186
- drawLine2(ctx, core.x, core.y, x, y, color, 0.055, Math.max(0.7, min * 0.0008));
1187
- drawGlow2(ctx, x, y, min * 0.018, color, 0.055);
1188
- drawDisc2(ctx, x, y, min * 0.0048, color, 0.62);
1189
- drawDrone2(ctx, x, y - min * 0.018, min * 0.005, tick + i * 9, color);
1190
- }
1191
- const packets = Math.max(14, Math.round((width < 1000 ? 30 : 72) * density));
1192
- for (let i = 0;i < packets; i += 1) {
1193
- const lane = i % nodes;
1194
- const a = lane / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006 * speed;
1195
- const nx = core.x + Math.cos(a) * rx;
1196
- const ny = core.y + Math.sin(a) * ry;
1197
- const p = (seededUnit2(37120 + i * 37) + tick * (0.003 + seededUnit2(37376 + i * 43) * 0.009) * speed) % 1;
1198
- const x = core.x + (nx - core.x) * p;
1199
- const y = core.y + (ny - core.y) * p;
1200
- const color = seededUnit2(37632 + i * 47) < 0.35 ? CYAN2 : LIME_DIM2;
1201
- drawDisc2(ctx, x, y, Math.max(1.2, min * 0.002), color, 0.48);
1202
- drawGlow2(ctx, x, y, min * 0.008, color, 0.035);
1203
- }
1204
- }
1205
- function drawBackground2(ctx, width, height, tick, scene) {
1206
- ctx.fillStyle = css(BG2);
1207
- ctx.fillRect(0, 0, width, height);
1208
- const kind = visualSceneKind2(scene);
1209
- drawGlow2(ctx, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME2, 0.035);
1210
- drawGlow2(ctx, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN2, 0.025);
1211
- drawMarketingRails2(ctx, width, height, kind, tick);
1212
- if (kind === "loop")
1213
- drawLoopScene2(ctx, width, height, tick);
1214
- else if (kind === "carrier")
1215
- drawCarrierScene2(ctx, width, height, tick);
1216
- else
1217
- drawDispatchScene2(ctx, width, height, tick);
1218
- }
1219
- function drawScaledDroneLayer(ctx, width, height, tick, scene) {
1220
- const scale = droneLayerScale2();
1221
- if (scale >= 0.99) {
1222
- drawBackground2(ctx, width, height, tick, scene);
1223
- return;
1224
- }
1225
- const sceneWidth = Math.max(1, Math.round(width * scale));
1226
- const sceneHeight = Math.max(1, Math.round(height * scale));
1227
- const { canvas, ctx: sceneCtx2 } = reusableSceneCanvas(sceneWidth, sceneHeight);
1228
- drawBackground2(sceneCtx2, sceneWidth, sceneHeight, tick, scene);
1229
- ctx.save();
1230
- ctx.imageSmoothingEnabled = true;
1231
- ctx.imageSmoothingQuality = "high";
1232
- ctx.drawImage(canvas, 0, 0, sceneWidth, sceneHeight, 0, 0, width, height);
1233
- ctx.restore();
1234
- }
1235
- function panelPlacement(layout, panel, size) {
1236
- const cellW = size.width / layout.width;
1237
- const cellH = size.height / layout.height;
1238
- const leftCells = layout.centerLeft + (panel.left ?? 0);
1239
- const topCells = layout.centerTop + panel.top;
1240
- const widthCells = panel.width ?? layout.centerWidth;
1241
- const heightCells = panel.height;
1242
- const left = Math.round(leftCells * cellW);
1243
- const top = Math.round(topCells * cellH);
1244
- const width = Math.round(widthCells * cellW);
1245
- const height = Math.round(heightCells * cellH);
1246
- return width > 8 && height > 8 ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
1247
- }
1248
- function panelRect2(layout, panel, size) {
1249
- return panelPlacement(layout, panel, size);
1250
- }
1251
- function clipRoundRect(ctx, rect, radius) {
1252
- ctx.beginPath();
1253
- ctx.roundRect(rect.left, rect.top, rect.width, rect.height, radius);
1254
- ctx.clip();
1255
- }
1256
- function strokeRoundRect(ctx, rect, radius) {
1257
- ctx.beginPath();
1258
- ctx.roundRect(rect.left + 0.75, rect.top + 0.75, rect.width - 1.5, rect.height - 1.5, Math.max(1, radius - 0.75));
1259
- ctx.stroke();
1260
- }
1261
- function compositePanel2(ctx, sourceCanvas, rect, headerPx) {
1262
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(rect.width, rect.height) * 0.035)));
1263
- const { canvas: patch, ctx: patchCtx2 } = reusablePatchCanvas(rect.width, rect.height);
1264
- patchCtx2.drawImage(sourceCanvas, rect.left, rect.top, rect.width, rect.height, 0, 0, rect.width, rect.height);
1265
- ctx.save();
1266
- clipRoundRect(ctx, rect, radius);
1267
- ctx.filter = `blur(${Math.max(10, Math.round(Math.min(rect.width, rect.height) * 0.018))}px)`;
1268
- ctx.drawImage(patch, 0, 0, rect.width, rect.height, rect.left, rect.top, rect.width, rect.height);
1269
- ctx.filter = "none";
1270
- ctx.fillStyle = css(PANEL2);
1271
- ctx.fillRect(rect.left, rect.top, rect.width, rect.height);
1272
- ctx.fillStyle = css(PANEL_HEADER2);
1273
- ctx.fillRect(rect.left, rect.top, rect.width, headerPx);
1274
- ctx.restore();
1275
- ctx.save();
1276
- ctx.strokeStyle = css(PANEL_LINE2, 0.8);
1277
- ctx.lineWidth = 1.5;
1278
- strokeRoundRect(ctx, rect, radius);
1279
- ctx.restore();
1280
- const dividerY = Math.round(rect.top + headerPx);
1281
- if (dividerY > rect.top && dividerY < rect.top + rect.height) {
1282
- drawLine2(ctx, rect.left + radius, dividerY, rect.left + rect.width - radius, dividerY, PANEL_LINE2, 0.28, 0.7);
1283
- }
1284
- }
1285
- function compositePanelPatch(ctx, sourceCanvas, width, height, headerPx) {
1286
- const rect = { left: 0, top: 0, width, height };
1287
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(width, height) * 0.035)));
1288
- ctx.save();
1289
- clipRoundRect(ctx, rect, radius);
1290
- ctx.filter = `blur(${Math.max(10, Math.round(Math.min(width, height) * 0.018))}px)`;
1291
- ctx.drawImage(sourceCanvas, 0, 0, width, height);
1292
- ctx.filter = "none";
1293
- ctx.fillStyle = css(PANEL2);
1294
- ctx.fillRect(0, 0, width, height);
1295
- ctx.fillStyle = css(PANEL_HEADER2);
1296
- ctx.fillRect(0, 0, width, headerPx);
1297
- ctx.restore();
1298
- }
1299
- function compositePanelChromePatch(ctx, width, height, headerPx) {
1300
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(width, height) * 0.035)));
1301
- ctx.clearRect(0, 0, width, height);
1302
- const dividerY = Math.round(headerPx);
1303
- if (dividerY > 0 && dividerY < height)
1304
- drawLine2(ctx, radius, dividerY, width - radius, dividerY, PANEL_LINE2, 0.22, 0.65);
1305
- }
1306
- async function encodePanelPatch(input, placement, imageId, headerPx) {
1307
- const { canvas: sourceCanvas, ctx: sourceCtx } = reusableSceneCanvas(placement.width, placement.height);
1308
- sourceCtx.save();
1309
- sourceCtx.translate(-placement.left, -placement.top);
1310
- drawScaledDroneLayer(sourceCtx, input.pixelSize.width, input.pixelSize.height, input.tick, input.scene);
1311
- sourceCtx.restore();
1312
- const { canvas: patchCanvas2, ctx: patchCtx2 } = reusablePatchCanvas(placement.width, placement.height);
1313
- compositePanelPatch(patchCtx2, sourceCanvas, placement.width, placement.height, headerPx);
1314
- const png = await patchCanvas2.encode("png");
1315
- const sequence = kittyPngFrameSequence(png, { columns: Math.max(1, Math.round(placement.widthCells)), rows: Math.max(1, Math.round(placement.heightCells)), imageId, zIndex: -12 });
1316
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
1317
- }
1318
- async function encodePanelChromePatch(placement, imageId, headerPx) {
1319
- const { canvas: patchCanvas2, ctx: patchCtx2 } = reusablePatchCanvas(placement.width, placement.height);
1320
- compositePanelChromePatch(patchCtx2, placement.width, placement.height, headerPx);
1321
- const png = await patchCanvas2.encode("png");
1322
- const sequence = kittyPngFrameSequence(png, { columns: Math.max(1, Math.round(placement.widthCells)), rows: Math.max(1, Math.round(placement.heightCells)), imageId, zIndex: -8 });
1323
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
1324
- }
1325
- function kittyImageSequence2(base64, options) {
1326
- const params = [`a=T`, `f=100`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
1327
- if (base64.length <= KITTY_CHUNK_SIZE2)
1328
- return `\x1B_G${params.join(",")};${base64}\x1B\\`;
1329
- const chunks = [];
1330
- for (let offset = 0;offset < base64.length; offset += KITTY_CHUNK_SIZE2) {
1331
- const part = base64.slice(offset, offset + KITTY_CHUNK_SIZE2);
1332
- const last = offset + KITTY_CHUNK_SIZE2 >= base64.length;
1333
- chunks.push(`\x1B_G${offset === 0 ? `${params.join(",")},` : ""}m=${last ? 0 : 1};${part}\x1B\\`);
1334
- }
1335
- return chunks.join("");
1336
- }
1337
- function kittyTempFileImageSequence(png, options) {
1338
- mkdirSync(TEMP_GRAPHICS_DIR, { recursive: true });
1339
- const path = join(TEMP_GRAPHICS_DIR, `frame-${process.pid}-${Date.now()}-${encodedFrames}.tty-graphics-protocol.png`);
1340
- writeFileSync(path, png);
1341
- const payload = Buffer.from(path).toString("base64");
1342
- 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}`];
1343
- return `\x1B_G${params.join(",")};${payload}\x1B\\`;
1344
956
  }
1345
- function kittyPngFrameSequence(png, options) {
1346
- if (kittyTransmission() === "temp-file")
1347
- return kittyTempFileImageSequence(png, options);
1348
- return kittyImageSequence2(png.toString("base64"), options);
1349
- }
1350
- async function renderNativeCanvasImageVisualFrame(input) {
1351
- if (input.transport !== "png")
1352
- return null;
1353
- const width = Math.max(1, Math.floor(input.pixelSize.width));
1354
- const height = Math.max(1, Math.floor(input.pixelSize.height));
1355
- if (width * height * 4 > MAX_IMAGE_BYTES2)
1356
- return null;
1357
- const layer = input.layer ?? "full";
1358
- if (layer === "split") {
1359
- const backgroundScale = droneLayerScale2();
1360
- const backgroundWidth = Math.max(input.layout.width, Math.round(width * backgroundScale));
1361
- const backgroundHeight = Math.max(input.layout.height, Math.round(height * backgroundScale));
1362
- const { canvas: backgroundCanvas, ctx: backgroundCtx } = reusableCanvas(backgroundWidth, backgroundHeight);
1363
- drawBackground2(backgroundCtx, backgroundWidth, backgroundHeight, input.tick, input.scene);
1364
- const backgroundPng = await backgroundCanvas.encode("png");
1365
- const sequences = [kittyPngFrameSequence(backgroundPng, { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: -30 })];
1366
- let panelIndex = 0;
1367
- for (const panel of input.panels) {
1368
- if (panel.chrome !== "ad-terminal")
1369
- continue;
1370
- const placement = panelPlacement(input.layout, panel, { width, height });
1371
- if (!placement)
1372
- continue;
1373
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
1374
- const cellH = height / input.layout.height;
1375
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1376
- sequences.push(await encodePanelPatch(input, placement, imageId, headerPx));
1377
- panelIndex += 1;
1378
- }
1379
- encodedFrames += 1;
1380
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1381
- clearAllCache();
1382
- return {
1383
- key: imageVisualFrameKey(input),
1384
- sequence: sequences.join("")
1385
- };
1386
- }
1387
- if (layer === "panel-patches") {
1388
- const sequences = [];
1389
- let panelIndex = 0;
1390
- for (const panel of input.panels) {
1391
- if (panel.chrome !== "ad-terminal")
1392
- continue;
1393
- const placement = panelPlacement(input.layout, panel, { width, height });
1394
- if (!placement)
1395
- continue;
1396
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
1397
- const cellH = height / input.layout.height;
1398
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1399
- sequences.push(await encodePanelPatch(input, placement, imageId, headerPx));
1400
- panelIndex += 1;
1401
- }
1402
- encodedFrames += 1;
1403
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1404
- clearAllCache();
1405
- return {
1406
- key: imageVisualFrameKey(input),
1407
- sequence: sequences.join("")
1408
- };
1409
- }
1410
- if (layer === "panel-chrome") {
1411
- const sequences = [];
1412
- let panelIndex = 0;
1413
- for (const panel of input.panels) {
1414
- if (panel.chrome !== "ad-terminal")
1415
- continue;
1416
- const placement = panelPlacement(input.layout, panel, { width, height });
1417
- if (!placement)
1418
- continue;
1419
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 2000 + panelIndex >>> 0 || panelIndex + 1);
1420
- const cellH = height / input.layout.height;
1421
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1422
- sequences.push(await encodePanelChromePatch(placement, imageId, headerPx));
1423
- panelIndex += 1;
1424
- }
1425
- encodedFrames += 1;
1426
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1427
- clearAllCache();
1428
- return {
1429
- key: imageVisualFrameKey(input),
1430
- sequence: sequences.join("")
1431
- };
1432
- }
1433
- const { canvas, ctx } = reusableCanvas(width, height);
1434
- if (layer === "background") {
1435
- drawBackground2(ctx, width, height, input.tick, input.scene);
1436
- } else if (layer === "panels") {
1437
- const { canvas: sourceCanvas, ctx: sourceCtx } = reusableSceneCanvas(width, height);
1438
- drawScaledDroneLayer(sourceCtx, width, height, input.tick, input.scene);
1439
- for (const panel of input.panels) {
1440
- if (panel.chrome !== "ad-terminal")
1441
- continue;
1442
- const rect = panelRect2(input.layout, panel, { width, height });
1443
- if (!rect)
1444
- continue;
1445
- const cellH = height / input.layout.height;
1446
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1447
- compositePanel2(ctx, sourceCanvas, rect, headerPx);
1448
- }
1449
- } else {
1450
- drawScaledDroneLayer(ctx, width, height, input.tick, input.scene);
1451
- for (const panel of input.panels) {
1452
- if (panel.chrome !== "ad-terminal")
1453
- continue;
1454
- const rect = panelRect2(input.layout, panel, { width, height });
1455
- if (!rect)
1456
- continue;
1457
- const cellH = height / input.layout.height;
1458
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1459
- compositePanel2(ctx, canvas, rect, headerPx);
1460
- }
1461
- }
1462
- const png = await canvas.encode("png");
1463
- encodedFrames += 1;
1464
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1465
- clearAllCache();
1466
- return {
1467
- key: imageVisualFrameKey(input),
1468
- sequence: kittyPngFrameSequence(png, { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: input.zIndex })
1469
- };
1470
- }
1471
- 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;
1472
- var init_image_visual_layer_native_canvas = __esm(() => {
1473
- init_image_visual_layer();
1474
- BG2 = [7, 8, 9, 255];
1475
- PANEL2 = [16, 17, 21, 184];
1476
- PANEL_HEADER2 = [16, 17, 21, 190];
1477
- PANEL_LINE2 = [255, 255, 255, 24];
1478
- LIME2 = [204, 255, 77, 255];
1479
- LIME_DIM2 = [169, 214, 63, 255];
1480
- CYAN2 = [86, 216, 255, 255];
1481
- MAGENTA2 = [255, 121, 176, 255];
1482
- INK_DIM2 = [108, 110, 121, 255];
1483
- MAX_IMAGE_BYTES2 = 48 * 1024 * 1024;
1484
- TEMP_GRAPHICS_DIR = join(tmpdir(), "rig-tty-graphics-protocol");
1485
- });
1486
957
 
1487
958
  // packages/cli/src/app-opentui/render/image-visual-layer-worker.ts
1488
- init_image_visual_layer();
1489
- import { parentPort } from "worker_threads";
1490
- var nativeCanvasRenderer;
1491
959
  async function renderFrame(request) {
1492
- if (process.env.RIG_OPENTUI_IMAGE_BACKEND !== "ts" && request.transport === "png") {
1493
- if (nativeCanvasRenderer === undefined) {
1494
- try {
1495
- const mod = await Promise.resolve().then(() => (init_image_visual_layer_native_canvas(), exports_image_visual_layer_native_canvas));
1496
- nativeCanvasRenderer = mod.renderNativeCanvasImageVisualFrame;
1497
- } catch {
1498
- nativeCanvasRenderer = null;
1499
- }
1500
- }
1501
- if (nativeCanvasRenderer) {
1502
- try {
1503
- const native = await nativeCanvasRenderer(request);
1504
- if (native)
1505
- return native;
1506
- } catch {
1507
- nativeCanvasRenderer = null;
1508
- }
1509
- }
1510
- }
960
+ if (request.transport !== "png")
961
+ throw new Error("OpenTUI image workers require PNG transport.");
1511
962
  return renderImageVisualFrame(request);
1512
963
  }
1513
964
  function isImageVisualWorkerRequest(value) {