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

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 (105) hide show
  1. package/dist/bin/rig.js +17034 -11249
  2. package/dist/src/app/board.js +32 -3
  3. package/dist/src/app/drone-ui.js +1 -1
  4. package/dist/src/app/theme.js +1 -0
  5. package/dist/src/app-opentui/adapters/common.d.ts +53 -0
  6. package/dist/src/app-opentui/adapters/common.js +149 -0
  7. package/dist/src/app-opentui/adapters/doctor.d.ts +11 -0
  8. package/dist/src/app-opentui/adapters/doctor.js +780 -0
  9. package/dist/src/app-opentui/adapters/fleet.d.ts +16 -0
  10. package/dist/src/app-opentui/adapters/fleet.js +874 -0
  11. package/dist/src/app-opentui/adapters/inbox.d.ts +33 -0
  12. package/dist/src/app-opentui/adapters/inbox.js +1454 -0
  13. package/dist/src/app-opentui/adapters/init.d.ts +13 -0
  14. package/dist/src/app-opentui/adapters/init.js +2357 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +16 -0
  16. package/dist/src/app-opentui/adapters/pi-attach.js +1295 -0
  17. package/dist/src/app-opentui/adapters/run-detail.d.ts +20 -0
  18. package/dist/src/app-opentui/adapters/run-detail.js +893 -0
  19. package/dist/src/app-opentui/adapters/server.d.ts +14 -0
  20. package/dist/src/app-opentui/adapters/server.js +798 -0
  21. package/dist/src/app-opentui/adapters/tasks.d.ts +55 -0
  22. package/dist/src/app-opentui/adapters/tasks.js +3472 -0
  23. package/dist/src/app-opentui/bootstrap.d.ts +16 -0
  24. package/dist/src/app-opentui/bootstrap.js +19509 -0
  25. package/dist/src/app-opentui/drone.d.ts +12 -0
  26. package/dist/src/app-opentui/drone.js +227 -0
  27. package/dist/src/app-opentui/events.d.ts +7 -0
  28. package/dist/src/app-opentui/events.js +28 -0
  29. package/dist/src/app-opentui/index.d.ts +8 -0
  30. package/dist/src/app-opentui/index.js +3477 -0
  31. package/dist/src/app-opentui/intent.d.ts +3 -0
  32. package/dist/src/app-opentui/intent.js +211 -0
  33. package/dist/src/app-opentui/layout.d.ts +15 -0
  34. package/dist/src/app-opentui/layout.js +44 -0
  35. package/dist/src/app-opentui/list-search.d.ts +8 -0
  36. package/dist/src/app-opentui/list-search.js +43 -0
  37. package/dist/src/app-opentui/pi-host-child.d.ts +2 -0
  38. package/dist/src/app-opentui/pi-host-child.js +778 -0
  39. package/dist/src/app-opentui/pi-pty-host.d.ts +64 -0
  40. package/dist/src/app-opentui/pi-pty-host.js +384 -0
  41. package/dist/src/app-opentui/registry.d.ts +4 -0
  42. package/dist/src/app-opentui/registry.js +6835 -0
  43. package/dist/src/app-opentui/render/graphics.d.ts +39 -0
  44. package/dist/src/app-opentui/render/graphics.js +537 -0
  45. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +2 -0
  46. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +1480 -0
  47. package/dist/src/app-opentui/render/image-visual-layer-worker.d.ts +1 -0
  48. package/dist/src/app-opentui/render/image-visual-layer-worker.js +1541 -0
  49. package/dist/src/app-opentui/render/image-visual-layer.d.ts +93 -0
  50. package/dist/src/app-opentui/render/image-visual-layer.js +945 -0
  51. package/dist/src/app-opentui/render/panels.d.ts +10 -0
  52. package/dist/src/app-opentui/render/panels.js +201 -0
  53. package/dist/src/app-opentui/render/scene.d.ts +16 -0
  54. package/dist/src/app-opentui/render/scene.js +110 -0
  55. package/dist/src/app-opentui/render/text.d.ts +10 -0
  56. package/dist/src/app-opentui/render/text.js +121 -0
  57. package/dist/src/app-opentui/render/type-bar.d.ts +14 -0
  58. package/dist/src/app-opentui/render/type-bar.js +137 -0
  59. package/dist/src/app-opentui/runtime.d.ts +2 -0
  60. package/dist/src/app-opentui/runtime.js +3406 -0
  61. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -0
  62. package/dist/src/app-opentui/scenes/doctor.js +91 -0
  63. package/dist/src/app-opentui/scenes/error.d.ts +2 -0
  64. package/dist/src/app-opentui/scenes/error.js +190 -0
  65. package/dist/src/app-opentui/scenes/fleet.d.ts +3 -0
  66. package/dist/src/app-opentui/scenes/fleet.js +207 -0
  67. package/dist/src/app-opentui/scenes/handoff.d.ts +2 -0
  68. package/dist/src/app-opentui/scenes/handoff.js +147 -0
  69. package/dist/src/app-opentui/scenes/help.d.ts +3 -0
  70. package/dist/src/app-opentui/scenes/help.js +138 -0
  71. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -0
  72. package/dist/src/app-opentui/scenes/inbox.js +118 -0
  73. package/dist/src/app-opentui/scenes/init.d.ts +2 -0
  74. package/dist/src/app-opentui/scenes/init.js +94 -0
  75. package/dist/src/app-opentui/scenes/main.d.ts +2 -0
  76. package/dist/src/app-opentui/scenes/main.js +96 -0
  77. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -0
  78. package/dist/src/app-opentui/scenes/run-detail.js +139 -0
  79. package/dist/src/app-opentui/scenes/server.d.ts +2 -0
  80. package/dist/src/app-opentui/scenes/server.js +88 -0
  81. package/dist/src/app-opentui/scenes/tasks.d.ts +3 -0
  82. package/dist/src/app-opentui/scenes/tasks.js +230 -0
  83. package/dist/src/app-opentui/state.d.ts +4 -0
  84. package/dist/src/app-opentui/state.js +286 -0
  85. package/dist/src/app-opentui/theme.d.ts +36 -0
  86. package/dist/src/app-opentui/theme.js +88 -0
  87. package/dist/src/app-opentui/types.d.ts +188 -0
  88. package/dist/src/app-opentui/types.js +1 -0
  89. package/dist/src/commands/_async-ui.js +1 -1
  90. package/dist/src/commands/_operator-view.js +1 -1
  91. package/dist/src/commands/_pi-frontend.js +1 -1
  92. package/dist/src/commands/_spinner.js +1 -1
  93. package/dist/src/commands/browser.js +1 -1
  94. package/dist/src/commands/doctor.js +1 -1
  95. package/dist/src/commands/github.js +1 -1
  96. package/dist/src/commands/inbox.js +1 -1
  97. package/dist/src/commands/init.js +1 -1
  98. package/dist/src/commands/inspect.js +1 -1
  99. package/dist/src/commands/run.js +1 -1
  100. package/dist/src/commands/setup.js +1 -1
  101. package/dist/src/commands/stats.js +1 -1
  102. package/dist/src/commands/task.js +1 -1
  103. package/dist/src/commands.js +33 -4
  104. package/dist/src/index.js +33 -4
  105. package/package.json +11 -8
