@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.
- package/dist/bin/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +2798 -1440
- package/dist/src/app/board.js +62 -13
- package/dist/src/app-opentui/adapters/command.d.ts +2 -0
- package/dist/src/app-opentui/adapters/command.js +329 -0
- package/dist/src/app-opentui/adapters/common.js +2 -2
- package/dist/src/app-opentui/adapters/doctor.js +63 -14
- package/dist/src/app-opentui/adapters/fleet.js +84 -20
- package/dist/src/app-opentui/adapters/inbox.js +83 -19
- package/dist/src/app-opentui/adapters/init.js +87 -23
- package/dist/src/app-opentui/adapters/pi-attach.js +96 -34
- package/dist/src/app-opentui/adapters/run-detail.js +83 -19
- package/dist/src/app-opentui/adapters/server.js +100 -23
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
- package/dist/src/app-opentui/adapters/tasks.js +742 -94
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +13769 -12368
- package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
- package/dist/src/app-opentui/command-pty-host.js +248 -0
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/focus-manager.d.ts +14 -0
- package/dist/src/app-opentui/focus-manager.js +24 -0
- package/dist/src/app-opentui/index.js +3802 -2882
- package/dist/src/app-opentui/intent.js +154 -48
- package/dist/src/app-opentui/keymap.d.ts +20 -0
- package/dist/src/app-opentui/keymap.js +707 -0
- package/dist/src/app-opentui/launch-routing.d.ts +16 -0
- package/dist/src/app-opentui/launch-routing.js +55 -0
- package/dist/src/app-opentui/layout.js +2 -2
- package/dist/src/app-opentui/pi-host-child.js +66 -16
- package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
- package/dist/src/app-opentui/pi-pty-host.js +9 -11
- package/dist/src/app-opentui/registry.js +3231 -2403
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
- package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
- package/dist/src/app-opentui/render/graphics.d.ts +1 -1
- package/dist/src/app-opentui/render/graphics.js +131 -16
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +408 -957
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +18 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +386 -356
- package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
- package/dist/src/app-opentui/render/panel-layout.js +48 -0
- package/dist/src/app-opentui/render/panels.d.ts +2 -0
- package/dist/src/app-opentui/render/panels.js +62 -29
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +163 -0
- package/dist/src/app-opentui/render/scene.d.ts +3 -0
- package/dist/src/app-opentui/render/scene.js +12 -0
- package/dist/src/app-opentui/render/text.js +5 -1
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +51 -16
- package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
- package/dist/src/app-opentui/runtime-resources.js +62 -0
- package/dist/src/app-opentui/runtime.js +2322 -1513
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +103 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +53 -14
- package/dist/src/app-opentui/scenes/error.js +44 -14
- package/dist/src/app-opentui/scenes/fleet.js +40 -21
- package/dist/src/app-opentui/scenes/handoff.js +142 -27
- package/dist/src/app-opentui/scenes/help.js +63 -48
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +64 -17
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +62 -21
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +80 -24
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +39 -25
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +71 -20
- package/dist/src/app-opentui/scenes/tasks.js +44 -22
- package/dist/src/app-opentui/state.js +70 -30
- package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
- package/dist/src/app-opentui/terminal-capabilities.js +15 -0
- package/dist/src/app-opentui/types.d.ts +10 -2
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_operator-view.js +63 -13
- package/dist/src/commands/_pi-frontend.js +63 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/connect.js +7 -1
- package/dist/src/commands/doctor.js +62 -13
- package/dist/src/commands/github.js +144 -23
- package/dist/src/commands/inbox.js +62 -13
- package/dist/src/commands/init.js +82 -18
- package/dist/src/commands/inspect.js +62 -13
- package/dist/src/commands/run.js +66 -13
- package/dist/src/commands/server.js +69 -14
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +62 -13
- package/dist/src/commands/task-run-driver.js +62 -13
- package/dist/src/commands/task.js +65 -14
- package/dist/src/commands.js +227 -92
- package/dist/src/index.js +234 -99
- package/package.json +8 -9
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
|
@@ -1,48 +1,141 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
535
|
-
|
|
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.
|
|
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 (
|
|
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
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
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
|
-
|
|
653
|
-
|
|
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.
|
|
659
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
|
776
|
+
const retainedSplit = this.pendingFrames.get("split");
|
|
747
777
|
this.pendingFrames.clear();
|
|
748
|
-
if (
|
|
749
|
-
this.pendingFrames.set("
|
|
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 === "
|
|
755
|
-
return 0;
|
|
756
|
-
if (layer === "background")
|
|
784
|
+
if (layer === "split")
|
|
757
785
|
return 1;
|
|
758
|
-
|
|
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
|
-
|
|
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 (
|
|
777
|
-
retained.set(
|
|
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
|
-
|
|
838
|
-
|
|
881
|
+
slot.latestRequestId = request.requestId;
|
|
882
|
+
if (!slot.worker) {
|
|
883
|
+
this.renderInline(slot, request);
|
|
839
884
|
return;
|
|
840
885
|
}
|
|
841
|
-
this.
|
|
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", () =>
|
|
850
|
-
|
|
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
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
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
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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
|
-
|
|
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
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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 (
|
|
1493
|
-
|
|
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) {
|