@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
|
@@ -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,36 +686,30 @@ 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 = [];
|
|
668
700
|
imagesVisible = false;
|
|
701
|
+
flushPaused = false;
|
|
669
702
|
requestId = 0;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
lastChromeKey = "";
|
|
703
|
+
lastBackgroundKey = "";
|
|
704
|
+
fatalWorkerError = null;
|
|
673
705
|
destroyed = false;
|
|
674
|
-
constructor(stdout = process.stdout) {
|
|
706
|
+
constructor(stdout = process.stdout, onFrameReady) {
|
|
675
707
|
this.stdout = stdout;
|
|
676
|
-
this.
|
|
677
|
-
|
|
678
|
-
this.startWorker(this.motionSlot);
|
|
679
|
-
this.startWorker(this.panelSlot);
|
|
680
|
-
this.startWorker(this.chromeSlot);
|
|
681
|
-
}
|
|
708
|
+
this.onFrameReady = onFrameReady;
|
|
709
|
+
this.protocol = getRigTerminalCapabilities().imageProtocol;
|
|
682
710
|
}
|
|
683
711
|
get enabled() {
|
|
684
|
-
return
|
|
712
|
+
return this.protocol === "kitty";
|
|
685
713
|
}
|
|
686
714
|
clear() {
|
|
687
715
|
if (this.enabled) {
|
|
@@ -691,16 +719,10 @@ class ImageVisualLayer {
|
|
|
691
719
|
this.pendingFrames.clear();
|
|
692
720
|
this.recentWrittenKeys.length = 0;
|
|
693
721
|
this.resetSlot(this.motionSlot);
|
|
694
|
-
this.
|
|
695
|
-
this.resetSlot(this.chromeSlot);
|
|
696
|
-
this.lastMotionFrameIndex = -1;
|
|
697
|
-
this.lastPanelFrameIndex = -1;
|
|
698
|
-
this.lastChromeKey = "";
|
|
722
|
+
this.lastBackgroundKey = "";
|
|
699
723
|
this.outputBudgetBytes = this.outputBytesPerSecond;
|
|
700
724
|
this.lastBudgetAtMs = Date.now();
|
|
701
725
|
this.motionSlot.clearedThroughRequestId = this.requestId;
|
|
702
|
-
this.panelSlot.clearedThroughRequestId = this.requestId;
|
|
703
|
-
this.chromeSlot.clearedThroughRequestId = this.requestId;
|
|
704
726
|
}
|
|
705
727
|
}
|
|
706
728
|
destroy() {
|
|
@@ -708,19 +730,31 @@ class ImageVisualLayer {
|
|
|
708
730
|
return;
|
|
709
731
|
this.clear();
|
|
710
732
|
this.destroyed = true;
|
|
711
|
-
const workers = [this.motionSlot.worker
|
|
733
|
+
const workers = [this.motionSlot.worker];
|
|
712
734
|
this.motionSlot.worker = null;
|
|
713
|
-
this.panelSlot.worker = null;
|
|
714
|
-
this.chromeSlot.worker = null;
|
|
715
735
|
for (const worker of workers)
|
|
716
736
|
if (worker)
|
|
717
737
|
worker.terminate().catch(() => {
|
|
718
738
|
return;
|
|
719
739
|
});
|
|
720
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
|
+
}
|
|
721
751
|
render(input) {
|
|
722
752
|
if (!this.enabled || this.destroyed)
|
|
723
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();
|
|
724
758
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
725
759
|
const height = Math.max(1, Math.floor(input.pixelSize.height));
|
|
726
760
|
if (width * height * 4 > MAX_IMAGE_BYTES)
|
|
@@ -728,54 +762,32 @@ class ImageVisualLayer {
|
|
|
728
762
|
if (this.isOutputBackedUp())
|
|
729
763
|
return;
|
|
730
764
|
this.refillOutputBudget();
|
|
731
|
-
const
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
if (!input.force && frameIndex === this.lastMotionFrameIndex)
|
|
737
|
-
return;
|
|
738
|
-
this.scheduleRender(this.motionSlot, { ...input, tick: visualTick, layer: "full" }, this.imageId, -10, input.force);
|
|
739
|
-
this.lastMotionFrameIndex = frameIndex;
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
const motionFrameIndex = Math.floor(now / this.frameIntervalMs);
|
|
743
|
-
if ((input.force || motionFrameIndex !== this.lastMotionFrameIndex) && this.outputBudgetBytes >= MIN_BACKGROUND_BUDGET_BYTES) {
|
|
744
|
-
this.scheduleRender(this.motionSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, droneLayerScale(), input.layout), panels: [], tick: visualTick, layer: "background" }, this.imageId, -30, input.force);
|
|
745
|
-
this.lastMotionFrameIndex = motionFrameIndex;
|
|
746
|
-
}
|
|
747
|
-
const panelFrameIndex = Math.floor(now / this.panelIntervalMs);
|
|
748
|
-
if ((input.force || panelFrameIndex !== this.lastPanelFrameIndex) && this.outputBudgetBytes >= MIN_PANEL_BUDGET_BYTES) {
|
|
749
|
-
this.scheduleRender(this.panelSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, panelLayerScale(), input.layout), tick: visualTick, layer: "panel-patches" }, this.imageId, -12, input.force, this.panelImageIds);
|
|
750
|
-
this.lastPanelFrameIndex = panelFrameIndex;
|
|
751
|
-
}
|
|
752
|
-
const chromeInput = { ...input, tick: 0, layer: "panel-chrome" };
|
|
753
|
-
const chromeKey = imageVisualFrameKey(chromeInput);
|
|
754
|
-
if (input.force || chromeKey !== this.lastChromeKey) {
|
|
755
|
-
this.scheduleRender(this.chromeSlot, chromeInput, this.imageId, -8, input.force, this.panelChromeImageIds);
|
|
756
|
-
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;
|
|
757
770
|
}
|
|
758
771
|
}
|
|
759
772
|
flush() {
|
|
773
|
+
this.throwIfFatal();
|
|
774
|
+
if (this.flushPaused)
|
|
775
|
+
return false;
|
|
760
776
|
if (this.pendingFrames.size === 0)
|
|
761
777
|
return false;
|
|
762
778
|
this.refillOutputBudget();
|
|
763
779
|
if (this.isOutputBackedUp()) {
|
|
764
|
-
const
|
|
780
|
+
const retainedSplit = this.pendingFrames.get("split");
|
|
765
781
|
this.pendingFrames.clear();
|
|
766
|
-
if (
|
|
767
|
-
this.pendingFrames.set("
|
|
782
|
+
if (retainedSplit)
|
|
783
|
+
this.pendingFrames.set("split", retainedSplit);
|
|
768
784
|
return false;
|
|
769
785
|
}
|
|
770
786
|
const framePriority = (frame) => {
|
|
771
787
|
const layer = pendingLayerKey(frame.key);
|
|
772
|
-
if (layer === "
|
|
773
|
-
return 0;
|
|
774
|
-
if (layer === "background")
|
|
788
|
+
if (layer === "split")
|
|
775
789
|
return 1;
|
|
776
|
-
|
|
777
|
-
return 2;
|
|
778
|
-
return 2;
|
|
790
|
+
return 4;
|
|
779
791
|
};
|
|
780
792
|
const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
|
|
781
793
|
const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
|
|
@@ -787,12 +799,14 @@ class ImageVisualLayer {
|
|
|
787
799
|
for (const frame of frames) {
|
|
788
800
|
const candidate = sequence + frame.sequence;
|
|
789
801
|
const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
|
|
790
|
-
|
|
802
|
+
const layer = pendingLayerKey(frame.key);
|
|
803
|
+
const allowOneStaticFrame = selected.length === 0 && layer === "split";
|
|
804
|
+
if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
|
|
791
805
|
selected.push(frame);
|
|
792
806
|
sequence = candidate;
|
|
793
807
|
byteLength = candidateBytes;
|
|
794
|
-
} else if (
|
|
795
|
-
retained.set(
|
|
808
|
+
} else if (layer === "split") {
|
|
809
|
+
retained.set(layer, frame);
|
|
796
810
|
}
|
|
797
811
|
}
|
|
798
812
|
this.pendingFrames.clear();
|
|
@@ -811,7 +825,7 @@ class ImageVisualLayer {
|
|
|
811
825
|
return accepted;
|
|
812
826
|
}
|
|
813
827
|
createSlot() {
|
|
814
|
-
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 };
|
|
815
829
|
}
|
|
816
830
|
resetSlot(slot) {
|
|
817
831
|
slot.busy = false;
|
|
@@ -839,7 +853,23 @@ class ImageVisualLayer {
|
|
|
839
853
|
isKnownKey(slot, key) {
|
|
840
854
|
return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
|
|
841
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
|
+
}
|
|
842
871
|
scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
|
|
872
|
+
this.throwIfFatal();
|
|
843
873
|
const key = imageVisualFrameKey(input);
|
|
844
874
|
if (!force && this.isKnownKey(slot, key))
|
|
845
875
|
return;
|
|
@@ -852,38 +882,41 @@ class ImageVisualLayer {
|
|
|
852
882
|
requestId: ++this.requestId,
|
|
853
883
|
key
|
|
854
884
|
};
|
|
855
|
-
|
|
856
|
-
|
|
885
|
+
slot.latestRequestId = request.requestId;
|
|
886
|
+
if (!slot.worker) {
|
|
887
|
+
this.renderInline(slot, request);
|
|
857
888
|
return;
|
|
858
889
|
}
|
|
859
|
-
this.
|
|
890
|
+
this.enqueueWorkerRender(slot, request);
|
|
860
891
|
}
|
|
861
|
-
startWorker(slot) {
|
|
892
|
+
startWorker(slot, name) {
|
|
862
893
|
const worker = createImageWorker();
|
|
863
894
|
if (!worker)
|
|
864
895
|
return;
|
|
865
896
|
slot.worker = worker;
|
|
866
897
|
worker.on("message", (message) => this.handleWorkerMessage(slot, message));
|
|
867
|
-
worker.once("error", () =>
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
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
|
+
}
|
|
881
917
|
}
|
|
882
918
|
enqueueWorkerRender(slot, request) {
|
|
883
|
-
|
|
884
|
-
this.renderSync(slot, request);
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
919
|
+
this.throwIfFatal();
|
|
887
920
|
if (slot.busy) {
|
|
888
921
|
slot.queuedRequest = request;
|
|
889
922
|
slot.queuedKey = request.key;
|
|
@@ -892,13 +925,16 @@ class ImageVisualLayer {
|
|
|
892
925
|
this.dispatchWorkerRender(slot, request);
|
|
893
926
|
}
|
|
894
927
|
dispatchWorkerRender(slot, request) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
}
|
|
928
|
+
this.throwIfFatal();
|
|
929
|
+
if (!slot.worker)
|
|
930
|
+
this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
|
|
899
931
|
slot.busy = true;
|
|
900
932
|
slot.inFlightKey = request.key;
|
|
901
|
-
|
|
933
|
+
try {
|
|
934
|
+
slot.worker.postMessage(request);
|
|
935
|
+
} catch (error) {
|
|
936
|
+
this.failWorker(String(request.layer ?? "visual"), error);
|
|
937
|
+
}
|
|
902
938
|
}
|
|
903
939
|
handleWorkerMessage(slot, message) {
|
|
904
940
|
if (this.destroyed || !message || typeof message !== "object")
|
|
@@ -906,14 +942,14 @@ class ImageVisualLayer {
|
|
|
906
942
|
const response = message;
|
|
907
943
|
slot.busy = false;
|
|
908
944
|
slot.inFlightKey = "";
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
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
|
+
}
|
|
917
953
|
}
|
|
918
954
|
const next = slot.queuedRequest;
|
|
919
955
|
slot.queuedRequest = null;
|
|
@@ -921,12 +957,6 @@ class ImageVisualLayer {
|
|
|
921
957
|
if (next && !this.isKnownKey(slot, next.key))
|
|
922
958
|
this.dispatchWorkerRender(slot, next);
|
|
923
959
|
}
|
|
924
|
-
renderSync(slot, request) {
|
|
925
|
-
const result = renderImageVisualFrame(request);
|
|
926
|
-
if (!result || this.isKnownKey(slot, result.key) || this.isOutputBackedUp())
|
|
927
|
-
return;
|
|
928
|
-
this.stageFrame(result);
|
|
929
|
-
}
|
|
930
960
|
}
|
|
931
961
|
function pixelSizeForRenderer(renderer) {
|
|
932
962
|
const resolution = renderer.resolution;
|
|
@@ -938,7 +968,7 @@ function pixelSizeForRenderer(renderer) {
|
|
|
938
968
|
};
|
|
939
969
|
}
|
|
940
970
|
function forceModernImageVisuals() {
|
|
941
|
-
return
|
|
971
|
+
return getRigTerminalCapabilities().supportsKittyImages;
|
|
942
972
|
}
|
|
943
973
|
export {
|
|
944
974
|
renderImageVisualFrame,
|