@h-rig/cli 0.0.6-alpha.81 → 0.0.6-alpha.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +2807 -1443
- 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 +102 -36
- 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 +13776 -12369
- 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 +3806 -2878
- 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 +15 -13
- package/dist/src/app-opentui/registry.js +3228 -2396
- 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 +413 -958
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +19 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +391 -357
- 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 +2329 -1512
- 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 -1480
|
@@ -2,7 +2,113 @@
|
|
|
2
2
|
// packages/cli/src/app-opentui/render/image-visual-layer.ts
|
|
3
3
|
import { Worker } from "worker_threads";
|
|
4
4
|
import { deflateSync } from "zlib";
|
|
5
|
-
import { allocateImageId, deleteKittyImage
|
|
5
|
+
import { allocateImageId, deleteKittyImage } from "@earendil-works/pi-tui";
|
|
6
|
+
|
|
7
|
+
// packages/cli/src/app-opentui/terminal-capabilities.ts
|
|
8
|
+
import { getCapabilities } from "@earendil-works/pi-tui";
|
|
9
|
+
function getRigTerminalCapabilities() {
|
|
10
|
+
const imageProtocol = getCapabilities().images;
|
|
11
|
+
const supportsKittyImages = imageProtocol === "kitty";
|
|
12
|
+
return {
|
|
13
|
+
imageProtocol,
|
|
14
|
+
supportsKittyImages,
|
|
15
|
+
visualMode: supportsKittyImages ? "kitty-images" : "framebuffer-fallback"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// packages/cli/src/app-opentui/render/ascii-fleet.ts
|
|
20
|
+
var LEAD_DRONE = [
|
|
21
|
+
" .-=-. .-=-. ",
|
|
22
|
+
" ( !!! ) ( !!! ) ",
|
|
23
|
+
" '-=-'._ _.'-=-' ",
|
|
24
|
+
" '._ _.' ",
|
|
25
|
+
" '=$$$$$$$=.' ",
|
|
26
|
+
" =$$$$$$$$$$$= ",
|
|
27
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
28
|
+
" $$$@@ @@$$$ ",
|
|
29
|
+
" $$@ ? @$$$ ",
|
|
30
|
+
" $$$@ '-' @$$$ ",
|
|
31
|
+
" $$$@@ @@$$$ ",
|
|
32
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
33
|
+
" =$$$$$$$$$$$= ",
|
|
34
|
+
" '=$$$$$$$=.' ",
|
|
35
|
+
" _.' '._ ",
|
|
36
|
+
" .-=-.' '.-=-. ",
|
|
37
|
+
" ( !!! ) ( !!! ) ",
|
|
38
|
+
" '-=-' '-=-' "
|
|
39
|
+
];
|
|
40
|
+
var MINI_DRONE = [
|
|
41
|
+
"(!!!) (!!!)",
|
|
42
|
+
" \\%==%/ ",
|
|
43
|
+
" %%?%% ",
|
|
44
|
+
" /%==%\\ ",
|
|
45
|
+
"(!!!) (!!!)"
|
|
46
|
+
];
|
|
47
|
+
var ROTORS = ["---", "\\\\\\", "|||", "///"];
|
|
48
|
+
var FLEET_GRID_WIDTH = 52;
|
|
49
|
+
var FLEET_GRID_HEIGHT = 27;
|
|
50
|
+
var FLEET = [
|
|
51
|
+
{ art: MINI_DRONE, x: 0, y: 2, amp: 1, speed: 0.05, phase: 0, dx: 1, driftSpeed: 0.02 },
|
|
52
|
+
{ art: MINI_DRONE, x: 39, y: 2, amp: 1, speed: 0.044, phase: 2.1, dx: 1, driftSpeed: 0.017 },
|
|
53
|
+
{ art: MINI_DRONE, x: 1, y: 20, amp: 1, speed: 0.048, phase: 4.2, dx: 1, driftSpeed: 0.023 },
|
|
54
|
+
{ art: MINI_DRONE, x: 38, y: 20, amp: 1, speed: 0.041, phase: 1.3, dx: 1, driftSpeed: 0.015 },
|
|
55
|
+
{ art: LEAD_DRONE, x: 10, y: 5, amp: 2, speed: 0.04, phase: 0, dx: 0, driftSpeed: 0.011 }
|
|
56
|
+
];
|
|
57
|
+
function fleetRows(tick, options = {}) {
|
|
58
|
+
const animate = options.animate ?? true;
|
|
59
|
+
const t = animate ? tick : 0;
|
|
60
|
+
const blade = ROTORS[Math.floor(t / 4) % ROTORS.length];
|
|
61
|
+
const pulse = Math.sin(t * 0.07);
|
|
62
|
+
const eye = pulse > 0.45 ? "@" : pulse > -0.1 ? "o" : ".";
|
|
63
|
+
const grid = Array.from({ length: FLEET_GRID_HEIGHT }, () => Array.from({ length: FLEET_GRID_WIDTH }, () => " "));
|
|
64
|
+
for (const drone of FLEET) {
|
|
65
|
+
const yOffset = drone.y + Math.round(drone.amp * Math.sin(t * drone.speed + drone.phase));
|
|
66
|
+
const xOffset = drone.x + Math.round(drone.dx * Math.sin(t * drone.driftSpeed + drone.phase));
|
|
67
|
+
for (let row = 0;row < drone.art.length; row += 1) {
|
|
68
|
+
const source = drone.art[row];
|
|
69
|
+
const targetY = yOffset + row;
|
|
70
|
+
if (targetY < 0 || targetY >= FLEET_GRID_HEIGHT)
|
|
71
|
+
continue;
|
|
72
|
+
for (let col = 0;col < source.length; col += 1) {
|
|
73
|
+
let char = source[col];
|
|
74
|
+
if (char === " ")
|
|
75
|
+
continue;
|
|
76
|
+
const targetX = xOffset + col;
|
|
77
|
+
if (targetX < 0 || targetX >= FLEET_GRID_WIDTH)
|
|
78
|
+
continue;
|
|
79
|
+
if (source.slice(col, col + 3) === "!!!") {
|
|
80
|
+
for (let rotorIndex = 0;rotorIndex < 3; rotorIndex += 1) {
|
|
81
|
+
if (targetX + rotorIndex < FLEET_GRID_WIDTH)
|
|
82
|
+
grid[targetY][targetX + rotorIndex] = blade[rotorIndex];
|
|
83
|
+
}
|
|
84
|
+
col += 2;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (char === "?")
|
|
88
|
+
char = eye;
|
|
89
|
+
grid[targetY][targetX] = char;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return grid.map((row) => row.join("").replace(/\s+$/, ""));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// packages/cli/src/app-opentui/render/panel-layout.ts
|
|
97
|
+
function panelPixelPlacement(layout, panel, size, minPixels = 8) {
|
|
98
|
+
const cellW = size.width / layout.width;
|
|
99
|
+
const cellH = size.height / layout.height;
|
|
100
|
+
const leftCells = layout.centerLeft + (panel.left ?? 0);
|
|
101
|
+
const topCells = layout.centerTop + panel.top;
|
|
102
|
+
const widthCells = panel.width ?? layout.centerWidth;
|
|
103
|
+
const heightCells = panel.height;
|
|
104
|
+
const left = Math.round(leftCells * cellW);
|
|
105
|
+
const top = Math.round(topCells * cellH);
|
|
106
|
+
const width = Math.round(widthCells * cellW);
|
|
107
|
+
const height = Math.round(heightCells * cellH);
|
|
108
|
+
return width > minPixels && height > minPixels ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// packages/cli/src/app-opentui/render/image-visual-layer.ts
|
|
6
112
|
var BG = [7, 8, 9, 255];
|
|
7
113
|
var PANEL = [16, 17, 21, 184];
|
|
8
114
|
var PANEL_HEADER = [16, 17, 21, 190];
|
|
@@ -16,12 +122,10 @@ var PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
|
16
122
|
var CRC_TABLE = makeCrcTable();
|
|
17
123
|
var MAX_IMAGE_BYTES = 48 * 1024 * 1024;
|
|
18
124
|
var KITTY_CHUNK_SIZE = 4096;
|
|
19
|
-
var PANEL_BLUR_SCALE =
|
|
125
|
+
var PANEL_BLUR_SCALE = 1;
|
|
20
126
|
var PANEL_MASK_CACHE_LIMIT = 16;
|
|
21
127
|
var ZLIB_LEVEL = 1;
|
|
22
128
|
var DEFAULT_IMAGE_RESOLUTION_SCALE = 1;
|
|
23
|
-
var DEFAULT_DRONE_LAYER_SCALE = 0.18;
|
|
24
|
-
var DEFAULT_IMAGE_FPS = 24;
|
|
25
129
|
var panelMaskCache = new Map;
|
|
26
130
|
var BG_WORD = rgbaWord(BG);
|
|
27
131
|
function imageTransport() {
|
|
@@ -36,24 +140,6 @@ function imageResolutionScale() {
|
|
|
36
140
|
return Math.max(0.25, Math.min(1, raw));
|
|
37
141
|
return DEFAULT_IMAGE_RESOLUTION_SCALE;
|
|
38
142
|
}
|
|
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
143
|
function rgbaWord(color) {
|
|
58
144
|
return (color[3] << 24 | color[2] << 16 | color[1] << 8 | color[0]) >>> 0;
|
|
59
145
|
}
|
|
@@ -69,6 +155,109 @@ function fillRgba(data, color, word = rgbaWord(color)) {
|
|
|
69
155
|
data[i + 3] = color[3];
|
|
70
156
|
}
|
|
71
157
|
}
|
|
158
|
+
var ASCII_GLYPHS = {
|
|
159
|
+
".": ["00000", "00000", "00000", "00000", "00000", "00100", "00100"],
|
|
160
|
+
"'": ["00100", "00100", "00000", "00000", "00000", "00000", "00000"],
|
|
161
|
+
"-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
|
|
162
|
+
_: ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
|
|
163
|
+
"=": ["00000", "11111", "00000", "00000", "11111", "00000", "00000"],
|
|
164
|
+
"|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
|
|
165
|
+
"/": ["00001", "00010", "00010", "00100", "01000", "01000", "10000"],
|
|
166
|
+
"\\": ["10000", "01000", "01000", "00100", "00010", "00010", "00001"],
|
|
167
|
+
"(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
|
|
168
|
+
")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
|
|
169
|
+
"!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
|
|
170
|
+
"%": ["11001", "11010", "00100", "01000", "10110", "00110", "00000"],
|
|
171
|
+
$: ["00100", "11110", "10100", "11110", "00101", "11110", "00100"],
|
|
172
|
+
"@": ["01110", "10001", "10111", "10101", "10111", "10000", "01111"],
|
|
173
|
+
o: ["00000", "01110", "10001", "10001", "10001", "01110", "00000"],
|
|
174
|
+
"*": ["00100", "10101", "01110", "11111", "01110", "10101", "00100"]
|
|
175
|
+
};
|
|
176
|
+
function sceneStaticTick(scene) {
|
|
177
|
+
let hash = 0;
|
|
178
|
+
for (let index = 0;index < scene.length; index += 1)
|
|
179
|
+
hash = hash * 33 + scene.charCodeAt(index) >>> 0;
|
|
180
|
+
return 11 + hash % 97;
|
|
181
|
+
}
|
|
182
|
+
function charColor(char, row, totalRows) {
|
|
183
|
+
if (char === "@" || char === "$" || char === "o")
|
|
184
|
+
return LIME;
|
|
185
|
+
if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
|
|
186
|
+
return CYAN;
|
|
187
|
+
if (char === "." || char === "'" || char === "_" || row < 3 || row > totalRows - 4)
|
|
188
|
+
return INK_DIM;
|
|
189
|
+
if (char === "-" || char === "=" || char === "(" || char === ")")
|
|
190
|
+
return LIME_DIM;
|
|
191
|
+
return MAGENTA;
|
|
192
|
+
}
|
|
193
|
+
function fillRectAlpha(data, width, height, left, top, rectWidth, rectHeight, color, alpha) {
|
|
194
|
+
const x0 = Math.max(0, Math.floor(left));
|
|
195
|
+
const y0 = Math.max(0, Math.floor(top));
|
|
196
|
+
const x1 = Math.min(width, Math.ceil(left + rectWidth));
|
|
197
|
+
const y1 = Math.min(height, Math.ceil(top + rectHeight));
|
|
198
|
+
for (let y = y0;y < y1; y += 1) {
|
|
199
|
+
for (let x = x0;x < x1; x += 1)
|
|
200
|
+
blendOver(data, (y * width + x) * 4, color, alpha);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function drawAsciiGlyph(data, width, height, x, y, cellWidth, cellHeight, char, color, alpha) {
|
|
204
|
+
const pattern = ASCII_GLYPHS[char] ?? ASCII_GLYPHS["*"];
|
|
205
|
+
const glyphWidth = Math.max(1, cellWidth * 0.56);
|
|
206
|
+
const glyphHeight = Math.max(1, cellHeight * 0.7);
|
|
207
|
+
const originX = x + (cellWidth - glyphWidth) * 0.5;
|
|
208
|
+
const originY = y + (cellHeight - glyphHeight) * 0.5;
|
|
209
|
+
const pixelWidth = Math.max(1, glyphWidth / pattern[0].length);
|
|
210
|
+
const pixelHeight = Math.max(1, glyphHeight / pattern.length);
|
|
211
|
+
for (let row = 0;row < pattern.length; row += 1) {
|
|
212
|
+
const bits = pattern[row];
|
|
213
|
+
for (let col = 0;col < bits.length; col += 1) {
|
|
214
|
+
if (bits[col] !== "1")
|
|
215
|
+
continue;
|
|
216
|
+
fillRectAlpha(data, width, height, originX + col * pixelWidth, originY + row * pixelHeight, pixelWidth * 0.72, pixelHeight * 0.72, color, alpha);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function drawStaticAsciiFleetBackground(data, width, height, scene, layout) {
|
|
221
|
+
const rows = fleetRows(sceneStaticTick(scene), { animate: true });
|
|
222
|
+
const cellWidth = width / Math.max(1, layout.width);
|
|
223
|
+
const cellHeight = height / Math.max(1, layout.height);
|
|
224
|
+
const leftCells = Math.floor((layout.width - FLEET_GRID_WIDTH) / 2);
|
|
225
|
+
const topCells = Math.floor((layout.height - FLEET_GRID_HEIGHT) / 2);
|
|
226
|
+
const fleetLeft = leftCells * cellWidth;
|
|
227
|
+
const fleetTop = topCells * cellHeight;
|
|
228
|
+
drawGlow(data, width, height, width * 0.5, height * 0.5, Math.min(width, height) * 0.4, LIME, 0.03);
|
|
229
|
+
drawGlow(data, width, height, width * 0.18, height * 0.22, Math.min(width, height) * 0.28, CYAN, 0.022);
|
|
230
|
+
drawGlow(data, width, height, width * 0.82, height * 0.78, Math.min(width, height) * 0.22, MAGENTA, 0.014);
|
|
231
|
+
for (let row = 0;row < rows.length; row += 1) {
|
|
232
|
+
const line = rows[row];
|
|
233
|
+
for (let col = 0;col < line.length; col += 1) {
|
|
234
|
+
const char = line[col];
|
|
235
|
+
if (char === " ")
|
|
236
|
+
continue;
|
|
237
|
+
const x = fleetLeft + col * cellWidth;
|
|
238
|
+
const y = fleetTop + row * cellHeight;
|
|
239
|
+
const color = charColor(char, row, rows.length);
|
|
240
|
+
drawAsciiGlyph(data, width, height, x + cellWidth * 0.07, y + cellHeight * 0.05, cellWidth, cellHeight, char, color, 0.52);
|
|
241
|
+
if (char === "@" || char === "$" || char === "%") {
|
|
242
|
+
drawGlow(data, width, height, x + cellWidth * 0.5, y + cellHeight * 0.5, Math.max(cellWidth, cellHeight) * 0.95, color, 0.02);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function applyStaticAtmosphere(data, width, height) {
|
|
248
|
+
const cx = width * 0.5;
|
|
249
|
+
const cy = height * 0.42;
|
|
250
|
+
const radius = Math.max(width, height) * 0.78;
|
|
251
|
+
for (let y = 0;y < height; y += 1) {
|
|
252
|
+
for (let x = 0;x < width; x += 1) {
|
|
253
|
+
const distance = Math.hypot(x - cx, y - cy) / radius;
|
|
254
|
+
const alpha = Math.max(0.1, Math.min(0.82, 0.12 + Math.pow(distance, 1.65) * 0.62));
|
|
255
|
+
blendOver(data, (y * width + x) * 4, BG, alpha);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.2, LIME, 0.03);
|
|
259
|
+
drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.16, CYAN, 0.022);
|
|
260
|
+
}
|
|
72
261
|
function makeCrcTable() {
|
|
73
262
|
const table = new Uint32Array(256);
|
|
74
263
|
for (let n = 0;n < 256; n += 1) {
|
|
@@ -139,9 +328,6 @@ function kittyImageSequence(base64, options) {
|
|
|
139
328
|
}
|
|
140
329
|
return chunks.join("");
|
|
141
330
|
}
|
|
142
|
-
function isModernImageTerminal(protocol) {
|
|
143
|
-
return protocol === "kitty";
|
|
144
|
-
}
|
|
145
331
|
function clampByte(value) {
|
|
146
332
|
return Math.max(0, Math.min(255, Math.round(value)));
|
|
147
333
|
}
|
|
@@ -153,23 +339,6 @@ function blendOver(data, index, color, alphaScale = 1) {
|
|
|
153
339
|
data[index + 2] = clampByte(color[2] * alpha + data[index + 2] * inv);
|
|
154
340
|
data[index + 3] = 255;
|
|
155
341
|
}
|
|
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
342
|
function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
|
|
174
343
|
const minX = Math.max(0, Math.floor(cx - radius));
|
|
175
344
|
const maxX = Math.min(width - 1, Math.ceil(cx + radius));
|
|
@@ -187,30 +356,6 @@ function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
|
|
|
187
356
|
}
|
|
188
357
|
}
|
|
189
358
|
}
|
|
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
359
|
function roundRectAlpha(px, py, x, y, w, h, r) {
|
|
215
360
|
const rx = Math.max(x + r, Math.min(px, x + w - r));
|
|
216
361
|
const ry = Math.max(y + r, Math.min(py, y + h - r));
|
|
@@ -220,17 +365,7 @@ function roundRectAlpha(px, py, x, y, w, h, r) {
|
|
|
220
365
|
return Math.max(0, Math.min(1, r + 0.75 - dist));
|
|
221
366
|
}
|
|
222
367
|
function panelRect(layout, panel, size) {
|
|
223
|
-
|
|
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;
|
|
368
|
+
return panelPixelPlacement(layout, panel, size);
|
|
234
369
|
}
|
|
235
370
|
function downsampleRegion(data, canvasW, canvasH, rect, scale) {
|
|
236
371
|
const w = Math.max(1, Math.ceil(rect.width / scale));
|
|
@@ -379,7 +514,8 @@ function getPanelMask(width, height, rect, headerPx, blurScale) {
|
|
|
379
514
|
function compositePanel(data, width, height, rect, headerPx) {
|
|
380
515
|
const blurScale = PANEL_BLUR_SCALE;
|
|
381
516
|
const sampled = downsampleRegion(data, width, height, rect, blurScale);
|
|
382
|
-
const
|
|
517
|
+
const blurRadius = Math.max(8, Math.min(28, Math.round(Math.min(rect.width, rect.height) * 0.018)));
|
|
518
|
+
const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, blurRadius);
|
|
383
519
|
const mask = getPanelMask(width, height, rect, headerPx, blurScale);
|
|
384
520
|
for (let i = 0;i < mask.indices.length; i += 1) {
|
|
385
521
|
const idx = mask.indices[i];
|
|
@@ -397,161 +533,10 @@ function compositePanel(data, width, height, rect, headerPx) {
|
|
|
397
533
|
blendOver(data, mask.dividerIndices[i], PANEL_LINE, 0.28);
|
|
398
534
|
}
|
|
399
535
|
}
|
|
400
|
-
function
|
|
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) {
|
|
536
|
+
function drawBackground(data, width, height, _tick, scene, layout) {
|
|
544
537
|
fillRgba(data, BG, BG_WORD);
|
|
545
|
-
|
|
546
|
-
|
|
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);
|
|
538
|
+
drawStaticAsciiFleetBackground(data, width, height, scene, layout);
|
|
539
|
+
applyStaticAtmosphere(data, width, height);
|
|
555
540
|
}
|
|
556
541
|
function imageVisualFrameKey(input) {
|
|
557
542
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
@@ -576,14 +561,76 @@ function encodeKittyFrame(width, height, data, input) {
|
|
|
576
561
|
compressed: true
|
|
577
562
|
});
|
|
578
563
|
}
|
|
564
|
+
function cropRgba(data, sourceWidth, rect) {
|
|
565
|
+
const out = new Uint8ClampedArray(rect.width * rect.height * 4);
|
|
566
|
+
for (let y = 0;y < rect.height; y += 1) {
|
|
567
|
+
const sourceStart = ((rect.top + y) * sourceWidth + rect.left) * 4;
|
|
568
|
+
out.set(data.subarray(sourceStart, sourceStart + rect.width * 4), y * rect.width * 4);
|
|
569
|
+
}
|
|
570
|
+
return out;
|
|
571
|
+
}
|
|
572
|
+
function encodePanelPatch(data, sourceWidth, placement, imageId, zIndex) {
|
|
573
|
+
const patch = cropRgba(data, sourceWidth, placement);
|
|
574
|
+
const png = encodePng(placement.width, placement.height, patch);
|
|
575
|
+
const sequence = kittyImageSequence(png.toString("base64"), {
|
|
576
|
+
columns: Math.max(1, Math.round(placement.widthCells)),
|
|
577
|
+
rows: Math.max(1, Math.round(placement.heightCells)),
|
|
578
|
+
imageId,
|
|
579
|
+
zIndex,
|
|
580
|
+
format: 100
|
|
581
|
+
});
|
|
582
|
+
return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
|
|
583
|
+
}
|
|
579
584
|
function renderImageVisualFrame(input) {
|
|
580
585
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
581
586
|
const height = Math.max(1, Math.floor(input.pixelSize.height));
|
|
582
587
|
const byteCount = width * height * 4;
|
|
583
588
|
if (byteCount > MAX_IMAGE_BYTES)
|
|
584
589
|
return null;
|
|
590
|
+
const layer = input.layer ?? "full";
|
|
585
591
|
const data = new Uint8ClampedArray(byteCount);
|
|
586
|
-
drawBackground(data, width, height, input.
|
|
592
|
+
drawBackground(data, width, height, 0, input.scene, input.layout);
|
|
593
|
+
if (layer === "split") {
|
|
594
|
+
const sequences = [encodeKittyFrame(width, height, data, input)];
|
|
595
|
+
let panelIndex = 0;
|
|
596
|
+
for (const panel of input.panels) {
|
|
597
|
+
if (panel.chrome !== "ad-terminal")
|
|
598
|
+
continue;
|
|
599
|
+
const placement = panelPixelPlacement(input.layout, panel, { width, height });
|
|
600
|
+
if (!placement)
|
|
601
|
+
continue;
|
|
602
|
+
const cellH = height / input.layout.height;
|
|
603
|
+
const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
|
|
604
|
+
compositePanel(data, width, height, placement, headerPx);
|
|
605
|
+
const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
|
|
606
|
+
sequences.push(encodePanelPatch(data, width, placement, imageId, -12));
|
|
607
|
+
panelIndex += 1;
|
|
608
|
+
}
|
|
609
|
+
return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
|
|
610
|
+
}
|
|
611
|
+
if (layer === "panel-chrome")
|
|
612
|
+
return { key: imageVisualFrameKey(input), sequence: "" };
|
|
613
|
+
if (layer === "background") {
|
|
614
|
+
return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
|
|
615
|
+
}
|
|
616
|
+
if (layer === "panel-patches") {
|
|
617
|
+
const sequences = [];
|
|
618
|
+
let panelIndex = 0;
|
|
619
|
+
for (const panel of input.panels) {
|
|
620
|
+
if (panel.chrome !== "ad-terminal")
|
|
621
|
+
continue;
|
|
622
|
+
const placement = panelPixelPlacement(input.layout, panel, { width, height });
|
|
623
|
+
if (!placement)
|
|
624
|
+
continue;
|
|
625
|
+
const cellH = height / input.layout.height;
|
|
626
|
+
const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
|
|
627
|
+
compositePanel(data, width, height, placement, headerPx);
|
|
628
|
+
const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
|
|
629
|
+
sequences.push(encodePanelPatch(data, width, placement, imageId, input.zIndex));
|
|
630
|
+
panelIndex += 1;
|
|
631
|
+
}
|
|
632
|
+
return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
|
|
633
|
+
}
|
|
587
634
|
for (const panel of input.panels) {
|
|
588
635
|
if (panel.chrome !== "ad-terminal")
|
|
589
636
|
continue;
|
|
@@ -596,8 +643,11 @@ function renderImageVisualFrame(input) {
|
|
|
596
643
|
}
|
|
597
644
|
return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
|
|
598
645
|
}
|
|
646
|
+
function shouldRenderImagesInline() {
|
|
647
|
+
return process.env.RIG_OPENTUI_IMAGE_WORKER === "0" || import.meta.url.includes("$bunfs");
|
|
648
|
+
}
|
|
599
649
|
function createImageWorker() {
|
|
600
|
-
if (
|
|
650
|
+
if (shouldRenderImagesInline())
|
|
601
651
|
return null;
|
|
602
652
|
const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
|
|
603
653
|
const workerUrl = new URL(isTypeScriptRuntime ? "./image-visual-layer-worker.ts" : "./image-visual-layer-worker.js", import.meta.url);
|
|
@@ -613,24 +663,8 @@ function createImageWorker() {
|
|
|
613
663
|
}
|
|
614
664
|
}
|
|
615
665
|
var RECENT_WRITTEN_LIMIT = 24;
|
|
616
|
-
var DEFAULT_PANEL_FPS = 24;
|
|
617
|
-
var DEFAULT_PANEL_LAYER_SCALE = 0.22;
|
|
618
666
|
var DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
|
|
619
667
|
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
668
|
function imageOutputBacklogBytes() {
|
|
635
669
|
const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BACKLOG_BYTES ?? "", 10);
|
|
636
670
|
if (Number.isFinite(raw) && raw > 0)
|
|
@@ -652,52 +686,43 @@ class ImageVisualLayer {
|
|
|
652
686
|
panelImageIds = Array.from({ length: 8 }, () => allocateImageId());
|
|
653
687
|
panelChromeImageIds = Array.from({ length: 8 }, () => allocateImageId());
|
|
654
688
|
stdout;
|
|
689
|
+
onFrameReady;
|
|
655
690
|
protocol;
|
|
656
691
|
transport = imageTransport();
|
|
657
|
-
frameIntervalMs = 1000 / imageFps();
|
|
658
|
-
panelIntervalMs = 1000 / panelFps();
|
|
659
692
|
maxOutputBacklogBytes = imageOutputBacklogBytes();
|
|
660
693
|
outputBytesPerSecond = imageOutputBytesPerSecond();
|
|
661
694
|
outputBudgetBytes = imageOutputBytesPerSecond();
|
|
662
695
|
lastBudgetAtMs = Date.now();
|
|
663
696
|
motionSlot = this.createSlot();
|
|
664
|
-
|
|
665
|
-
chromeSlot = this.createSlot();
|
|
697
|
+
workersStarted = false;
|
|
666
698
|
pendingFrames = new Map;
|
|
667
699
|
recentWrittenKeys = [];
|
|
700
|
+
imagesVisible = false;
|
|
701
|
+
flushPaused = false;
|
|
668
702
|
requestId = 0;
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
lastChromeKey = "";
|
|
703
|
+
lastBackgroundKey = "";
|
|
704
|
+
fatalWorkerError = null;
|
|
672
705
|
destroyed = false;
|
|
673
|
-
constructor(stdout = process.stdout) {
|
|
706
|
+
constructor(stdout = process.stdout, onFrameReady) {
|
|
674
707
|
this.stdout = stdout;
|
|
675
|
-
this.
|
|
676
|
-
|
|
677
|
-
this.startWorker(this.motionSlot);
|
|
678
|
-
this.startWorker(this.panelSlot);
|
|
679
|
-
this.startWorker(this.chromeSlot);
|
|
680
|
-
}
|
|
708
|
+
this.onFrameReady = onFrameReady;
|
|
709
|
+
this.protocol = getRigTerminalCapabilities().imageProtocol;
|
|
681
710
|
}
|
|
682
711
|
get enabled() {
|
|
683
|
-
return
|
|
712
|
+
return this.protocol === "kitty";
|
|
684
713
|
}
|
|
685
714
|
clear() {
|
|
686
715
|
if (this.enabled) {
|
|
687
|
-
|
|
716
|
+
if (this.imagesVisible)
|
|
717
|
+
this.stdout.write([this.imageId, ...this.panelImageIds, ...this.panelChromeImageIds].map((id) => deleteKittyImage(id)).join("") + "\x1B[?25h");
|
|
718
|
+
this.imagesVisible = false;
|
|
688
719
|
this.pendingFrames.clear();
|
|
689
720
|
this.recentWrittenKeys.length = 0;
|
|
690
721
|
this.resetSlot(this.motionSlot);
|
|
691
|
-
this.
|
|
692
|
-
this.resetSlot(this.chromeSlot);
|
|
693
|
-
this.lastMotionFrameIndex = -1;
|
|
694
|
-
this.lastPanelFrameIndex = -1;
|
|
695
|
-
this.lastChromeKey = "";
|
|
722
|
+
this.lastBackgroundKey = "";
|
|
696
723
|
this.outputBudgetBytes = this.outputBytesPerSecond;
|
|
697
724
|
this.lastBudgetAtMs = Date.now();
|
|
698
725
|
this.motionSlot.clearedThroughRequestId = this.requestId;
|
|
699
|
-
this.panelSlot.clearedThroughRequestId = this.requestId;
|
|
700
|
-
this.chromeSlot.clearedThroughRequestId = this.requestId;
|
|
701
726
|
}
|
|
702
727
|
}
|
|
703
728
|
destroy() {
|
|
@@ -705,19 +730,31 @@ class ImageVisualLayer {
|
|
|
705
730
|
return;
|
|
706
731
|
this.clear();
|
|
707
732
|
this.destroyed = true;
|
|
708
|
-
const workers = [this.motionSlot.worker
|
|
733
|
+
const workers = [this.motionSlot.worker];
|
|
709
734
|
this.motionSlot.worker = null;
|
|
710
|
-
this.panelSlot.worker = null;
|
|
711
|
-
this.chromeSlot.worker = null;
|
|
712
735
|
for (const worker of workers)
|
|
713
736
|
if (worker)
|
|
714
737
|
worker.terminate().catch(() => {
|
|
715
738
|
return;
|
|
716
739
|
});
|
|
717
740
|
}
|
|
741
|
+
setFlushPaused(paused) {
|
|
742
|
+
this.flushPaused = paused;
|
|
743
|
+
}
|
|
744
|
+
isReady(input) {
|
|
745
|
+
if (!this.enabled || this.destroyed)
|
|
746
|
+
return true;
|
|
747
|
+
this.throwIfFatal();
|
|
748
|
+
const splitKey = imageVisualFrameKey({ ...input, tick: 0, layer: "split" });
|
|
749
|
+
return this.recentWrittenKeys.includes(splitKey) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === splitKey);
|
|
750
|
+
}
|
|
718
751
|
render(input) {
|
|
719
752
|
if (!this.enabled || this.destroyed)
|
|
720
753
|
return;
|
|
754
|
+
this.throwIfFatal();
|
|
755
|
+
if (this.transport !== "png")
|
|
756
|
+
this.failWorker("renderer", new Error("OpenTUI image visuals require PNG transport for static-background rendering."));
|
|
757
|
+
this.ensureWorkersStarted();
|
|
721
758
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
722
759
|
const height = Math.max(1, Math.floor(input.pixelSize.height));
|
|
723
760
|
if (width * height * 4 > MAX_IMAGE_BYTES)
|
|
@@ -725,54 +762,32 @@ class ImageVisualLayer {
|
|
|
725
762
|
if (this.isOutputBackedUp())
|
|
726
763
|
return;
|
|
727
764
|
this.refillOutputBudget();
|
|
728
|
-
const
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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;
|
|
765
|
+
const splitInput = { ...input, tick: 0, layer: "split" };
|
|
766
|
+
const splitKey = imageVisualFrameKey(splitInput);
|
|
767
|
+
if (input.force || splitKey !== this.lastBackgroundKey) {
|
|
768
|
+
this.scheduleRender(this.motionSlot, splitInput, this.imageId, -30, input.force, this.panelImageIds);
|
|
769
|
+
this.lastBackgroundKey = splitKey;
|
|
754
770
|
}
|
|
755
771
|
}
|
|
756
772
|
flush() {
|
|
773
|
+
this.throwIfFatal();
|
|
774
|
+
if (this.flushPaused)
|
|
775
|
+
return false;
|
|
757
776
|
if (this.pendingFrames.size === 0)
|
|
758
777
|
return false;
|
|
759
778
|
this.refillOutputBudget();
|
|
760
779
|
if (this.isOutputBackedUp()) {
|
|
761
|
-
const
|
|
780
|
+
const retainedSplit = this.pendingFrames.get("split");
|
|
762
781
|
this.pendingFrames.clear();
|
|
763
|
-
if (
|
|
764
|
-
this.pendingFrames.set("
|
|
782
|
+
if (retainedSplit)
|
|
783
|
+
this.pendingFrames.set("split", retainedSplit);
|
|
765
784
|
return false;
|
|
766
785
|
}
|
|
767
786
|
const framePriority = (frame) => {
|
|
768
787
|
const layer = pendingLayerKey(frame.key);
|
|
769
|
-
if (layer === "
|
|
770
|
-
return 0;
|
|
771
|
-
if (layer === "background")
|
|
788
|
+
if (layer === "split")
|
|
772
789
|
return 1;
|
|
773
|
-
|
|
774
|
-
return 2;
|
|
775
|
-
return 2;
|
|
790
|
+
return 4;
|
|
776
791
|
};
|
|
777
792
|
const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
|
|
778
793
|
const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
|
|
@@ -784,12 +799,14 @@ class ImageVisualLayer {
|
|
|
784
799
|
for (const frame of frames) {
|
|
785
800
|
const candidate = sequence + frame.sequence;
|
|
786
801
|
const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
|
|
787
|
-
|
|
802
|
+
const layer = pendingLayerKey(frame.key);
|
|
803
|
+
const allowOneStaticFrame = selected.length === 0 && layer === "split";
|
|
804
|
+
if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
|
|
788
805
|
selected.push(frame);
|
|
789
806
|
sequence = candidate;
|
|
790
807
|
byteLength = candidateBytes;
|
|
791
|
-
} else if (
|
|
792
|
-
retained.set(
|
|
808
|
+
} else if (layer === "split") {
|
|
809
|
+
retained.set(layer, frame);
|
|
793
810
|
}
|
|
794
811
|
}
|
|
795
812
|
this.pendingFrames.clear();
|
|
@@ -804,10 +821,11 @@ class ImageVisualLayer {
|
|
|
804
821
|
this.stdout.cork?.();
|
|
805
822
|
const accepted = this.stdout.write(payload);
|
|
806
823
|
this.stdout.uncork?.();
|
|
824
|
+
this.imagesVisible = true;
|
|
807
825
|
return accepted;
|
|
808
826
|
}
|
|
809
827
|
createSlot() {
|
|
810
|
-
return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0 };
|
|
828
|
+
return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0, latestRequestId: 0 };
|
|
811
829
|
}
|
|
812
830
|
resetSlot(slot) {
|
|
813
831
|
slot.busy = false;
|
|
@@ -835,7 +853,23 @@ class ImageVisualLayer {
|
|
|
835
853
|
isKnownKey(slot, key) {
|
|
836
854
|
return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
|
|
837
855
|
}
|
|
856
|
+
ensureWorkersStarted() {
|
|
857
|
+
if (this.workersStarted)
|
|
858
|
+
return;
|
|
859
|
+
this.workersStarted = true;
|
|
860
|
+
this.startWorker(this.motionSlot, "static-visuals");
|
|
861
|
+
}
|
|
862
|
+
failWorker(name, error) {
|
|
863
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
864
|
+
this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${detail}`);
|
|
865
|
+
throw this.fatalWorkerError;
|
|
866
|
+
}
|
|
867
|
+
throwIfFatal() {
|
|
868
|
+
if (this.fatalWorkerError)
|
|
869
|
+
throw this.fatalWorkerError;
|
|
870
|
+
}
|
|
838
871
|
scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
|
|
872
|
+
this.throwIfFatal();
|
|
839
873
|
const key = imageVisualFrameKey(input);
|
|
840
874
|
if (!force && this.isKnownKey(slot, key))
|
|
841
875
|
return;
|
|
@@ -848,38 +882,41 @@ class ImageVisualLayer {
|
|
|
848
882
|
requestId: ++this.requestId,
|
|
849
883
|
key
|
|
850
884
|
};
|
|
851
|
-
|
|
852
|
-
|
|
885
|
+
slot.latestRequestId = request.requestId;
|
|
886
|
+
if (!slot.worker) {
|
|
887
|
+
this.renderInline(slot, request);
|
|
853
888
|
return;
|
|
854
889
|
}
|
|
855
|
-
this.
|
|
890
|
+
this.enqueueWorkerRender(slot, request);
|
|
856
891
|
}
|
|
857
|
-
startWorker(slot) {
|
|
892
|
+
startWorker(slot, name) {
|
|
858
893
|
const worker = createImageWorker();
|
|
859
894
|
if (!worker)
|
|
860
895
|
return;
|
|
861
896
|
slot.worker = worker;
|
|
862
897
|
worker.on("message", (message) => this.handleWorkerMessage(slot, message));
|
|
863
|
-
worker.once("error", () =>
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
898
|
+
worker.once("error", (error) => {
|
|
899
|
+
if (!this.destroyed)
|
|
900
|
+
this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
901
|
+
});
|
|
902
|
+
worker.once("exit", (code) => {
|
|
903
|
+
if (!this.destroyed)
|
|
904
|
+
this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} exited unexpectedly with code ${code}.`);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
renderInline(slot, request) {
|
|
908
|
+
try {
|
|
909
|
+
const result = renderImageVisualFrame(request);
|
|
910
|
+
if (result?.sequence && !this.isKnownKey(slot, result.key) && (!this.isOutputBackedUp() || pendingLayerKey(result.key) === "split")) {
|
|
911
|
+
this.stageFrame({ key: result.key, sequence: result.sequence });
|
|
912
|
+
this.onFrameReady?.();
|
|
913
|
+
}
|
|
914
|
+
} catch (error) {
|
|
915
|
+
this.failWorker(String(request.layer ?? "visual"), error);
|
|
916
|
+
}
|
|
877
917
|
}
|
|
878
918
|
enqueueWorkerRender(slot, request) {
|
|
879
|
-
|
|
880
|
-
this.renderSync(slot, request);
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
919
|
+
this.throwIfFatal();
|
|
883
920
|
if (slot.busy) {
|
|
884
921
|
slot.queuedRequest = request;
|
|
885
922
|
slot.queuedKey = request.key;
|
|
@@ -888,13 +925,16 @@ class ImageVisualLayer {
|
|
|
888
925
|
this.dispatchWorkerRender(slot, request);
|
|
889
926
|
}
|
|
890
927
|
dispatchWorkerRender(slot, request) {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
928
|
+
this.throwIfFatal();
|
|
929
|
+
if (!slot.worker)
|
|
930
|
+
this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
|
|
895
931
|
slot.busy = true;
|
|
896
932
|
slot.inFlightKey = request.key;
|
|
897
|
-
|
|
933
|
+
try {
|
|
934
|
+
slot.worker.postMessage(request);
|
|
935
|
+
} catch (error) {
|
|
936
|
+
this.failWorker(String(request.layer ?? "visual"), error);
|
|
937
|
+
}
|
|
898
938
|
}
|
|
899
939
|
handleWorkerMessage(slot, message) {
|
|
900
940
|
if (this.destroyed || !message || typeof message !== "object")
|
|
@@ -902,14 +942,14 @@ class ImageVisualLayer {
|
|
|
902
942
|
const response = message;
|
|
903
943
|
slot.busy = false;
|
|
904
944
|
slot.inFlightKey = "";
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
945
|
+
const isStale = response.requestId <= slot.clearedThroughRequestId || response.requestId < slot.latestRequestId;
|
|
946
|
+
if (!isStale) {
|
|
947
|
+
if (response.error)
|
|
948
|
+
this.failWorker(pendingLayerKey(response.key), new Error(response.error));
|
|
949
|
+
if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "split")) {
|
|
950
|
+
this.stageFrame({ key: response.key, sequence: response.sequence });
|
|
951
|
+
this.onFrameReady?.();
|
|
952
|
+
}
|
|
913
953
|
}
|
|
914
954
|
const next = slot.queuedRequest;
|
|
915
955
|
slot.queuedRequest = null;
|
|
@@ -917,12 +957,6 @@ class ImageVisualLayer {
|
|
|
917
957
|
if (next && !this.isKnownKey(slot, next.key))
|
|
918
958
|
this.dispatchWorkerRender(slot, next);
|
|
919
959
|
}
|
|
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
960
|
}
|
|
927
961
|
function pixelSizeForRenderer(renderer) {
|
|
928
962
|
const resolution = renderer.resolution;
|
|
@@ -934,7 +968,7 @@ function pixelSizeForRenderer(renderer) {
|
|
|
934
968
|
};
|
|
935
969
|
}
|
|
936
970
|
function forceModernImageVisuals() {
|
|
937
|
-
return
|
|
971
|
+
return getRigTerminalCapabilities().supportsKittyImages;
|
|
938
972
|
}
|
|
939
973
|
export {
|
|
940
974
|
renderImageVisualFrame,
|