@@ -0,0 +1,945 @@
1
+ // @bun
2
+ // packages/cli/src/app-opentui/render/image-visual-layer.ts
3
+ import { Worker } from "worker_threads";
4
+ import { deflateSync } from "zlib";
5
+ import { allocateImageId, deleteKittyImage, getCapabilities } from "@earendil-works/pi-tui";
6
+ var BG = [7, 8, 9, 255];
7
+ var PANEL = [16, 17, 21, 184];
8
+ var PANEL_HEADER = [16, 17, 21, 190];
9
+ var PANEL_LINE = [255, 255, 255, 24];
10
+ var LIME = [204, 255, 77, 255];
11
+ var LIME_DIM = [169, 214, 63, 255];
12
+ var CYAN = [86, 216, 255, 255];
13
+ var MAGENTA = [255, 121, 176, 255];
14
+ var INK_DIM = [108, 110, 121, 255];
15
+ var PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
16
+ var CRC_TABLE = makeCrcTable();
17
+ var MAX_IMAGE_BYTES = 48 * 1024 * 1024;
18
+ var KITTY_CHUNK_SIZE = 4096;
19
+ var PANEL_BLUR_SCALE = 5;
20
+ var PANEL_MASK_CACHE_LIMIT = 16;
21
+ var ZLIB_LEVEL = 1;
22
+ var DEFAULT_IMAGE_RESOLUTION_SCALE = 1;
23
+ var DEFAULT_DRONE_LAYER_SCALE = 0.18;
24
+ var DEFAULT_IMAGE_FPS = 24;
25
+ var panelMaskCache = new Map;
26
+ var BG_WORD = rgbaWord(BG);
27
+ function imageTransport() {
28
+ const forced = process.env.RIG_OPENTUI_IMAGE_TRANSPORT;
29
+ if (forced === "rgba-zlib")
30
+ return "rgba-zlib";
31
+ return "png";
32
+ }
33
+ function imageResolutionScale() {
34
+ const raw = Number.parseFloat(process.env.RIG_OPENTUI_IMAGE_SCALE ?? "");
35
+ if (Number.isFinite(raw) && raw > 0)
36
+ return Math.max(0.25, Math.min(1, raw));
37
+ return DEFAULT_IMAGE_RESOLUTION_SCALE;
38
+ }
39
+ function droneLayerScale() {
40
+ const raw = Number.parseFloat(process.env.RIG_OPENTUI_DRONE_SCALE ?? "");
41
+ if (Number.isFinite(raw) && raw > 0)
42
+ return Math.max(0.15, Math.min(1, raw));
43
+ return DEFAULT_DRONE_LAYER_SCALE;
44
+ }
45
+ function scaledPixelSize(size, scale, layout) {
46
+ return {
47
+ width: Math.max(layout.width, Math.floor(size.width * scale)),
48
+ height: Math.max(layout.height, Math.floor(size.height * scale))
49
+ };
50
+ }
51
+ function imageFps() {
52
+ const raw = Number.parseFloat(process.env.RIG_OPENTUI_IMAGE_FPS ?? "");
53
+ if (Number.isFinite(raw) && raw > 0)
54
+ return Math.max(10, Math.min(60, raw));
55
+ return DEFAULT_IMAGE_FPS;
56
+ }
57
+ function rgbaWord(color) {
58
+ return (color[3] << 24 | color[2] << 16 | color[1] << 8 | color[0]) >>> 0;
59
+ }
60
+ function fillRgba(data, color, word = rgbaWord(color)) {
61
+ if (data.byteOffset % 4 === 0 && data.byteLength % 4 === 0) {
62
+ new Uint32Array(data.buffer, data.byteOffset, data.byteLength / 4).fill(word);
63
+ return;
64
+ }
65
+ for (let i = 0;i < data.length; i += 4) {
66
+ data[i] = color[0];
67
+ data[i + 1] = color[1];
68
+ data[i + 2] = color[2];
69
+ data[i + 3] = color[3];
70
+ }
71
+ }
72
+ function makeCrcTable() {
73
+ const table = new Uint32Array(256);
74
+ for (let n = 0;n < 256; n += 1) {
75
+ let c = n;
76
+ for (let k = 0;k < 8; k += 1)
77
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
78
+ table[n] = c >>> 0;
79
+ }
80
+ return table;
81
+ }
82
+ function crc32(bytes) {
83
+ let c = 4294967295;
84
+ for (let i = 0;i < bytes.length; i += 1)
85
+ c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
86
+ return (c ^ 4294967295) >>> 0;
87
+ }
88
+ function chunk(type, data) {
89
+ const out = new Uint8Array(12 + data.length);
90
+ const view = new DataView(out.buffer);
91
+ view.setUint32(0, data.length);
92
+ out[4] = type.charCodeAt(0);
93
+ out[5] = type.charCodeAt(1);
94
+ out[6] = type.charCodeAt(2);
95
+ out[7] = type.charCodeAt(3);
96
+ out.set(data, 8);
97
+ view.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
98
+ return out;
99
+ }
100
+ function encodePng(width, height, rgba) {
101
+ const ihdr = new Uint8Array(13);
102
+ const ihdrView = new DataView(ihdr.buffer);
103
+ ihdrView.setUint32(0, width);
104
+ ihdrView.setUint32(4, height);
105
+ ihdr[8] = 8;
106
+ ihdr[9] = 6;
107
+ ihdr[10] = 0;
108
+ ihdr[11] = 0;
109
+ ihdr[12] = 0;
110
+ const stride = width * 4;
111
+ const raw = new Uint8Array((stride + 1) * height);
112
+ for (let y = 0;y < height; y += 1) {
113
+ const rawOffset = y * (stride + 1);
114
+ raw[rawOffset] = 0;
115
+ raw.set(rgba.subarray(y * stride, y * stride + stride), rawOffset + 1);
116
+ }
117
+ const compressed = deflateSync(raw, { level: ZLIB_LEVEL });
118
+ return Buffer.concat([
119
+ Buffer.from(PNG_SIGNATURE),
120
+ Buffer.from(chunk("IHDR", ihdr)),
121
+ Buffer.from(chunk("IDAT", compressed)),
122
+ Buffer.from(chunk("IEND", new Uint8Array(0)))
123
+ ]);
124
+ }
125
+ function kittyImageSequence(base64, options) {
126
+ const params = [`a=T`, `f=${options.format}`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
127
+ if (options.format === 32) {
128
+ params.push(`s=${options.pixelWidth ?? 1}`, `v=${options.pixelHeight ?? 1}`);
129
+ if (options.compressed)
130
+ params.push("o=z");
131
+ }
132
+ if (base64.length <= KITTY_CHUNK_SIZE)
133
+ return `\x1B_G${params.join(",")};${base64}\x1B\\`;
134
+ const chunks = [];
135
+ for (let offset = 0;offset < base64.length; offset += KITTY_CHUNK_SIZE) {
136
+ const part = base64.slice(offset, offset + KITTY_CHUNK_SIZE);
137
+ const last = offset + KITTY_CHUNK_SIZE >= base64.length;
138
+ chunks.push(`\x1B_G${offset === 0 ? `${params.join(",")},` : ""}m=${last ? 0 : 1};${part}\x1B\\`);
139
+ }
140
+ return chunks.join("");
141
+ }
142
+ function isModernImageTerminal(protocol) {
143
+ return protocol === "kitty";
144
+ }
145
+ function clampByte(value) {
146
+ return Math.max(0, Math.min(255, Math.round(value)));
147
+ }
148
+ function blendOver(data, index, color, alphaScale = 1) {
149
+ const alpha = color[3] / 255 * alphaScale;
150
+ const inv = 1 - alpha;
151
+ data[index] = clampByte(color[0] * alpha + data[index] * inv);
152
+ data[index + 1] = clampByte(color[1] * alpha + data[index + 1] * inv);
153
+ data[index + 2] = clampByte(color[2] * alpha + data[index + 2] * inv);
154
+ data[index + 3] = 255;
155
+ }
156
+ function drawDisc(data, width, height, cx, cy, radius, color, alpha = 1) {
157
+ const minX = Math.max(0, Math.floor(cx - radius));
158
+ const maxX = Math.min(width - 1, Math.ceil(cx + radius));
159
+ const minY = Math.max(0, Math.floor(cy - radius));
160
+ const maxY = Math.min(height - 1, Math.ceil(cy + radius));
161
+ for (let y = minY;y <= maxY; y += 1) {
162
+ for (let x = minX;x <= maxX; x += 1) {
163
+ const dx = x + 0.5 - cx;
164
+ const dy = y + 0.5 - cy;
165
+ const d = Math.sqrt(dx * dx + dy * dy);
166
+ if (d > radius)
167
+ continue;
168
+ const edge = Math.max(0, Math.min(1, radius - d));
169
+ blendOver(data, (y * width + x) * 4, color, alpha * Math.min(1, 0.35 + edge));
170
+ }
171
+ }
172
+ }
173
+ function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
174
+ const minX = Math.max(0, Math.floor(cx - radius));
175
+ const maxX = Math.min(width - 1, Math.ceil(cx + radius));
176
+ const minY = Math.max(0, Math.floor(cy - radius));
177
+ const maxY = Math.min(height - 1, Math.ceil(cy + radius));
178
+ for (let y = minY;y <= maxY; y += 1) {
179
+ for (let x = minX;x <= maxX; x += 1) {
180
+ const dx = x + 0.5 - cx;
181
+ const dy = y + 0.5 - cy;
182
+ const d = Math.sqrt(dx * dx + dy * dy);
183
+ if (d > radius)
184
+ continue;
185
+ const falloff = Math.pow(1 - d / Math.max(1, radius), 2.4);
186
+ blendOver(data, (y * width + x) * 4, color, alpha * falloff);
187
+ }
188
+ }
189
+ }
190
+ function drawLine(data, width, height, x0, y0, x1, y1, color, alpha = 0.5, thickness = 1.4) {
191
+ const dx = x1 - x0;
192
+ const dy = y1 - y0;
193
+ const steps = Math.max(1, Math.ceil(Math.hypot(dx, dy) * 1.2));
194
+ for (let i = 0;i <= steps; i += 1) {
195
+ const t = i / steps;
196
+ drawDisc(data, width, height, x0 + dx * t, y0 + dy * t, thickness, color, alpha);
197
+ }
198
+ }
199
+ function drawDrone(data, width, height, x, y, scale, tick, color) {
200
+ const wobble = Math.sin(tick * 0.055) * scale * 0.35;
201
+ const cy = y + wobble;
202
+ drawGlow(data, width, height, x, cy, scale * 7, color, 0.18);
203
+ drawDisc(data, width, height, x, cy, scale * 1.45, color, 0.92);
204
+ drawDisc(data, width, height, x, cy, scale * 0.55, [235, 255, 176, 255], 0.85);
205
+ const arm = scale * 4.1;
206
+ const rotor = scale * 1.6;
207
+ const arms = [[-arm, -arm * 0.72], [arm, -arm * 0.72], [-arm, arm * 0.72], [arm, arm * 0.72]];
208
+ for (const [dx, dy] of arms) {
209
+ drawLine(data, width, height, x, cy, x + dx, cy + dy, color, 0.42, Math.max(1, scale * 0.22));
210
+ drawGlow(data, width, height, x + dx, cy + dy, rotor * 2.6, color, 0.1);
211
+ drawDisc(data, width, height, x + dx, cy + dy, rotor, color, 0.34 + 0.12 * Math.sin(tick * 0.15));
212
+ }
213
+ }
214
+ function roundRectAlpha(px, py, x, y, w, h, r) {
215
+ const rx = Math.max(x + r, Math.min(px, x + w - r));
216
+ const ry = Math.max(y + r, Math.min(py, y + h - r));
217
+ const dx = px - rx;
218
+ const dy = py - ry;
219
+ const dist = Math.sqrt(dx * dx + dy * dy);
220
+ return Math.max(0, Math.min(1, r + 0.75 - dist));
221
+ }
222
+ function panelRect(layout, panel, size) {
223
+ const cellW = size.width / layout.width;
224
+ const cellH = size.height / layout.height;
225
+ const leftCells = layout.centerLeft + (panel.left ?? 0);
226
+ const topCells = layout.centerTop + panel.top;
227
+ const widthCells = panel.width ?? layout.centerWidth;
228
+ const heightCells = panel.height;
229
+ const left = Math.round(leftCells * cellW);
230
+ const top = Math.round(topCells * cellH);
231
+ const width = Math.round(widthCells * cellW);
232
+ const height = Math.round(heightCells * cellH);
233
+ return width > 8 && height > 8 ? { left, top, width, height } : null;
234
+ }
235
+ function downsampleRegion(data, canvasW, canvasH, rect, scale) {
236
+ const w = Math.max(1, Math.ceil(rect.width / scale));
237
+ const h = Math.max(1, Math.ceil(rect.height / scale));
238
+ const out = new Uint8ClampedArray(w * h * 3);
239
+ for (let y = 0;y < h; y += 1) {
240
+ for (let x = 0;x < w; x += 1) {
241
+ let r = 0, g = 0, b = 0, count = 0;
242
+ const sx0 = rect.left + x * scale;
243
+ const sy0 = rect.top + y * scale;
244
+ for (let yy = 0;yy < scale; yy += 1) {
245
+ const sy = sy0 + yy;
246
+ if (sy < 0 || sy >= canvasH)
247
+ continue;
248
+ for (let xx = 0;xx < scale; xx += 1) {
249
+ const sx = sx0 + xx;
250
+ if (sx < 0 || sx >= canvasW)
251
+ continue;
252
+ const i = (sy * canvasW + sx) * 4;
253
+ r += data[i];
254
+ g += data[i + 1];
255
+ b += data[i + 2];
256
+ count += 1;
257
+ }
258
+ }
259
+ const oi = (y * w + x) * 3;
260
+ out[oi] = count ? r / count : BG[0];
261
+ out[oi + 1] = count ? g / count : BG[1];
262
+ out[oi + 2] = count ? b / count : BG[2];
263
+ }
264
+ }
265
+ return { pixels: out, width: w, height: h };
266
+ }
267
+ function blurRgb(buffer, width, height, radius) {
268
+ const tmp = new Uint8ClampedArray(buffer.length);
269
+ const out = new Uint8ClampedArray(buffer.length);
270
+ const window = radius * 2 + 1;
271
+ for (let y = 0;y < height; y += 1) {
272
+ let r = 0, g = 0, b = 0;
273
+ for (let x = -radius;x <= radius; x += 1) {
274
+ const sx = Math.max(0, Math.min(width - 1, x));
275
+ const i = (y * width + sx) * 3;
276
+ r += buffer[i];
277
+ g += buffer[i + 1];
278
+ b += buffer[i + 2];
279
+ }
280
+ for (let x = 0;x < width; x += 1) {
281
+ const oi = (y * width + x) * 3;
282
+ tmp[oi] = r / window;
283
+ tmp[oi + 1] = g / window;
284
+ tmp[oi + 2] = b / window;
285
+ const removeX = Math.max(0, x - radius);
286
+ const addX = Math.min(width - 1, x + radius + 1);
287
+ const ri = (y * width + removeX) * 3;
288
+ const ai = (y * width + addX) * 3;
289
+ r += tmp.length ? buffer[ai] - buffer[ri] : 0;
290
+ g += tmp.length ? buffer[ai + 1] - buffer[ri + 1] : 0;
291
+ b += tmp.length ? buffer[ai + 2] - buffer[ri + 2] : 0;
292
+ }
293
+ }
294
+ for (let x = 0;x < width; x += 1) {
295
+ let r = 0, g = 0, b = 0;
296
+ for (let y = -radius;y <= radius; y += 1) {
297
+ const sy = Math.max(0, Math.min(height - 1, y));
298
+ const i = (sy * width + x) * 3;
299
+ r += tmp[i];
300
+ g += tmp[i + 1];
301
+ b += tmp[i + 2];
302
+ }
303
+ for (let y = 0;y < height; y += 1) {
304
+ const oi = (y * width + x) * 3;
305
+ out[oi] = r / window;
306
+ out[oi + 1] = g / window;
307
+ out[oi + 2] = b / window;
308
+ const removeY = Math.max(0, y - radius);
309
+ const addY = Math.min(height - 1, y + radius + 1);
310
+ const ri = (removeY * width + x) * 3;
311
+ const ai = (addY * width + x) * 3;
312
+ r += tmp[ai] - tmp[ri];
313
+ g += tmp[ai + 1] - tmp[ri + 1];
314
+ b += tmp[ai + 2] - tmp[ri + 2];
315
+ }
316
+ }
317
+ return out;
318
+ }
319
+ function panelMaskKey(width, height, rect, headerPx, blurScale) {
320
+ return `${width}x${height}:${rect.left},${rect.top},${rect.width},${rect.height}:${Math.round(headerPx * 100) / 100}:${blurScale}`;
321
+ }
322
+ function getPanelMask(width, height, rect, headerPx, blurScale) {
323
+ const key = panelMaskKey(width, height, rect, headerPx, blurScale);
324
+ const cached = panelMaskCache.get(key);
325
+ if (cached)
326
+ return cached;
327
+ const radius = Math.max(14, Math.min(34, Math.round(Math.min(rect.width, rect.height) * 0.035)));
328
+ const sampledWidth = Math.max(1, Math.ceil(rect.width / blurScale));
329
+ const sampledHeight = Math.max(1, Math.ceil(rect.height / blurScale));
330
+ const x0 = Math.max(0, rect.left);
331
+ const y0 = Math.max(0, rect.top);
332
+ const x1 = Math.min(width - 1, rect.left + rect.width - 1);
333
+ const y1 = Math.min(height - 1, rect.top + rect.height - 1);
334
+ const indices = [];
335
+ const sampleIndices = [];
336
+ const panelAlpha = [];
337
+ const edgeAlpha = [];
338
+ const headerFlags = [];
339
+ const dividerIndices = [];
340
+ for (let y = y0;y <= y1; y += 1) {
341
+ for (let x = x0;x <= x1; x += 1) {
342
+ const a = roundRectAlpha(x + 0.5, y + 0.5, rect.left, rect.top, rect.width, rect.height, radius);
343
+ if (a <= 0)
344
+ continue;
345
+ const edge = roundRectAlpha(x + 0.5, y + 0.5, rect.left + 1.2, rect.top + 1.2, rect.width - 2.4, rect.height - 2.4, Math.max(1, radius - 1.2));
346
+ const sx = Math.max(0, Math.min(sampledWidth - 1, Math.floor((x - rect.left) / blurScale)));
347
+ const sy = Math.max(0, Math.min(sampledHeight - 1, Math.floor((y - rect.top) / blurScale)));
348
+ indices.push((y * width + x) * 4);
349
+ sampleIndices.push((sy * sampledWidth + sx) * 3);
350
+ panelAlpha.push(a);
351
+ edgeAlpha.push(edge < 0.96 ? Math.min(1, (1 - edge) * 0.7) : 0);
352
+ headerFlags.push(y < rect.top + headerPx ? 1 : 0);
353
+ }
354
+ }
355
+ const dividerY = Math.round(rect.top + headerPx);
356
+ if (dividerY > y0 && dividerY < y1) {
357
+ for (let x = x0 + radius;x <= x1 - radius; x += 1) {
358
+ dividerIndices.push((dividerY * width + x) * 4);
359
+ }
360
+ }
361
+ const mask = {
362
+ key,
363
+ indices: Uint32Array.from(indices),
364
+ sampleIndices: Uint32Array.from(sampleIndices),
365
+ panelAlpha: Float32Array.from(panelAlpha),
366
+ edgeAlpha: Float32Array.from(edgeAlpha),
367
+ headerFlags: Uint8Array.from(headerFlags),
368
+ dividerIndices: Uint32Array.from(dividerIndices)
369
+ };
370
+ panelMaskCache.set(key, mask);
371
+ while (panelMaskCache.size > PANEL_MASK_CACHE_LIMIT) {
372
+ const oldest = panelMaskCache.keys().next().value;
373
+ if (!oldest)
374
+ break;
375
+ panelMaskCache.delete(oldest);
376
+ }
377
+ return mask;
378
+ }
379
+ function compositePanel(data, width, height, rect, headerPx) {
380
+ const blurScale = PANEL_BLUR_SCALE;
381
+ const sampled = downsampleRegion(data, width, height, rect, blurScale);
382
+ const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, 4);
383
+ const mask = getPanelMask(width, height, rect, headerPx, blurScale);
384
+ for (let i = 0;i < mask.indices.length; i += 1) {
385
+ const idx = mask.indices[i];
386
+ const sampleIdx = mask.sampleIndices[i];
387
+ data[idx] = blurred[sampleIdx];
388
+ data[idx + 1] = blurred[sampleIdx + 1];
389
+ data[idx + 2] = blurred[sampleIdx + 2];
390
+ data[idx + 3] = 255;
391
+ blendOver(data, idx, mask.headerFlags[i] ? PANEL_HEADER : PANEL, mask.panelAlpha[i]);
392
+ const edge = mask.edgeAlpha[i];
393
+ if (edge > 0)
394
+ blendOver(data, idx, PANEL_LINE, edge);
395
+ }
396
+ for (let i = 0;i < mask.dividerIndices.length; i += 1) {
397
+ blendOver(data, mask.dividerIndices[i], PANEL_LINE, 0.28);
398
+ }
399
+ }
400
+ function visualSceneKind(scene) {
401
+ if (scene === "tasks")
402
+ return "loop";
403
+ if (scene === "help")
404
+ return "carrier";
405
+ return "dispatch";
406
+ }
407
+ function seededUnit(seed) {
408
+ let value = seed >>> 0;
409
+ value = value * 1664525 + 1013904223 >>> 0;
410
+ return value / 4294967295;
411
+ }
412
+ function sceneCore(width, height, kind) {
413
+ if (kind === "dispatch")
414
+ return { x: width * 0.3, y: height * 0.45 };
415
+ if (kind === "carrier")
416
+ return { x: width * 0.5, y: height * 0.54 };
417
+ return { x: width * 0.09, y: height * 0.56 };
418
+ }
419
+ function dispatchLane(width, index, count) {
420
+ const a = seededUnit(28672 + index * 101);
421
+ const b = seededUnit(36864 + index * 131);
422
+ const c = seededUnit(45056 + index * 151);
423
+ const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
424
+ return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
425
+ }
426
+ function lanePoint(width, height, lane, p, kind = "dispatch") {
427
+ const core = sceneCore(width, height, kind);
428
+ const dist = lane.len * p;
429
+ const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
430
+ const ux = Math.cos(lane.ang);
431
+ const uy = Math.sin(lane.ang);
432
+ return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
433
+ }
434
+ function drawMarketingRails(data, width, height, kind, tick) {
435
+ const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
436
+ ys.forEach((ratio, index) => {
437
+ const y = height * ratio + Math.sin(tick * 0.018 + index) * Math.max(2, height * 0.0025);
438
+ const color = index === 2 ? CYAN : index === 1 ? LIME : LIME_DIM;
439
+ 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));
440
+ for (let p = (tick * 0.004 + index * 0.21) % 1;p < 1; p += 0.18 + index * 0.02) {
441
+ const x = width * (0.06 + 0.88 * p);
442
+ drawDisc(data, width, height, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.24);
443
+ drawGlow(data, width, height, x, y, Math.max(5, height * 0.006), color, 0.035);
444
+ }
445
+ });
446
+ }
447
+ function drawDispatchScene(data, width, height, tick) {
448
+ const core = sceneCore(width, height, "dispatch");
449
+ const min = Math.min(width, height);
450
+ drawGlow(data, width, height, core.x, core.y, min * 0.085, LIME, 0.12);
451
+ drawDisc(data, width, height, core.x, core.y, min * 0.0065, LIME, 0.85);
452
+ const laneCount = width < 1000 ? 7 : 12;
453
+ const lanes = Array.from({ length: laneCount }, (_, index) => dispatchLane(width, index, laneCount));
454
+ lanes.forEach((lane) => {
455
+ let prev = lanePoint(width, height, lane, 0);
456
+ for (let step = 1;step <= 32; step += 1) {
457
+ const next = lanePoint(width, height, lane, step / 32);
458
+ 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));
459
+ prev = next;
460
+ }
461
+ });
462
+ const packets = width < 1000 ? 70 : 150;
463
+ for (let i = 0;i < packets; i += 1) {
464
+ const lane = lanes[i % lanes.length];
465
+ const speed = 0.003 + seededUnit(12288 + i * 17) * 0.009;
466
+ const p = (seededUnit(16384 + i * 19) + tick * speed) % 1;
467
+ const pt = lanePoint(width, height, lane, p);
468
+ const wobble = (seededUnit(20480 + i * 23) - 0.5) * min * 0.012;
469
+ const fade = p < 0.1 ? p / 0.1 : p > 0.88 ? (1 - p) / 0.12 : 1;
470
+ const color = seededUnit(24576 + i * 29) < 0.22 ? CYAN : LIME_DIM;
471
+ const y = pt.y + wobble * Math.sin(p * Math.PI * 2);
472
+ 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));
473
+ drawDisc(data, width, height, pt.x, y, Math.max(1.3, min * 0.0022), color, 0.5 * fade);
474
+ drawGlow(data, width, height, pt.x, y, Math.max(4, min * 0.008), color, 0.045 * fade);
475
+ }
476
+ for (let i = 0;i < (width < 1000 ? 4 : 8); i += 1) {
477
+ const lane = lanes[i % lanes.length];
478
+ const p = (tick * 0.002 + i * 0.13) % 1;
479
+ const pt = lanePoint(width, height, lane, p);
480
+ 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);
481
+ }
482
+ }
483
+ function drawLoopScene(data, width, height, tick) {
484
+ const min = Math.min(width, height);
485
+ const y = height * 0.56;
486
+ const x0 = width * 0.09;
487
+ const x1 = width * 0.91;
488
+ drawLine(data, width, height, x0, y, x1, y, LIME_DIM, 0.12, Math.max(0.8, min * 0.001));
489
+ const gates = width < 1000 ? 4 : 5;
490
+ const active = Math.floor(tick / 16) % gates;
491
+ for (let i = 0;i < gates; i += 1) {
492
+ const x = x0 + (x1 - x0) * (i / Math.max(1, gates - 1));
493
+ const color = i === active ? LIME : i <= active ? LIME_DIM : INK_DIM;
494
+ 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));
495
+ drawDisc(data, width, height, x, y, i === active ? min * 0.006 : min * 0.0035, color, i === active ? 0.72 : 0.25);
496
+ drawGlow(data, width, height, x, y, i === active ? min * 0.018 : min * 0.01, color, i === active ? 0.1 : 0.035);
497
+ }
498
+ const packets = width < 1000 ? 36 : 84;
499
+ for (let i = 0;i < packets; i += 1) {
500
+ const p = (seededUnit(33024 + i * 13) + tick * (0.0022 + seededUnit(33280 + i * 17) * 0.004)) % 1;
501
+ const x = x0 + (x1 - x0) * p;
502
+ const segment = Math.min(gates - 1, Math.floor(p * gates));
503
+ const color = segment < active ? LIME_DIM : segment === active ? CYAN : INK_DIM;
504
+ const py = y + (seededUnit(33536 + i * 19) - 0.5) * min * 0.018 * Math.sin(p * Math.PI * 2);
505
+ drawLine(data, width, height, x - min * 0.014, py, x, py, color, 0.1, Math.max(0.7, min * 0.0008));
506
+ drawDisc(data, width, height, x, py, Math.max(1.2, min * 0.002), color, color === INK_DIM ? 0.22 : 0.58);
507
+ }
508
+ drawDrone(data, width, height, x0 + (x1 - x0) * (tick * 0.003 % 1), y - min * 0.04, min * 0.0065, tick, LIME_DIM);
509
+ drawDrone(data, width, height, x0 + (x1 - x0) * ((tick * 0.002 + 0.5) % 1), y + min * 0.036, min * 0.0055, tick + 40, MAGENTA);
510
+ }
511
+ function drawCarrierScene(data, width, height, tick) {
512
+ const min = Math.min(width, height);
513
+ const core = sceneCore(width, height, "carrier");
514
+ drawGlow(data, width, height, core.x, core.y, min * 0.075, LIME, 0.12);
515
+ drawDisc(data, width, height, core.x, core.y, min * 0.0065, LIME, 0.92);
516
+ const nodes = width < 1000 ? 5 : 7;
517
+ const rx = width * 0.36;
518
+ const ry = height * 0.22;
519
+ for (let i = 0;i < nodes; i += 1) {
520
+ const a = i / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006;
521
+ const x = core.x + Math.cos(a) * rx;
522
+ const y = core.y + Math.sin(a) * ry;
523
+ const color = i % 2 ? CYAN : LIME_DIM;
524
+ drawLine(data, width, height, core.x, core.y, x, y, color, 0.055, Math.max(0.7, min * 0.0008));
525
+ drawGlow(data, width, height, x, y, min * 0.018, color, 0.055);
526
+ drawDisc(data, width, height, x, y, min * 0.0048, color, 0.62);
527
+ drawDrone(data, width, height, x, y - min * 0.018, min * 0.005, tick + i * 9, color);
528
+ }
529
+ const packets = width < 1000 ? 30 : 72;
530
+ for (let i = 0;i < packets; i += 1) {
531
+ const lane = i % nodes;
532
+ const a = lane / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006;
533
+ const nx = core.x + Math.cos(a) * rx;
534
+ const ny = core.y + Math.sin(a) * ry;
535
+ const p = (seededUnit(37120 + i * 37) + tick * (0.003 + seededUnit(37376 + i * 43) * 0.009)) % 1;
536
+ const x = core.x + (nx - core.x) * p;
537
+ const y = core.y + (ny - core.y) * p;
538
+ const color = seededUnit(37632 + i * 47) < 0.35 ? CYAN : LIME_DIM;
539
+ drawDisc(data, width, height, x, y, Math.max(1.2, min * 0.002), color, 0.48);
540
+ drawGlow(data, width, height, x, y, min * 0.008, color, 0.035);
541
+ }
542
+ }
543
+ function drawBackground(data, width, height, tick, scene) {
544
+ fillRgba(data, BG, BG_WORD);
545
+ const kind = visualSceneKind(scene);
546
+ drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME, 0.035);
547
+ drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN, 0.025);
548
+ drawMarketingRails(data, width, height, kind, tick);
549
+ if (kind === "loop")
550
+ drawLoopScene(data, width, height, tick);
551
+ else if (kind === "carrier")
552
+ drawCarrierScene(data, width, height, tick);
553
+ else
554
+ drawDispatchScene(data, width, height, tick);
555
+ }
556
+ function imageVisualFrameKey(input) {
557
+ const width = Math.max(1, Math.floor(input.pixelSize.width));
558
+ const height = Math.max(1, Math.floor(input.pixelSize.height));
559
+ const layer = "layer" in input ? input.layer ?? "full" : "full";
560
+ return `${layer}:${width}x${height}:${input.layout.width}x${input.layout.height}:${input.scene}:${input.tick}:${input.panels.map((panel) => `${panel.id}:${panel.top}:${panel.height}:${panel.width ?? ""}:${panel.left ?? ""}:${panel.headerHeight ?? ""}`).join("|")}`;
561
+ }
562
+ function encodeKittyFrame(width, height, data, input) {
563
+ if (input.transport === "png") {
564
+ const png = encodePng(width, height, data);
565
+ return kittyImageSequence(png.toString("base64"), { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: input.zIndex, format: 100 });
566
+ }
567
+ const compressed = deflateSync(data, { level: ZLIB_LEVEL });
568
+ return kittyImageSequence(compressed.toString("base64"), {
569
+ columns: input.layout.width,
570
+ rows: input.layout.height,
571
+ imageId: input.imageId,
572
+ zIndex: input.zIndex,
573
+ format: 32,
574
+ pixelWidth: width,
575
+ pixelHeight: height,
576
+ compressed: true
577
+ });
578
+ }
579
+ function renderImageVisualFrame(input) {
580
+ const width = Math.max(1, Math.floor(input.pixelSize.width));
581
+ const height = Math.max(1, Math.floor(input.pixelSize.height));
582
+ const byteCount = width * height * 4;
583
+ if (byteCount > MAX_IMAGE_BYTES)
584
+ return null;
585
+ const data = new Uint8ClampedArray(byteCount);
586
+ drawBackground(data, width, height, input.tick, input.scene);
587
+ for (const panel of input.panels) {
588
+ if (panel.chrome !== "ad-terminal")
589
+ continue;
590
+ const rect = panelRect(input.layout, panel, { width, height });
591
+ if (!rect)
592
+ continue;
593
+ const cellH = height / input.layout.height;
594
+ const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
595
+ compositePanel(data, width, height, rect, headerPx);
596
+ }
597
+ return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
598
+ }
599
+ function createImageWorker() {
600
+ if (process.env.RIG_OPENTUI_IMAGE_WORKER === "0")
601
+ return null;
602
+ const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
603
+ const workerUrl = new URL(isTypeScriptRuntime ? "./image-visual-layer-worker.ts" : "./image-visual-layer-worker.js", import.meta.url);
604
+ try {
605
+ return new Worker(workerUrl);
606
+ } catch {
607
+ if (typeof process.versions.bun === "string") {
608
+ try {
609
+ return new Worker("./src/app-opentui/render/image-visual-layer-worker.ts");
610
+ } catch {}
611
+ }
612
+ return null;
613
+ }
614
+ }
615
+ var RECENT_WRITTEN_LIMIT = 24;
616
+ var DEFAULT_PANEL_FPS = 24;
617
+ var DEFAULT_PANEL_LAYER_SCALE = 0.22;
618
+ var DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
619
+ var DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
620
+ var MIN_BACKGROUND_BUDGET_BYTES = 24 * 1024;
621
+ var MIN_PANEL_BUDGET_BYTES = 56 * 1024;
622
+ function panelFps() {
623
+ const raw = Number.parseFloat(process.env.RIG_OPENTUI_PANEL_FPS ?? "");
624
+ if (Number.isFinite(raw) && raw > 0)
625
+ return Math.max(4, Math.min(60, raw));
626
+ return DEFAULT_PANEL_FPS;
627
+ }
628
+ function panelLayerScale() {
629
+ const raw = Number.parseFloat(process.env.RIG_OPENTUI_PANEL_SCALE ?? "");
630
+ if (Number.isFinite(raw) && raw > 0)
631
+ return Math.max(0.18, Math.min(1, raw));
632
+ return DEFAULT_PANEL_LAYER_SCALE;
633
+ }
634
+ function imageOutputBacklogBytes() {
635
+ const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BACKLOG_BYTES ?? "", 10);
636
+ if (Number.isFinite(raw) && raw > 0)
637
+ return Math.max(64 * 1024, Math.min(2 * 1024 * 1024, raw));
638
+ return DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES;
639
+ }
640
+ function imageOutputBytesPerSecond() {
641
+ const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BYTES_PER_SECOND ?? "", 10);
642
+ if (Number.isFinite(raw) && raw > 0)
643
+ return Math.max(128 * 1024, Math.min(2 * 1024 * 1024, raw));
644
+ return DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND;
645
+ }
646
+ function pendingLayerKey(key) {
647
+ return key.split(":", 1)[0] || "full";
648
+ }
649
+
650
+ class ImageVisualLayer {
651
+ imageId = allocateImageId();
652
+ panelImageIds = Array.from({ length: 8 }, () => allocateImageId());
653
+ panelChromeImageIds = Array.from({ length: 8 }, () => allocateImageId());
654
+ stdout;
655
+ protocol;
656
+ transport = imageTransport();
657
+ frameIntervalMs = 1000 / imageFps();
658
+ panelIntervalMs = 1000 / panelFps();
659
+ maxOutputBacklogBytes = imageOutputBacklogBytes();
660
+ outputBytesPerSecond = imageOutputBytesPerSecond();
661
+ outputBudgetBytes = imageOutputBytesPerSecond();
662
+ lastBudgetAtMs = Date.now();
663
+ motionSlot = this.createSlot();
664
+ panelSlot = this.createSlot();
665
+ chromeSlot = this.createSlot();
666
+ pendingFrames = new Map;
667
+ recentWrittenKeys = [];
668
+ requestId = 0;
669
+ lastMotionFrameIndex = -1;
670
+ lastPanelFrameIndex = -1;
671
+ lastChromeKey = "";
672
+ destroyed = false;
673
+ constructor(stdout = process.stdout) {
674
+ this.stdout = stdout;
675
+ this.protocol = getCapabilities().images;
676
+ if (this.enabled) {
677
+ this.startWorker(this.motionSlot);
678
+ this.startWorker(this.panelSlot);
679
+ this.startWorker(this.chromeSlot);
680
+ }
681
+ }
682
+ get enabled() {
683
+ return isModernImageTerminal(this.protocol);
684
+ }
685
+ clear() {
686
+ if (this.enabled) {
687
+ this.stdout.write([this.imageId, ...this.panelImageIds, ...this.panelChromeImageIds].map((id) => deleteKittyImage(id)).join("") + "\x1B[?25h");
688
+ this.pendingFrames.clear();
689
+ this.recentWrittenKeys.length = 0;
690
+ this.resetSlot(this.motionSlot);
691
+ this.resetSlot(this.panelSlot);
692
+ this.resetSlot(this.chromeSlot);
693
+ this.lastMotionFrameIndex = -1;
694
+ this.lastPanelFrameIndex = -1;
695
+ this.lastChromeKey = "";
696
+ this.outputBudgetBytes = this.outputBytesPerSecond;
697
+ this.lastBudgetAtMs = Date.now();
698
+ this.motionSlot.clearedThroughRequestId = this.requestId;
699
+ this.panelSlot.clearedThroughRequestId = this.requestId;
700
+ this.chromeSlot.clearedThroughRequestId = this.requestId;
701
+ }
702
+ }
703
+ destroy() {
704
+ if (this.destroyed)
705
+ return;
706
+ this.clear();
707
+ this.destroyed = true;
708
+ const workers = [this.motionSlot.worker, this.panelSlot.worker, this.chromeSlot.worker];
709
+ this.motionSlot.worker = null;
710
+ this.panelSlot.worker = null;
711
+ this.chromeSlot.worker = null;
712
+ for (const worker of workers)
713
+ if (worker)
714
+ worker.terminate().catch(() => {
715
+ return;
716
+ });
717
+ }
718
+ render(input) {
719
+ if (!this.enabled || this.destroyed)
720
+ return;
721
+ const width = Math.max(1, Math.floor(input.pixelSize.width));
722
+ const height = Math.max(1, Math.floor(input.pixelSize.height));
723
+ if (width * height * 4 > MAX_IMAGE_BYTES)
724
+ return;
725
+ if (this.isOutputBackedUp())
726
+ return;
727
+ this.refillOutputBudget();
728
+ const now = Date.now();
729
+ const visualTick = Math.floor(now / (1000 / 60));
730
+ const useDynamicSplit = Boolean(this.motionSlot.worker && this.panelSlot.worker && this.chromeSlot.worker && this.transport === "png" && process.env.RIG_OPENTUI_DYNAMIC_SPLIT !== "0");
731
+ if (!useDynamicSplit) {
732
+ const frameIndex = Math.floor(now / this.frameIntervalMs);
733
+ if (!input.force && frameIndex === this.lastMotionFrameIndex)
734
+ return;
735
+ this.scheduleRender(this.motionSlot, { ...input, tick: visualTick, layer: "full" }, this.imageId, -10, input.force);
736
+ this.lastMotionFrameIndex = frameIndex;
737
+ return;
738
+ }
739
+ const motionFrameIndex = Math.floor(now / this.frameIntervalMs);
740
+ if ((input.force || motionFrameIndex !== this.lastMotionFrameIndex) && this.outputBudgetBytes >= MIN_BACKGROUND_BUDGET_BYTES) {
741
+ this.scheduleRender(this.motionSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, droneLayerScale(), input.layout), panels: [], tick: visualTick, layer: "background" }, this.imageId, -30, input.force);
742
+ this.lastMotionFrameIndex = motionFrameIndex;
743
+ }
744
+ const panelFrameIndex = Math.floor(now / this.panelIntervalMs);
745
+ if ((input.force || panelFrameIndex !== this.lastPanelFrameIndex) && this.outputBudgetBytes >= MIN_PANEL_BUDGET_BYTES) {
746
+ this.scheduleRender(this.panelSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, panelLayerScale(), input.layout), tick: visualTick, layer: "panel-patches" }, this.imageId, -12, input.force, this.panelImageIds);
747
+ this.lastPanelFrameIndex = panelFrameIndex;
748
+ }
749
+ const chromeInput = { ...input, tick: 0, layer: "panel-chrome" };
750
+ const chromeKey = imageVisualFrameKey(chromeInput);
751
+ if (input.force || chromeKey !== this.lastChromeKey) {
752
+ this.scheduleRender(this.chromeSlot, chromeInput, this.imageId, -8, input.force, this.panelChromeImageIds);
753
+ this.lastChromeKey = chromeKey;
754
+ }
755
+ }
756
+ flush() {
757
+ if (this.pendingFrames.size === 0)
758
+ return false;
759
+ this.refillOutputBudget();
760
+ if (this.isOutputBackedUp()) {
761
+ const chrome = this.pendingFrames.get("panel-chrome");
762
+ this.pendingFrames.clear();
763
+ if (chrome)
764
+ this.pendingFrames.set("panel-chrome", chrome);
765
+ return false;
766
+ }
767
+ const framePriority = (frame) => {
768
+ const layer = pendingLayerKey(frame.key);
769
+ if (layer === "panel-chrome")
770
+ return 0;
771
+ if (layer === "background")
772
+ return 1;
773
+ if (layer === "panel-patches")
774
+ return 2;
775
+ return 2;
776
+ };
777
+ const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
778
+ const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
779
+ const suffix = "\x1B[u\x1B8";
780
+ const selected = [];
781
+ const retained = new Map;
782
+ let sequence = "";
783
+ let byteLength = Buffer.byteLength(prefix + suffix);
784
+ for (const frame of frames) {
785
+ const candidate = sequence + frame.sequence;
786
+ const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
787
+ if (candidateBytes <= this.outputBudgetBytes) {
788
+ selected.push(frame);
789
+ sequence = candidate;
790
+ byteLength = candidateBytes;
791
+ } else if (pendingLayerKey(frame.key) === "panel-chrome") {
792
+ retained.set(pendingLayerKey(frame.key), frame);
793
+ }
794
+ }
795
+ this.pendingFrames.clear();
796
+ for (const [layer, frame] of retained)
797
+ this.pendingFrames.set(layer, frame);
798
+ if (!sequence || selected.length === 0)
799
+ return false;
800
+ const payload = `${prefix}${sequence}${suffix}`;
801
+ for (const frame of selected)
802
+ this.markWritten(frame.key);
803
+ this.outputBudgetBytes -= byteLength;
804
+ this.stdout.cork?.();
805
+ const accepted = this.stdout.write(payload);
806
+ this.stdout.uncork?.();
807
+ return accepted;
808
+ }
809
+ createSlot() {
810
+ return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0 };
811
+ }
812
+ resetSlot(slot) {
813
+ slot.busy = false;
814
+ slot.queuedRequest = null;
815
+ slot.inFlightKey = "";
816
+ slot.queuedKey = "";
817
+ }
818
+ markWritten(key) {
819
+ this.recentWrittenKeys.push(key);
820
+ while (this.recentWrittenKeys.length > RECENT_WRITTEN_LIMIT)
821
+ this.recentWrittenKeys.shift();
822
+ }
823
+ refillOutputBudget() {
824
+ const now = Date.now();
825
+ const elapsedMs = Math.max(0, now - this.lastBudgetAtMs);
826
+ this.lastBudgetAtMs = now;
827
+ this.outputBudgetBytes = Math.min(this.outputBytesPerSecond, this.outputBudgetBytes + elapsedMs / 1000 * this.outputBytesPerSecond);
828
+ }
829
+ isOutputBackedUp() {
830
+ return Boolean(this.stdout.writableNeedDrain) || this.stdout.writableLength > this.maxOutputBacklogBytes;
831
+ }
832
+ stageFrame(frame) {
833
+ this.pendingFrames.set(pendingLayerKey(frame.key), frame);
834
+ }
835
+ isKnownKey(slot, key) {
836
+ return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
837
+ }
838
+ scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
839
+ const key = imageVisualFrameKey(input);
840
+ if (!force && this.isKnownKey(slot, key))
841
+ return;
842
+ const request = {
843
+ ...input,
844
+ imageId,
845
+ ...imageIds ? { imageIds } : {},
846
+ zIndex,
847
+ transport: this.transport,
848
+ requestId: ++this.requestId,
849
+ key
850
+ };
851
+ if (slot.worker) {
852
+ this.enqueueWorkerRender(slot, request);
853
+ return;
854
+ }
855
+ this.renderSync(slot, request);
856
+ }
857
+ startWorker(slot) {
858
+ const worker = createImageWorker();
859
+ if (!worker)
860
+ return;
861
+ slot.worker = worker;
862
+ worker.on("message", (message) => this.handleWorkerMessage(slot, message));
863
+ worker.once("error", () => this.disableWorker(slot));
864
+ worker.once("exit", () => this.disableWorker(slot));
865
+ }
866
+ disableWorker(slot) {
867
+ if (this.destroyed)
868
+ return;
869
+ slot.worker = null;
870
+ slot.busy = false;
871
+ slot.inFlightKey = "";
872
+ const queued = slot.queuedRequest;
873
+ slot.queuedRequest = null;
874
+ slot.queuedKey = "";
875
+ if (queued?.layer === "full")
876
+ this.renderSync(slot, queued);
877
+ }
878
+ enqueueWorkerRender(slot, request) {
879
+ if (!slot.worker) {
880
+ this.renderSync(slot, request);
881
+ return;
882
+ }
883
+ if (slot.busy) {
884
+ slot.queuedRequest = request;
885
+ slot.queuedKey = request.key;
886
+ return;
887
+ }
888
+ this.dispatchWorkerRender(slot, request);
889
+ }
890
+ dispatchWorkerRender(slot, request) {
891
+ if (!slot.worker) {
892
+ this.renderSync(slot, request);
893
+ return;
894
+ }
895
+ slot.busy = true;
896
+ slot.inFlightKey = request.key;
897
+ slot.worker.postMessage(request);
898
+ }
899
+ handleWorkerMessage(slot, message) {
900
+ if (this.destroyed || !message || typeof message !== "object")
901
+ return;
902
+ const response = message;
903
+ slot.busy = false;
904
+ slot.inFlightKey = "";
905
+ if (response.requestId <= slot.clearedThroughRequestId)
906
+ return;
907
+ if (response.error) {
908
+ this.disableWorker(slot);
909
+ return;
910
+ }
911
+ if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "panel-chrome")) {
912
+ this.stageFrame({ key: response.key, sequence: response.sequence });
913
+ }
914
+ const next = slot.queuedRequest;
915
+ slot.queuedRequest = null;
916
+ slot.queuedKey = "";
917
+ if (next && !this.isKnownKey(slot, next.key))
918
+ this.dispatchWorkerRender(slot, next);
919
+ }
920
+ renderSync(slot, request) {
921
+ const result = renderImageVisualFrame(request);
922
+ if (!result || this.isKnownKey(slot, result.key) || this.isOutputBackedUp())
923
+ return;
924
+ this.stageFrame(result);
925
+ }
926
+ }
927
+ function pixelSizeForRenderer(renderer) {
928
+ const resolution = renderer.resolution;
929
+ const base = resolution?.width && resolution?.height ? resolution : { width: renderer.width * 12, height: renderer.height * 24 };
930
+ const scale = imageResolutionScale();
931
+ return {
932
+ width: Math.max(renderer.width, Math.floor(base.width * scale)),
933
+ height: Math.max(renderer.height, Math.floor(base.height * scale))
934
+ };
935
+ }
936
+ function forceModernImageVisuals() {
937
+ return isModernImageTerminal(getCapabilities().images);
938
+ }
939
+ export {
940
+ renderImageVisualFrame,
941
+ pixelSizeForRenderer,
942
+ imageVisualFrameKey,
943
+ forceModernImageVisuals,
944
+ ImageVisualLayer
945
+ };