@h-rig/cli 0.0.6-alpha.82 → 0.0.6-alpha.84
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 +23402 -14577
- package/dist/src/app/board.js +217 -41
- 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.d.ts +0 -2
- package/dist/src/app-opentui/adapters/doctor.js +64 -39
- package/dist/src/app-opentui/adapters/family.d.ts +62 -0
- package/dist/src/app-opentui/adapters/family.js +14305 -0
- package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
- package/dist/src/app-opentui/adapters/fleet.js +90 -60
- package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
- package/dist/src/app-opentui/adapters/inbox.js +137 -78
- package/dist/src/app-opentui/adapters/init.d.ts +0 -2
- package/dist/src/app-opentui/adapters/init.js +85 -47
- package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
- package/dist/src/app-opentui/adapters/inspect.js +1024 -0
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
- package/dist/src/app-opentui/adapters/pi-attach.js +442 -125
- package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
- package/dist/src/app-opentui/adapters/pi.js +363 -0
- package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
- package/dist/src/app-opentui/adapters/plugin.js +544 -0
- package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
- package/dist/src/app-opentui/adapters/repo.js +186 -0
- package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
- package/dist/src/app-opentui/adapters/run-detail.js +180 -63
- package/dist/src/app-opentui/adapters/server.d.ts +10 -2
- package/dist/src/app-opentui/adapters/server.js +191 -45
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -2
- package/dist/src/app-opentui/adapters/tasks.js +1123 -143
- package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
- package/dist/src/app-opentui/adapters/workspace.js +333 -0
- package/dist/src/app-opentui/autocomplete.d.ts +20 -0
- package/dist/src/app-opentui/autocomplete.js +576 -0
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +25252 -16474
- package/dist/src/app-opentui/command-palette.d.ts +3 -0
- package/dist/src/app-opentui/command-palette.js +1010 -0
- 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/drone.js +8 -6
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
- package/dist/src/app-opentui/fleet-stats.js +114 -0
- 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 +5431 -2797
- package/dist/src/app-opentui/intent.js +179 -50
- package/dist/src/app-opentui/keymap.d.ts +21 -0
- package/dist/src/app-opentui/keymap.js +1748 -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.d.ts +7 -0
- package/dist/src/app-opentui/layout.js +13 -6
- package/dist/src/app-opentui/list-search.d.ts +24 -0
- package/dist/src/app-opentui/list-search.js +88 -1
- package/dist/src/app-opentui/pi-host-child.js +99 -17
- package/dist/src/app-opentui/pi-pty-host.d.ts +14 -0
- package/dist/src/app-opentui/pi-pty-host.js +30 -14
- package/dist/src/app-opentui/react/App.d.ts +9 -0
- package/dist/src/app-opentui/react/App.js +5144 -0
- package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
- package/dist/src/app-opentui/react/Backdrop.js +1834 -0
- package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
- package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
- package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
- package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
- package/dist/src/app-opentui/react/context.d.ts +17 -0
- package/dist/src/app-opentui/react/context.js +37 -0
- package/dist/src/app-opentui/react/launch.d.ts +2 -0
- package/dist/src/app-opentui/react/launch.js +5743 -0
- package/dist/src/app-opentui/react/nav.d.ts +18 -0
- package/dist/src/app-opentui/react/nav.js +54 -0
- package/dist/src/app-opentui/react/scroll.d.ts +12 -0
- package/dist/src/app-opentui/react/scroll.js +21 -0
- package/dist/src/app-opentui/react/syntax.d.ts +2 -0
- package/dist/src/app-opentui/react/syntax.js +64 -0
- package/dist/src/app-opentui/registry.js +20428 -4828
- 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/constants.d.ts +31 -0
- package/dist/src/app-opentui/render/constants.js +66 -0
- package/dist/src/app-opentui/render/graphics.d.ts +2 -1
- package/dist/src/app-opentui/render/graphics.js +228 -46
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +469 -930
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +25 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +448 -329
- package/dist/src/app-opentui/render/native-host.d.ts +37 -0
- package/dist/src/app-opentui/render/native-host.js +179 -0
- 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 +78 -38
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +165 -0
- package/dist/src/app-opentui/render/scene.d.ts +53 -1
- package/dist/src/app-opentui/render/scene.js +195 -6
- package/dist/src/app-opentui/render/text.d.ts +7 -1
- package/dist/src/app-opentui/render/text.js +15 -8
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +113 -39
- 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.d.ts +44 -1
- package/dist/src/app-opentui/runtime.js +5415 -2738
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +117 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +221 -17
- package/dist/src/app-opentui/scenes/error.js +60 -20
- package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
- package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
- package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
- package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
- package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
- package/dist/src/app-opentui/scenes/family.d.ts +3 -0
- package/dist/src/app-opentui/scenes/family.js +2144 -0
- package/dist/src/app-opentui/scenes/fleet.js +552 -43
- package/dist/src/app-opentui/scenes/handoff.js +342 -35
- package/dist/src/app-opentui/scenes/help.js +640 -56
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +329 -21
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +120 -34
- package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
- package/dist/src/app-opentui/scenes/inspect.js +793 -0
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +264 -29
- package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
- package/dist/src/app-opentui/scenes/pi.js +508 -0
- package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
- package/dist/src/app-opentui/scenes/plugin.js +486 -0
- package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
- package/dist/src/app-opentui/scenes/repo.js +424 -0
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +362 -35
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +243 -26
- package/dist/src/app-opentui/scenes/tasks.js +518 -41
- package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
- package/dist/src/app-opentui/scenes/workspace.js +426 -0
- package/dist/src/app-opentui/selectable.d.ts +19 -0
- package/dist/src/app-opentui/selectable.js +79 -0
- package/dist/src/app-opentui/state.js +129 -36
- package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
- package/dist/src/app-opentui/surface-catalog.js +540 -0
- 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/theme.d.ts +28 -6
- package/dist/src/app-opentui/theme.js +61 -8
- package/dist/src/app-opentui/types.d.ts +130 -5
- package/dist/src/commands/_authority-runs.d.ts +1 -1
- package/dist/src/commands/_authority-runs.js +2 -12
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_help-catalog.js +95 -15
- package/dist/src/commands/_operator-view.js +97 -15
- package/dist/src/commands/_pi-frontend.d.ts +2 -0
- package/dist/src/commands/_pi-frontend.js +97 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_server-events.d.ts +26 -0
- package/dist/src/commands/_server-events.js +310 -0
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/agent.js +2 -12
- 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 +100 -15
- package/dist/src/commands/server.js +71 -26
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +157 -28
- package/dist/src/commands/task-run-driver.js +64 -25
- package/dist/src/commands/task.js +196 -43
- package/dist/src/commands.js +419 -123
- package/dist/src/index.js +426 -130
- package/package.json +11 -10
- 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,122 @@
|
|
|
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/constants.ts
|
|
112
|
+
var GLOW_FALLOFF_EXP_IMAGE = 2.4;
|
|
113
|
+
|
|
114
|
+
// packages/cli/src/app-opentui/render/image-visual-layer.ts
|
|
115
|
+
var PANEL_MASK_RADIUS_MIN = 14;
|
|
116
|
+
var PANEL_MASK_RADIUS_MAX = 34;
|
|
117
|
+
var PANEL_MASK_RADIUS_FACTOR = 0.035;
|
|
118
|
+
var PANEL_BLUR_RADIUS_MIN = 8;
|
|
119
|
+
var PANEL_BLUR_RADIUS_MAX = 28;
|
|
120
|
+
var PANEL_BLUR_RADIUS_FACTOR = 0.018;
|
|
6
121
|
var BG = [7, 8, 9, 255];
|
|
7
122
|
var PANEL = [16, 17, 21, 184];
|
|
8
123
|
var PANEL_HEADER = [16, 17, 21, 190];
|
|
@@ -16,12 +131,16 @@ var PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
|
16
131
|
var CRC_TABLE = makeCrcTable();
|
|
17
132
|
var MAX_IMAGE_BYTES = 48 * 1024 * 1024;
|
|
18
133
|
var KITTY_CHUNK_SIZE = 4096;
|
|
19
|
-
var PANEL_BLUR_SCALE =
|
|
134
|
+
var PANEL_BLUR_SCALE = 1;
|
|
20
135
|
var PANEL_MASK_CACHE_LIMIT = 16;
|
|
21
136
|
var ZLIB_LEVEL = 1;
|
|
22
137
|
var DEFAULT_IMAGE_RESOLUTION_SCALE = 1;
|
|
23
|
-
var
|
|
24
|
-
|
|
138
|
+
var LOAD_BUCKETS = 5;
|
|
139
|
+
function loadBucket(load) {
|
|
140
|
+
if (!Number.isFinite(load ?? NaN))
|
|
141
|
+
return 0;
|
|
142
|
+
return Math.max(0, Math.min(LOAD_BUCKETS, Math.round((load ?? 0) * LOAD_BUCKETS)));
|
|
143
|
+
}
|
|
25
144
|
var panelMaskCache = new Map;
|
|
26
145
|
var BG_WORD = rgbaWord(BG);
|
|
27
146
|
function imageTransport() {
|
|
@@ -36,24 +155,6 @@ function imageResolutionScale() {
|
|
|
36
155
|
return Math.max(0.25, Math.min(1, raw));
|
|
37
156
|
return DEFAULT_IMAGE_RESOLUTION_SCALE;
|
|
38
157
|
}
|
|
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
158
|
function rgbaWord(color) {
|
|
58
159
|
return (color[3] << 24 | color[2] << 16 | color[1] << 8 | color[0]) >>> 0;
|
|
59
160
|
}
|
|
@@ -69,6 +170,121 @@ function fillRgba(data, color, word = rgbaWord(color)) {
|
|
|
69
170
|
data[i + 3] = color[3];
|
|
70
171
|
}
|
|
71
172
|
}
|
|
173
|
+
var ASCII_GLYPHS = {
|
|
174
|
+
".": ["00000", "00000", "00000", "00000", "00000", "00100", "00100"],
|
|
175
|
+
"'": ["00100", "00100", "00000", "00000", "00000", "00000", "00000"],
|
|
176
|
+
"-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
|
|
177
|
+
_: ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
|
|
178
|
+
"=": ["00000", "11111", "00000", "00000", "11111", "00000", "00000"],
|
|
179
|
+
"|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
|
|
180
|
+
"/": ["00001", "00010", "00010", "00100", "01000", "01000", "10000"],
|
|
181
|
+
"\\": ["10000", "01000", "01000", "00100", "00010", "00010", "00001"],
|
|
182
|
+
"(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
|
|
183
|
+
")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
|
|
184
|
+
"!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
|
|
185
|
+
"%": ["11001", "11010", "00100", "01000", "10110", "00110", "00000"],
|
|
186
|
+
$: ["00100", "11110", "10100", "11110", "00101", "11110", "00100"],
|
|
187
|
+
"@": ["01110", "10001", "10111", "10101", "10111", "10000", "01111"],
|
|
188
|
+
o: ["00000", "01110", "10001", "10001", "10001", "01110", "00000"],
|
|
189
|
+
"*": ["00100", "10101", "01110", "11111", "01110", "10101", "00100"]
|
|
190
|
+
};
|
|
191
|
+
function sceneStaticTick(scene) {
|
|
192
|
+
let hash = 0;
|
|
193
|
+
for (let index = 0;index < scene.length; index += 1)
|
|
194
|
+
hash = hash * 33 + scene.charCodeAt(index) >>> 0;
|
|
195
|
+
return 11 + hash % 97;
|
|
196
|
+
}
|
|
197
|
+
function charColor(char, row, totalRows) {
|
|
198
|
+
if (char === "@" || char === "$" || char === "o")
|
|
199
|
+
return LIME;
|
|
200
|
+
if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
|
|
201
|
+
return CYAN;
|
|
202
|
+
if (char === "." || char === "'" || char === "_" || row < 3 || row > totalRows - 4)
|
|
203
|
+
return INK_DIM;
|
|
204
|
+
if (char === "-" || char === "=" || char === "(" || char === ")")
|
|
205
|
+
return LIME_DIM;
|
|
206
|
+
return MAGENTA;
|
|
207
|
+
}
|
|
208
|
+
function fillRectAlpha(data, width, height, left, top, rectWidth, rectHeight, color, alpha) {
|
|
209
|
+
const x0 = Math.max(0, Math.floor(left));
|
|
210
|
+
const y0 = Math.max(0, Math.floor(top));
|
|
211
|
+
const x1 = Math.min(width, Math.ceil(left + rectWidth));
|
|
212
|
+
const y1 = Math.min(height, Math.ceil(top + rectHeight));
|
|
213
|
+
for (let y = y0;y < y1; y += 1) {
|
|
214
|
+
for (let x = x0;x < x1; x += 1)
|
|
215
|
+
blendOver(data, (y * width + x) * 4, color, alpha);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function drawAsciiGlyph(data, width, height, x, y, cellWidth, cellHeight, char, color, alpha) {
|
|
219
|
+
const pattern = ASCII_GLYPHS[char] ?? ASCII_GLYPHS["*"];
|
|
220
|
+
const glyphWidth = Math.max(1, cellWidth * 0.56);
|
|
221
|
+
const glyphHeight = Math.max(1, cellHeight * 0.7);
|
|
222
|
+
const originX = x + (cellWidth - glyphWidth) * 0.5;
|
|
223
|
+
const originY = y + (cellHeight - glyphHeight) * 0.5;
|
|
224
|
+
const pixelWidth = Math.max(1, glyphWidth / pattern[0].length);
|
|
225
|
+
const pixelHeight = Math.max(1, glyphHeight / pattern.length);
|
|
226
|
+
for (let row = 0;row < pattern.length; row += 1) {
|
|
227
|
+
const bits = pattern[row];
|
|
228
|
+
for (let col = 0;col < bits.length; col += 1) {
|
|
229
|
+
if (bits[col] !== "1")
|
|
230
|
+
continue;
|
|
231
|
+
fillRectAlpha(data, width, height, originX + col * pixelWidth, originY + row * pixelHeight, pixelWidth * 0.72, pixelHeight * 0.72, color, alpha);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function drawStaticAsciiFleetBackground(data, width, height, scene, layout, load, activeRuns) {
|
|
236
|
+
const rows = fleetRows(sceneStaticTick(scene), { animate: true });
|
|
237
|
+
const cellWidth = width / Math.max(1, layout.width);
|
|
238
|
+
const cellHeight = height / Math.max(1, layout.height);
|
|
239
|
+
const sideBySide = layout.width >= FLEET_GRID_WIDTH * 1.9;
|
|
240
|
+
const leftCells = sideBySide ? Math.max(1, Math.floor(layout.width * 0.3 - FLEET_GRID_WIDTH / 2)) : Math.floor((layout.width - FLEET_GRID_WIDTH) / 2);
|
|
241
|
+
const topCells = Math.floor((layout.height - FLEET_GRID_HEIGHT) / 2);
|
|
242
|
+
const fleetLeft = leftCells * cellWidth;
|
|
243
|
+
const fleetTop = topCells * cellHeight;
|
|
244
|
+
drawGlow(data, width, height, width * 0.5, height * 0.5, Math.min(width, height) * 0.4, LIME, 0.022 + load * 0.03);
|
|
245
|
+
drawGlow(data, width, height, width * 0.18, height * 0.22, Math.min(width, height) * 0.28, CYAN, 0.018 + load * 0.012);
|
|
246
|
+
drawGlow(data, width, height, width * 0.82, height * 0.78, Math.min(width, height) * 0.22, MAGENTA, 0.012 + load * 0.008);
|
|
247
|
+
let droneTotal = 0;
|
|
248
|
+
for (const line of rows)
|
|
249
|
+
for (const char of line)
|
|
250
|
+
if (char === "@" || char === "$" || char === "%")
|
|
251
|
+
droneTotal += 1;
|
|
252
|
+
const litTarget = Math.max(0, Math.min(droneTotal, activeRuns));
|
|
253
|
+
let droneIndex = 0;
|
|
254
|
+
for (let row = 0;row < rows.length; row += 1) {
|
|
255
|
+
const line = rows[row];
|
|
256
|
+
for (let col = 0;col < line.length; col += 1) {
|
|
257
|
+
const char = line[col];
|
|
258
|
+
if (char === " ")
|
|
259
|
+
continue;
|
|
260
|
+
const x = fleetLeft + col * cellWidth;
|
|
261
|
+
const y = fleetTop + row * cellHeight;
|
|
262
|
+
const color = charColor(char, row, rows.length);
|
|
263
|
+
const isDrone = char === "@" || char === "$" || char === "%";
|
|
264
|
+
const lit = isDrone && droneIndex < litTarget;
|
|
265
|
+
drawAsciiGlyph(data, width, height, x + cellWidth * 0.07, y + cellHeight * 0.05, cellWidth, cellHeight, char, color, lit ? 0.72 : 0.46);
|
|
266
|
+
if (isDrone) {
|
|
267
|
+
droneIndex += 1;
|
|
268
|
+
drawGlow(data, width, height, x + cellWidth * 0.5, y + cellHeight * 0.5, Math.max(cellWidth, cellHeight) * 0.95, color, lit ? 0.044 : 0.014);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function applyStaticAtmosphere(data, width, height, load) {
|
|
274
|
+
const cx = width * 0.5;
|
|
275
|
+
const cy = height * 0.42;
|
|
276
|
+
const radius = Math.max(width, height) * 0.78;
|
|
277
|
+
const minVignette = 0.1 - load * 0.02;
|
|
278
|
+
for (let y = 0;y < height; y += 1) {
|
|
279
|
+
for (let x = 0;x < width; x += 1) {
|
|
280
|
+
const distance = Math.hypot(x - cx, y - cy) / radius;
|
|
281
|
+
const alpha = Math.max(minVignette, Math.min(0.82, 0.12 + Math.pow(distance, 1.65) * 0.62));
|
|
282
|
+
blendOver(data, (y * width + x) * 4, BG, alpha);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.2, LIME, 0.024 + load * 0.012);
|
|
286
|
+
drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.16, CYAN, 0.018 + load * 0.008);
|
|
287
|
+
}
|
|
72
288
|
function makeCrcTable() {
|
|
73
289
|
const table = new Uint32Array(256);
|
|
74
290
|
for (let n = 0;n < 256; n += 1) {
|
|
@@ -139,9 +355,6 @@ function kittyImageSequence(base64, options) {
|
|
|
139
355
|
}
|
|
140
356
|
return chunks.join("");
|
|
141
357
|
}
|
|
142
|
-
function isModernImageTerminal(protocol) {
|
|
143
|
-
return protocol === "kitty";
|
|
144
|
-
}
|
|
145
358
|
function clampByte(value) {
|
|
146
359
|
return Math.max(0, Math.min(255, Math.round(value)));
|
|
147
360
|
}
|
|
@@ -182,7 +395,7 @@ function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
|
|
|
182
395
|
const d = Math.sqrt(dx * dx + dy * dy);
|
|
183
396
|
if (d > radius)
|
|
184
397
|
continue;
|
|
185
|
-
const falloff = Math.pow(1 - d / Math.max(1, radius),
|
|
398
|
+
const falloff = Math.pow(1 - d / Math.max(1, radius), GLOW_FALLOFF_EXP_IMAGE);
|
|
186
399
|
blendOver(data, (y * width + x) * 4, color, alpha * falloff);
|
|
187
400
|
}
|
|
188
401
|
}
|
|
@@ -196,21 +409,6 @@ function drawLine(data, width, height, x0, y0, x1, y1, color, alpha = 0.5, thick
|
|
|
196
409
|
drawDisc(data, width, height, x0 + dx * t, y0 + dy * t, thickness, color, alpha);
|
|
197
410
|
}
|
|
198
411
|
}
|
|
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
412
|
function roundRectAlpha(px, py, x, y, w, h, r) {
|
|
215
413
|
const rx = Math.max(x + r, Math.min(px, x + w - r));
|
|
216
414
|
const ry = Math.max(y + r, Math.min(py, y + h - r));
|
|
@@ -220,17 +418,7 @@ function roundRectAlpha(px, py, x, y, w, h, r) {
|
|
|
220
418
|
return Math.max(0, Math.min(1, r + 0.75 - dist));
|
|
221
419
|
}
|
|
222
420
|
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;
|
|
421
|
+
return panelPixelPlacement(layout, panel, size);
|
|
234
422
|
}
|
|
235
423
|
function downsampleRegion(data, canvasW, canvasH, rect, scale) {
|
|
236
424
|
const w = Math.max(1, Math.ceil(rect.width / scale));
|
|
@@ -324,7 +512,7 @@ function getPanelMask(width, height, rect, headerPx, blurScale) {
|
|
|
324
512
|
const cached = panelMaskCache.get(key);
|
|
325
513
|
if (cached)
|
|
326
514
|
return cached;
|
|
327
|
-
const radius = Math.max(
|
|
515
|
+
const radius = Math.max(PANEL_MASK_RADIUS_MIN, Math.min(PANEL_MASK_RADIUS_MAX, Math.round(Math.min(rect.width, rect.height) * PANEL_MASK_RADIUS_FACTOR)));
|
|
328
516
|
const sampledWidth = Math.max(1, Math.ceil(rect.width / blurScale));
|
|
329
517
|
const sampledHeight = Math.max(1, Math.ceil(rect.height / blurScale));
|
|
330
518
|
const x0 = Math.max(0, rect.left);
|
|
@@ -379,7 +567,8 @@ function getPanelMask(width, height, rect, headerPx, blurScale) {
|
|
|
379
567
|
function compositePanel(data, width, height, rect, headerPx) {
|
|
380
568
|
const blurScale = PANEL_BLUR_SCALE;
|
|
381
569
|
const sampled = downsampleRegion(data, width, height, rect, blurScale);
|
|
382
|
-
const
|
|
570
|
+
const blurRadius = Math.max(PANEL_BLUR_RADIUS_MIN, Math.min(PANEL_BLUR_RADIUS_MAX, Math.round(Math.min(rect.width, rect.height) * PANEL_BLUR_RADIUS_FACTOR)));
|
|
571
|
+
const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, blurRadius);
|
|
383
572
|
const mask = getPanelMask(width, height, rect, headerPx, blurScale);
|
|
384
573
|
for (let i = 0;i < mask.indices.length; i += 1) {
|
|
385
574
|
const idx = mask.indices[i];
|
|
@@ -397,167 +586,49 @@ function compositePanel(data, width, height, rect, headerPx) {
|
|
|
397
586
|
blendOver(data, mask.dividerIndices[i], PANEL_LINE, 0.28);
|
|
398
587
|
}
|
|
399
588
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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");
|
|
589
|
+
var RIG_GLYPH_STROKES = {
|
|
590
|
+
R: [[0, 0, 0, 1], [0, 0, 0.82, 0], [0.82, 0, 0.82, 0.5], [0, 0.5, 0.82, 0.5], [0.36, 0.5, 0.92, 1]],
|
|
591
|
+
I: [[0.5, 0, 0.5, 1], [0.18, 0, 0.82, 0], [0.18, 1, 0.82, 1]],
|
|
592
|
+
G: [[0.92, 0.08, 0.12, 0], [0.12, 0, 0.12, 1], [0.12, 1, 0.92, 1], [0.92, 0.5, 0.92, 1], [0.5, 0.5, 0.92, 0.5]]
|
|
593
|
+
};
|
|
594
|
+
function drawBigRigWordmark(data, width, height, layout, load) {
|
|
595
|
+
const sideBySide = layout.width >= FLEET_GRID_WIDTH * 1.9;
|
|
596
|
+
if (!sideBySide)
|
|
597
|
+
return;
|
|
598
|
+
const letters = "RIG";
|
|
449
599
|
const min = Math.min(width, height);
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
600
|
+
const glyphH = height * 0.42;
|
|
601
|
+
const glyphW = width * 0.13;
|
|
602
|
+
const gap = glyphW * 0.3;
|
|
603
|
+
const totalW = letters.length * glyphW + (letters.length - 1) * gap;
|
|
604
|
+
const centerX = width * 0.71;
|
|
605
|
+
const startX = centerX - totalW / 2;
|
|
606
|
+
const top = (height - glyphH) / 2;
|
|
607
|
+
const stroke = Math.max(2, min * 0.013);
|
|
608
|
+
drawGlow(data, width, height, centerX, top + glyphH / 2, Math.max(glyphW, glyphH) * 0.9, LIME, 0.022 + load * 0.02);
|
|
609
|
+
const strokeAlpha = 0.1 + load * 0.06;
|
|
610
|
+
letters.split("").forEach((letter, index) => {
|
|
611
|
+
const segments = RIG_GLYPH_STROKES[letter];
|
|
612
|
+
if (!segments)
|
|
613
|
+
return;
|
|
614
|
+
const ox = startX + index * (glyphW + gap);
|
|
615
|
+
for (const [x0, y0, x1, y1] of segments) {
|
|
616
|
+
drawLine(data, width, height, ox + x0 * glyphW, top + y0 * glyphH, ox + x1 * glyphW, top + y1 * glyphH, LIME_DIM, strokeAlpha, stroke);
|
|
460
617
|
}
|
|
461
618
|
});
|
|
462
|
-
|
|
463
|
-
|
|
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) {
|
|
619
|
+
}
|
|
620
|
+
function drawBackground(data, width, height, _tick, scene, layout, load, activeRuns) {
|
|
544
621
|
fillRgba(data, BG, BG_WORD);
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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);
|
|
622
|
+
drawStaticAsciiFleetBackground(data, width, height, scene, layout, load, activeRuns);
|
|
623
|
+
drawBigRigWordmark(data, width, height, layout, load);
|
|
624
|
+
applyStaticAtmosphere(data, width, height, load);
|
|
555
625
|
}
|
|
556
626
|
function imageVisualFrameKey(input) {
|
|
557
627
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
558
628
|
const height = Math.max(1, Math.floor(input.pixelSize.height));
|
|
559
629
|
const layer = "layer" in input ? input.layer ?? "full" : "full";
|
|
560
|
-
|
|
630
|
+
const load = loadBucket(input.load);
|
|
631
|
+
return `${layer}:${width}x${height}:${input.layout.width}x${input.layout.height}:${input.scene}:${input.tick}:l${load}:${input.panels.map((panel) => `${panel.id}:${panel.top}:${panel.height}:${panel.width ?? ""}:${panel.left ?? ""}:${panel.headerHeight ?? ""}`).join("|")}`;
|
|
561
632
|
}
|
|
562
633
|
function encodeKittyFrame(width, height, data, input) {
|
|
563
634
|
if (input.transport === "png") {
|
|
@@ -576,14 +647,78 @@ function encodeKittyFrame(width, height, data, input) {
|
|
|
576
647
|
compressed: true
|
|
577
648
|
});
|
|
578
649
|
}
|
|
650
|
+
function cropRgba(data, sourceWidth, rect) {
|
|
651
|
+
const out = new Uint8ClampedArray(rect.width * rect.height * 4);
|
|
652
|
+
for (let y = 0;y < rect.height; y += 1) {
|
|
653
|
+
const sourceStart = ((rect.top + y) * sourceWidth + rect.left) * 4;
|
|
654
|
+
out.set(data.subarray(sourceStart, sourceStart + rect.width * 4), y * rect.width * 4);
|
|
655
|
+
}
|
|
656
|
+
return out;
|
|
657
|
+
}
|
|
658
|
+
function encodePanelPatch(data, sourceWidth, placement, imageId, zIndex) {
|
|
659
|
+
const patch = cropRgba(data, sourceWidth, placement);
|
|
660
|
+
const png = encodePng(placement.width, placement.height, patch);
|
|
661
|
+
const sequence = kittyImageSequence(png.toString("base64"), {
|
|
662
|
+
columns: Math.max(1, Math.round(placement.widthCells)),
|
|
663
|
+
rows: Math.max(1, Math.round(placement.heightCells)),
|
|
664
|
+
imageId,
|
|
665
|
+
zIndex,
|
|
666
|
+
format: 100
|
|
667
|
+
});
|
|
668
|
+
return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
|
|
669
|
+
}
|
|
579
670
|
function renderImageVisualFrame(input) {
|
|
580
671
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
581
672
|
const height = Math.max(1, Math.floor(input.pixelSize.height));
|
|
582
673
|
const byteCount = width * height * 4;
|
|
583
674
|
if (byteCount > MAX_IMAGE_BYTES)
|
|
584
675
|
return null;
|
|
676
|
+
const layer = input.layer ?? "full";
|
|
585
677
|
const data = new Uint8ClampedArray(byteCount);
|
|
586
|
-
|
|
678
|
+
const load = loadBucket(input.load) / LOAD_BUCKETS;
|
|
679
|
+
const activeRuns = Math.max(0, Math.floor(input.activeRuns ?? 0));
|
|
680
|
+
drawBackground(data, width, height, 0, input.scene, input.layout, load, activeRuns);
|
|
681
|
+
if (layer === "split") {
|
|
682
|
+
const sequences = [encodeKittyFrame(width, height, data, input)];
|
|
683
|
+
let panelIndex = 0;
|
|
684
|
+
for (const panel of input.panels) {
|
|
685
|
+
if (panel.chrome !== "ad-terminal")
|
|
686
|
+
continue;
|
|
687
|
+
const placement = panelPixelPlacement(input.layout, panel, { width, height });
|
|
688
|
+
if (!placement)
|
|
689
|
+
continue;
|
|
690
|
+
const cellH = height / input.layout.height;
|
|
691
|
+
const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
|
|
692
|
+
compositePanel(data, width, height, placement, headerPx);
|
|
693
|
+
const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
|
|
694
|
+
sequences.push(encodePanelPatch(data, width, placement, imageId, -12));
|
|
695
|
+
panelIndex += 1;
|
|
696
|
+
}
|
|
697
|
+
return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
|
|
698
|
+
}
|
|
699
|
+
if (layer === "panel-chrome")
|
|
700
|
+
return { key: imageVisualFrameKey(input), sequence: "" };
|
|
701
|
+
if (layer === "background") {
|
|
702
|
+
return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
|
|
703
|
+
}
|
|
704
|
+
if (layer === "panel-patches") {
|
|
705
|
+
const sequences = [];
|
|
706
|
+
let panelIndex = 0;
|
|
707
|
+
for (const panel of input.panels) {
|
|
708
|
+
if (panel.chrome !== "ad-terminal")
|
|
709
|
+
continue;
|
|
710
|
+
const placement = panelPixelPlacement(input.layout, panel, { width, height });
|
|
711
|
+
if (!placement)
|
|
712
|
+
continue;
|
|
713
|
+
const cellH = height / input.layout.height;
|
|
714
|
+
const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
|
|
715
|
+
compositePanel(data, width, height, placement, headerPx);
|
|
716
|
+
const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
|
|
717
|
+
sequences.push(encodePanelPatch(data, width, placement, imageId, input.zIndex));
|
|
718
|
+
panelIndex += 1;
|
|
719
|
+
}
|
|
720
|
+
return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
|
|
721
|
+
}
|
|
587
722
|
for (const panel of input.panels) {
|
|
588
723
|
if (panel.chrome !== "ad-terminal")
|
|
589
724
|
continue;
|
|
@@ -596,8 +731,11 @@ function renderImageVisualFrame(input) {
|
|
|
596
731
|
}
|
|
597
732
|
return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
|
|
598
733
|
}
|
|
734
|
+
function shouldRenderImagesInline() {
|
|
735
|
+
return process.env.RIG_OPENTUI_IMAGE_WORKER === "0" || import.meta.url.includes("$bunfs");
|
|
736
|
+
}
|
|
599
737
|
function createImageWorker() {
|
|
600
|
-
if (
|
|
738
|
+
if (shouldRenderImagesInline())
|
|
601
739
|
return null;
|
|
602
740
|
const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
|
|
603
741
|
const workerUrl = new URL(isTypeScriptRuntime ? "./image-visual-layer-worker.ts" : "./image-visual-layer-worker.js", import.meta.url);
|
|
@@ -613,24 +751,8 @@ function createImageWorker() {
|
|
|
613
751
|
}
|
|
614
752
|
}
|
|
615
753
|
var RECENT_WRITTEN_LIMIT = 24;
|
|
616
|
-
var DEFAULT_PANEL_FPS = 24;
|
|
617
|
-
var DEFAULT_PANEL_LAYER_SCALE = 0.22;
|
|
618
754
|
var DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
|
|
619
755
|
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
756
|
function imageOutputBacklogBytes() {
|
|
635
757
|
const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BACKLOG_BYTES ?? "", 10);
|
|
636
758
|
if (Number.isFinite(raw) && raw > 0)
|
|
@@ -652,36 +774,30 @@ class ImageVisualLayer {
|
|
|
652
774
|
panelImageIds = Array.from({ length: 8 }, () => allocateImageId());
|
|
653
775
|
panelChromeImageIds = Array.from({ length: 8 }, () => allocateImageId());
|
|
654
776
|
stdout;
|
|
777
|
+
onFrameReady;
|
|
655
778
|
protocol;
|
|
656
779
|
transport = imageTransport();
|
|
657
|
-
frameIntervalMs = 1000 / imageFps();
|
|
658
|
-
panelIntervalMs = 1000 / panelFps();
|
|
659
780
|
maxOutputBacklogBytes = imageOutputBacklogBytes();
|
|
660
781
|
outputBytesPerSecond = imageOutputBytesPerSecond();
|
|
661
782
|
outputBudgetBytes = imageOutputBytesPerSecond();
|
|
662
783
|
lastBudgetAtMs = Date.now();
|
|
663
784
|
motionSlot = this.createSlot();
|
|
664
|
-
|
|
665
|
-
chromeSlot = this.createSlot();
|
|
785
|
+
workersStarted = false;
|
|
666
786
|
pendingFrames = new Map;
|
|
667
787
|
recentWrittenKeys = [];
|
|
668
788
|
imagesVisible = false;
|
|
789
|
+
flushPaused = false;
|
|
669
790
|
requestId = 0;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
lastChromeKey = "";
|
|
791
|
+
lastBackgroundKey = "";
|
|
792
|
+
fatalWorkerError = null;
|
|
673
793
|
destroyed = false;
|
|
674
|
-
constructor(stdout = process.stdout) {
|
|
794
|
+
constructor(stdout = process.stdout, onFrameReady) {
|
|
675
795
|
this.stdout = stdout;
|
|
676
|
-
this.
|
|
677
|
-
|
|
678
|
-
this.startWorker(this.motionSlot);
|
|
679
|
-
this.startWorker(this.panelSlot);
|
|
680
|
-
this.startWorker(this.chromeSlot);
|
|
681
|
-
}
|
|
796
|
+
this.onFrameReady = onFrameReady;
|
|
797
|
+
this.protocol = getRigTerminalCapabilities().imageProtocol;
|
|
682
798
|
}
|
|
683
799
|
get enabled() {
|
|
684
|
-
return
|
|
800
|
+
return this.protocol === "kitty";
|
|
685
801
|
}
|
|
686
802
|
clear() {
|
|
687
803
|
if (this.enabled) {
|
|
@@ -691,16 +807,10 @@ class ImageVisualLayer {
|
|
|
691
807
|
this.pendingFrames.clear();
|
|
692
808
|
this.recentWrittenKeys.length = 0;
|
|
693
809
|
this.resetSlot(this.motionSlot);
|
|
694
|
-
this.
|
|
695
|
-
this.resetSlot(this.chromeSlot);
|
|
696
|
-
this.lastMotionFrameIndex = -1;
|
|
697
|
-
this.lastPanelFrameIndex = -1;
|
|
698
|
-
this.lastChromeKey = "";
|
|
810
|
+
this.lastBackgroundKey = "";
|
|
699
811
|
this.outputBudgetBytes = this.outputBytesPerSecond;
|
|
700
812
|
this.lastBudgetAtMs = Date.now();
|
|
701
813
|
this.motionSlot.clearedThroughRequestId = this.requestId;
|
|
702
|
-
this.panelSlot.clearedThroughRequestId = this.requestId;
|
|
703
|
-
this.chromeSlot.clearedThroughRequestId = this.requestId;
|
|
704
814
|
}
|
|
705
815
|
}
|
|
706
816
|
destroy() {
|
|
@@ -708,19 +818,31 @@ class ImageVisualLayer {
|
|
|
708
818
|
return;
|
|
709
819
|
this.clear();
|
|
710
820
|
this.destroyed = true;
|
|
711
|
-
const workers = [this.motionSlot.worker
|
|
821
|
+
const workers = [this.motionSlot.worker];
|
|
712
822
|
this.motionSlot.worker = null;
|
|
713
|
-
this.panelSlot.worker = null;
|
|
714
|
-
this.chromeSlot.worker = null;
|
|
715
823
|
for (const worker of workers)
|
|
716
824
|
if (worker)
|
|
717
825
|
worker.terminate().catch(() => {
|
|
718
826
|
return;
|
|
719
827
|
});
|
|
720
828
|
}
|
|
829
|
+
setFlushPaused(paused) {
|
|
830
|
+
this.flushPaused = paused;
|
|
831
|
+
}
|
|
832
|
+
isReady(input) {
|
|
833
|
+
if (!this.enabled || this.destroyed)
|
|
834
|
+
return true;
|
|
835
|
+
this.throwIfFatal();
|
|
836
|
+
const splitKey = imageVisualFrameKey({ ...input, tick: 0, layer: "split" });
|
|
837
|
+
return this.recentWrittenKeys.includes(splitKey) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === splitKey);
|
|
838
|
+
}
|
|
721
839
|
render(input) {
|
|
722
840
|
if (!this.enabled || this.destroyed)
|
|
723
841
|
return;
|
|
842
|
+
this.throwIfFatal();
|
|
843
|
+
if (this.transport !== "png")
|
|
844
|
+
this.failWorker("renderer", new Error("OpenTUI image visuals require PNG transport for static-background rendering."));
|
|
845
|
+
this.ensureWorkersStarted();
|
|
724
846
|
const width = Math.max(1, Math.floor(input.pixelSize.width));
|
|
725
847
|
const height = Math.max(1, Math.floor(input.pixelSize.height));
|
|
726
848
|
if (width * height * 4 > MAX_IMAGE_BYTES)
|
|
@@ -728,54 +850,32 @@ class ImageVisualLayer {
|
|
|
728
850
|
if (this.isOutputBackedUp())
|
|
729
851
|
return;
|
|
730
852
|
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;
|
|
853
|
+
const splitInput = { ...input, tick: 0, layer: "split" };
|
|
854
|
+
const splitKey = imageVisualFrameKey(splitInput);
|
|
855
|
+
if (input.force || splitKey !== this.lastBackgroundKey) {
|
|
856
|
+
this.scheduleRender(this.motionSlot, splitInput, this.imageId, -30, input.force, this.panelImageIds);
|
|
857
|
+
this.lastBackgroundKey = splitKey;
|
|
757
858
|
}
|
|
758
859
|
}
|
|
759
860
|
flush() {
|
|
861
|
+
this.throwIfFatal();
|
|
862
|
+
if (this.flushPaused)
|
|
863
|
+
return false;
|
|
760
864
|
if (this.pendingFrames.size === 0)
|
|
761
865
|
return false;
|
|
762
866
|
this.refillOutputBudget();
|
|
763
867
|
if (this.isOutputBackedUp()) {
|
|
764
|
-
const
|
|
868
|
+
const retainedSplit = this.pendingFrames.get("split");
|
|
765
869
|
this.pendingFrames.clear();
|
|
766
|
-
if (
|
|
767
|
-
this.pendingFrames.set("
|
|
870
|
+
if (retainedSplit)
|
|
871
|
+
this.pendingFrames.set("split", retainedSplit);
|
|
768
872
|
return false;
|
|
769
873
|
}
|
|
770
874
|
const framePriority = (frame) => {
|
|
771
875
|
const layer = pendingLayerKey(frame.key);
|
|
772
|
-
if (layer === "
|
|
773
|
-
return 0;
|
|
774
|
-
if (layer === "background")
|
|
876
|
+
if (layer === "split")
|
|
775
877
|
return 1;
|
|
776
|
-
|
|
777
|
-
return 2;
|
|
778
|
-
return 2;
|
|
878
|
+
return 4;
|
|
779
879
|
};
|
|
780
880
|
const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
|
|
781
881
|
const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
|
|
@@ -787,12 +887,14 @@ class ImageVisualLayer {
|
|
|
787
887
|
for (const frame of frames) {
|
|
788
888
|
const candidate = sequence + frame.sequence;
|
|
789
889
|
const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
|
|
790
|
-
|
|
890
|
+
const layer = pendingLayerKey(frame.key);
|
|
891
|
+
const allowOneStaticFrame = selected.length === 0 && layer === "split";
|
|
892
|
+
if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
|
|
791
893
|
selected.push(frame);
|
|
792
894
|
sequence = candidate;
|
|
793
895
|
byteLength = candidateBytes;
|
|
794
|
-
} else if (
|
|
795
|
-
retained.set(
|
|
896
|
+
} else if (layer === "split") {
|
|
897
|
+
retained.set(layer, frame);
|
|
796
898
|
}
|
|
797
899
|
}
|
|
798
900
|
this.pendingFrames.clear();
|
|
@@ -811,7 +913,7 @@ class ImageVisualLayer {
|
|
|
811
913
|
return accepted;
|
|
812
914
|
}
|
|
813
915
|
createSlot() {
|
|
814
|
-
return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0 };
|
|
916
|
+
return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0, latestRequestId: 0 };
|
|
815
917
|
}
|
|
816
918
|
resetSlot(slot) {
|
|
817
919
|
slot.busy = false;
|
|
@@ -839,7 +941,23 @@ class ImageVisualLayer {
|
|
|
839
941
|
isKnownKey(slot, key) {
|
|
840
942
|
return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
|
|
841
943
|
}
|
|
944
|
+
ensureWorkersStarted() {
|
|
945
|
+
if (this.workersStarted)
|
|
946
|
+
return;
|
|
947
|
+
this.workersStarted = true;
|
|
948
|
+
this.startWorker(this.motionSlot, "static-visuals");
|
|
949
|
+
}
|
|
950
|
+
failWorker(name, error) {
|
|
951
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
952
|
+
this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${detail}`);
|
|
953
|
+
throw this.fatalWorkerError;
|
|
954
|
+
}
|
|
955
|
+
throwIfFatal() {
|
|
956
|
+
if (this.fatalWorkerError)
|
|
957
|
+
throw this.fatalWorkerError;
|
|
958
|
+
}
|
|
842
959
|
scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
|
|
960
|
+
this.throwIfFatal();
|
|
843
961
|
const key = imageVisualFrameKey(input);
|
|
844
962
|
if (!force && this.isKnownKey(slot, key))
|
|
845
963
|
return;
|
|
@@ -852,38 +970,41 @@ class ImageVisualLayer {
|
|
|
852
970
|
requestId: ++this.requestId,
|
|
853
971
|
key
|
|
854
972
|
};
|
|
855
|
-
|
|
856
|
-
|
|
973
|
+
slot.latestRequestId = request.requestId;
|
|
974
|
+
if (!slot.worker) {
|
|
975
|
+
this.renderInline(slot, request);
|
|
857
976
|
return;
|
|
858
977
|
}
|
|
859
|
-
this.
|
|
978
|
+
this.enqueueWorkerRender(slot, request);
|
|
860
979
|
}
|
|
861
|
-
startWorker(slot) {
|
|
980
|
+
startWorker(slot, name) {
|
|
862
981
|
const worker = createImageWorker();
|
|
863
982
|
if (!worker)
|
|
864
983
|
return;
|
|
865
984
|
slot.worker = worker;
|
|
866
985
|
worker.on("message", (message) => this.handleWorkerMessage(slot, message));
|
|
867
|
-
worker.once("error", () =>
|
|
868
|
-
|
|
986
|
+
worker.once("error", (error) => {
|
|
987
|
+
if (!this.destroyed)
|
|
988
|
+
this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
989
|
+
});
|
|
990
|
+
worker.once("exit", (code) => {
|
|
991
|
+
if (!this.destroyed)
|
|
992
|
+
this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} exited unexpectedly with code ${code}.`);
|
|
993
|
+
});
|
|
869
994
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
this.renderSync(slot, queued);
|
|
995
|
+
renderInline(slot, request) {
|
|
996
|
+
try {
|
|
997
|
+
const result = renderImageVisualFrame(request);
|
|
998
|
+
if (result?.sequence && !this.isKnownKey(slot, result.key) && (!this.isOutputBackedUp() || pendingLayerKey(result.key) === "split")) {
|
|
999
|
+
this.stageFrame({ key: result.key, sequence: result.sequence });
|
|
1000
|
+
this.onFrameReady?.();
|
|
1001
|
+
}
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
this.failWorker(String(request.layer ?? "visual"), error);
|
|
1004
|
+
}
|
|
881
1005
|
}
|
|
882
1006
|
enqueueWorkerRender(slot, request) {
|
|
883
|
-
|
|
884
|
-
this.renderSync(slot, request);
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
1007
|
+
this.throwIfFatal();
|
|
887
1008
|
if (slot.busy) {
|
|
888
1009
|
slot.queuedRequest = request;
|
|
889
1010
|
slot.queuedKey = request.key;
|
|
@@ -892,13 +1013,16 @@ class ImageVisualLayer {
|
|
|
892
1013
|
this.dispatchWorkerRender(slot, request);
|
|
893
1014
|
}
|
|
894
1015
|
dispatchWorkerRender(slot, request) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
}
|
|
1016
|
+
this.throwIfFatal();
|
|
1017
|
+
if (!slot.worker)
|
|
1018
|
+
this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
|
|
899
1019
|
slot.busy = true;
|
|
900
1020
|
slot.inFlightKey = request.key;
|
|
901
|
-
|
|
1021
|
+
try {
|
|
1022
|
+
slot.worker.postMessage(request);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
this.failWorker(String(request.layer ?? "visual"), error);
|
|
1025
|
+
}
|
|
902
1026
|
}
|
|
903
1027
|
handleWorkerMessage(slot, message) {
|
|
904
1028
|
if (this.destroyed || !message || typeof message !== "object")
|
|
@@ -906,14 +1030,14 @@ class ImageVisualLayer {
|
|
|
906
1030
|
const response = message;
|
|
907
1031
|
slot.busy = false;
|
|
908
1032
|
slot.inFlightKey = "";
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1033
|
+
const isStale = response.requestId <= slot.clearedThroughRequestId || response.requestId < slot.latestRequestId;
|
|
1034
|
+
if (!isStale) {
|
|
1035
|
+
if (response.error)
|
|
1036
|
+
this.failWorker(pendingLayerKey(response.key), new Error(response.error));
|
|
1037
|
+
if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "split")) {
|
|
1038
|
+
this.stageFrame({ key: response.key, sequence: response.sequence });
|
|
1039
|
+
this.onFrameReady?.();
|
|
1040
|
+
}
|
|
917
1041
|
}
|
|
918
1042
|
const next = slot.queuedRequest;
|
|
919
1043
|
slot.queuedRequest = null;
|
|
@@ -921,12 +1045,6 @@ class ImageVisualLayer {
|
|
|
921
1045
|
if (next && !this.isKnownKey(slot, next.key))
|
|
922
1046
|
this.dispatchWorkerRender(slot, next);
|
|
923
1047
|
}
|
|
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
1048
|
}
|
|
931
1049
|
function pixelSizeForRenderer(renderer) {
|
|
932
1050
|
const resolution = renderer.resolution;
|
|
@@ -938,11 +1056,12 @@ function pixelSizeForRenderer(renderer) {
|
|
|
938
1056
|
};
|
|
939
1057
|
}
|
|
940
1058
|
function forceModernImageVisuals() {
|
|
941
|
-
return
|
|
1059
|
+
return getRigTerminalCapabilities().supportsKittyImages;
|
|
942
1060
|
}
|
|
943
1061
|
export {
|
|
944
1062
|
renderImageVisualFrame,
|
|
945
1063
|
pixelSizeForRenderer,
|
|
1064
|
+
loadBucket,
|
|
946
1065
|
imageVisualFrameKey,
|
|
947
1066
|
forceModernImageVisuals,
|
|
948
1067
|
ImageVisualLayer
|