@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.
Files changed (206) hide show
  1. package/dist/bin/build-rig-binaries.js +40 -8
  2. package/dist/bin/rig.js +23402 -14577
  3. package/dist/src/app/board.js +217 -41
  4. package/dist/src/app-opentui/adapters/command.d.ts +2 -0
  5. package/dist/src/app-opentui/adapters/command.js +329 -0
  6. package/dist/src/app-opentui/adapters/common.js +2 -2
  7. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/doctor.js +64 -39
  9. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  10. package/dist/src/app-opentui/adapters/family.js +14305 -0
  11. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/fleet.js +90 -60
  13. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  14. package/dist/src/app-opentui/adapters/inbox.js +137 -78
  15. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  16. package/dist/src/app-opentui/adapters/init.js +85 -47
  17. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  18. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  19. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  20. package/dist/src/app-opentui/adapters/pi-attach.js +442 -125
  21. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  22. package/dist/src/app-opentui/adapters/pi.js +363 -0
  23. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  24. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  25. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  26. package/dist/src/app-opentui/adapters/repo.js +186 -0
  27. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  28. package/dist/src/app-opentui/adapters/run-detail.js +180 -63
  29. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  30. package/dist/src/app-opentui/adapters/server.js +191 -45
  31. package/dist/src/app-opentui/adapters/tasks.d.ts +11 -2
  32. package/dist/src/app-opentui/adapters/tasks.js +1123 -143
  33. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  34. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  35. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  36. package/dist/src/app-opentui/autocomplete.js +576 -0
  37. package/dist/src/app-opentui/bootstrap.d.ts +1 -6
  38. package/dist/src/app-opentui/bootstrap.js +25252 -16474
  39. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  40. package/dist/src/app-opentui/command-palette.js +1010 -0
  41. package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
  42. package/dist/src/app-opentui/command-pty-host.js +248 -0
  43. package/dist/src/app-opentui/drone.js +8 -6
  44. package/dist/src/app-opentui/events.js +1 -1
  45. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  46. package/dist/src/app-opentui/fleet-stats.js +114 -0
  47. package/dist/src/app-opentui/focus-manager.d.ts +14 -0
  48. package/dist/src/app-opentui/focus-manager.js +24 -0
  49. package/dist/src/app-opentui/index.js +5431 -2797
  50. package/dist/src/app-opentui/intent.js +179 -50
  51. package/dist/src/app-opentui/keymap.d.ts +21 -0
  52. package/dist/src/app-opentui/keymap.js +1748 -0
  53. package/dist/src/app-opentui/launch-routing.d.ts +16 -0
  54. package/dist/src/app-opentui/launch-routing.js +55 -0
  55. package/dist/src/app-opentui/layout.d.ts +7 -0
  56. package/dist/src/app-opentui/layout.js +13 -6
  57. package/dist/src/app-opentui/list-search.d.ts +24 -0
  58. package/dist/src/app-opentui/list-search.js +88 -1
  59. package/dist/src/app-opentui/pi-host-child.js +99 -17
  60. package/dist/src/app-opentui/pi-pty-host.d.ts +14 -0
  61. package/dist/src/app-opentui/pi-pty-host.js +30 -14
  62. package/dist/src/app-opentui/react/App.d.ts +9 -0
  63. package/dist/src/app-opentui/react/App.js +5144 -0
  64. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  65. package/dist/src/app-opentui/react/Backdrop.js +1834 -0
  66. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  67. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  68. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  69. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  70. package/dist/src/app-opentui/react/context.d.ts +17 -0
  71. package/dist/src/app-opentui/react/context.js +37 -0
  72. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  73. package/dist/src/app-opentui/react/launch.js +5743 -0
  74. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  75. package/dist/src/app-opentui/react/nav.js +54 -0
  76. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  77. package/dist/src/app-opentui/react/scroll.js +21 -0
  78. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  79. package/dist/src/app-opentui/react/syntax.js +64 -0
  80. package/dist/src/app-opentui/registry.js +20428 -4828
  81. package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
  82. package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
  83. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  84. package/dist/src/app-opentui/render/constants.js +66 -0
  85. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  86. package/dist/src/app-opentui/render/graphics.js +228 -46
  87. package/dist/src/app-opentui/render/image-visual-layer-worker.js +469 -930
  88. package/dist/src/app-opentui/render/image-visual-layer.d.ts +25 -10
  89. package/dist/src/app-opentui/render/image-visual-layer.js +448 -329
  90. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  91. package/dist/src/app-opentui/render/native-host.js +179 -0
  92. package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
  93. package/dist/src/app-opentui/render/panel-layout.js +48 -0
  94. package/dist/src/app-opentui/render/panels.d.ts +2 -0
  95. package/dist/src/app-opentui/render/panels.js +78 -38
  96. package/dist/src/app-opentui/render/preloader.d.ts +10 -0
  97. package/dist/src/app-opentui/render/preloader.js +165 -0
  98. package/dist/src/app-opentui/render/scene.d.ts +53 -1
  99. package/dist/src/app-opentui/render/scene.js +195 -6
  100. package/dist/src/app-opentui/render/text.d.ts +7 -1
  101. package/dist/src/app-opentui/render/text.js +15 -8
  102. package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
  103. package/dist/src/app-opentui/render/type-bar.js +113 -39
  104. package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
  105. package/dist/src/app-opentui/runtime-resources.js +62 -0
  106. package/dist/src/app-opentui/runtime.d.ts +44 -1
  107. package/dist/src/app-opentui/runtime.js +5415 -2738
  108. package/dist/src/app-opentui/scenes/command.d.ts +3 -0
  109. package/dist/src/app-opentui/scenes/command.js +117 -0
  110. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
  111. package/dist/src/app-opentui/scenes/doctor.js +221 -17
  112. package/dist/src/app-opentui/scenes/error.js +60 -20
  113. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  114. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  115. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  116. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  117. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  118. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  119. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  120. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  121. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  122. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  123. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  124. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  125. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  126. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  127. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  128. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  129. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  130. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  131. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  132. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  133. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  134. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  135. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  136. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  137. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  138. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  139. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  140. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  141. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  142. package/dist/src/app-opentui/scenes/family.js +2144 -0
  143. package/dist/src/app-opentui/scenes/fleet.js +552 -43
  144. package/dist/src/app-opentui/scenes/handoff.js +342 -35
  145. package/dist/src/app-opentui/scenes/help.js +640 -56
  146. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
  147. package/dist/src/app-opentui/scenes/inbox.js +329 -21
  148. package/dist/src/app-opentui/scenes/init.d.ts +2 -1
  149. package/dist/src/app-opentui/scenes/init.js +120 -34
  150. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  151. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  152. package/dist/src/app-opentui/scenes/main.d.ts +2 -1
  153. package/dist/src/app-opentui/scenes/main.js +264 -29
  154. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  155. package/dist/src/app-opentui/scenes/pi.js +508 -0
  156. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  157. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  158. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  159. package/dist/src/app-opentui/scenes/repo.js +424 -0
  160. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
  161. package/dist/src/app-opentui/scenes/run-detail.js +362 -35
  162. package/dist/src/app-opentui/scenes/server.d.ts +2 -1
  163. package/dist/src/app-opentui/scenes/server.js +243 -26
  164. package/dist/src/app-opentui/scenes/tasks.js +518 -41
  165. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  166. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  167. package/dist/src/app-opentui/selectable.d.ts +19 -0
  168. package/dist/src/app-opentui/selectable.js +79 -0
  169. package/dist/src/app-opentui/state.js +129 -36
  170. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  171. package/dist/src/app-opentui/surface-catalog.js +540 -0
  172. package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
  173. package/dist/src/app-opentui/terminal-capabilities.js +15 -0
  174. package/dist/src/app-opentui/theme.d.ts +28 -6
  175. package/dist/src/app-opentui/theme.js +61 -8
  176. package/dist/src/app-opentui/types.d.ts +130 -5
  177. package/dist/src/commands/_authority-runs.d.ts +1 -1
  178. package/dist/src/commands/_authority-runs.js +2 -12
  179. package/dist/src/commands/_doctor-checks.js +62 -13
  180. package/dist/src/commands/_help-catalog.js +95 -15
  181. package/dist/src/commands/_operator-view.js +97 -15
  182. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  183. package/dist/src/commands/_pi-frontend.js +97 -13
  184. package/dist/src/commands/_preflight.js +64 -14
  185. package/dist/src/commands/_server-client.js +82 -18
  186. package/dist/src/commands/_server-events.d.ts +26 -0
  187. package/dist/src/commands/_server-events.js +310 -0
  188. package/dist/src/commands/_snapshot-upload.js +62 -13
  189. package/dist/src/commands/agent.js +2 -12
  190. package/dist/src/commands/connect.js +7 -1
  191. package/dist/src/commands/doctor.js +62 -13
  192. package/dist/src/commands/github.js +144 -23
  193. package/dist/src/commands/inbox.js +62 -13
  194. package/dist/src/commands/init.js +82 -18
  195. package/dist/src/commands/inspect.js +62 -13
  196. package/dist/src/commands/run.js +100 -15
  197. package/dist/src/commands/server.js +71 -26
  198. package/dist/src/commands/setup.js +62 -13
  199. package/dist/src/commands/stats.js +157 -28
  200. package/dist/src/commands/task-run-driver.js +64 -25
  201. package/dist/src/commands/task.js +196 -43
  202. package/dist/src/commands.js +419 -123
  203. package/dist/src/index.js +426 -130
  204. package/package.json +11 -10
  205. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
  206. 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, getCapabilities } from "@earendil-works/pi-tui";
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 = 5;
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 DEFAULT_DRONE_LAYER_SCALE = 0.18;
24
- var DEFAULT_IMAGE_FPS = 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), 2.4);
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
- const cellW = size.width / layout.width;
224
- const cellH = size.height / layout.height;
225
- const leftCells = layout.centerLeft + (panel.left ?? 0);
226
- const topCells = layout.centerTop + panel.top;
227
- const widthCells = panel.width ?? layout.centerWidth;
228
- const heightCells = panel.height;
229
- const left = Math.round(leftCells * cellW);
230
- const top = Math.round(topCells * cellH);
231
- const width = Math.round(widthCells * cellW);
232
- const height = Math.round(heightCells * cellH);
233
- return width > 8 && height > 8 ? { left, top, width, height } : null;
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(14, Math.min(34, Math.round(Math.min(rect.width, rect.height) * 0.035)));
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 blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, 4);
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
- function visualSceneKind(scene) {
401
- if (scene === "tasks")
402
- return "loop";
403
- if (scene === "help")
404
- return "carrier";
405
- return "dispatch";
406
- }
407
- function seededUnit(seed) {
408
- let value = seed >>> 0;
409
- value = value * 1664525 + 1013904223 >>> 0;
410
- return value / 4294967295;
411
- }
412
- function sceneCore(width, height, kind) {
413
- if (kind === "dispatch")
414
- return { x: width * 0.3, y: height * 0.45 };
415
- if (kind === "carrier")
416
- return { x: width * 0.5, y: height * 0.54 };
417
- return { x: width * 0.09, y: height * 0.56 };
418
- }
419
- function dispatchLane(width, index, count) {
420
- const a = seededUnit(28672 + index * 101);
421
- const b = seededUnit(36864 + index * 131);
422
- const c = seededUnit(45056 + index * 151);
423
- const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
424
- return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
425
- }
426
- function lanePoint(width, height, lane, p, kind = "dispatch") {
427
- const core = sceneCore(width, height, kind);
428
- const dist = lane.len * p;
429
- const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
430
- const ux = Math.cos(lane.ang);
431
- const uy = Math.sin(lane.ang);
432
- return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
433
- }
434
- function drawMarketingRails(data, width, height, kind, tick) {
435
- const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
436
- ys.forEach((ratio, index) => {
437
- const y = height * ratio + Math.sin(tick * 0.018 + index) * Math.max(2, height * 0.0025);
438
- const color = index === 2 ? CYAN : index === 1 ? LIME : LIME_DIM;
439
- drawLine(data, width, height, width * 0.06, y, width * 0.94, y + Math.sin(index) * height * 0.002, color, index === 1 ? 0.11 : 0.07, Math.max(0.8, height * 0.0012));
440
- for (let p = (tick * 0.004 + index * 0.21) % 1;p < 1; p += 0.18 + index * 0.02) {
441
- const x = width * (0.06 + 0.88 * p);
442
- drawDisc(data, width, height, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.24);
443
- drawGlow(data, width, height, x, y, Math.max(5, height * 0.006), color, 0.035);
444
- }
445
- });
446
- }
447
- function drawDispatchScene(data, width, height, tick) {
448
- const core = sceneCore(width, height, "dispatch");
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
- 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;
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
- 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) {
619
+ }
620
+ function drawBackground(data, width, height, _tick, scene, layout, load, activeRuns) {
544
621
  fillRgba(data, BG, BG_WORD);
545
- const kind = visualSceneKind(scene);
546
- drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME, 0.035);
547
- drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN, 0.025);
548
- drawMarketingRails(data, width, height, kind, tick);
549
- if (kind === "loop")
550
- drawLoopScene(data, width, height, tick);
551
- else if (kind === "carrier")
552
- drawCarrierScene(data, width, height, tick);
553
- else
554
- drawDispatchScene(data, width, height, tick);
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
- return `${layer}:${width}x${height}:${input.layout.width}x${input.layout.height}:${input.scene}:${input.tick}:${input.panels.map((panel) => `${panel.id}:${panel.top}:${panel.height}:${panel.width ?? ""}:${panel.left ?? ""}:${panel.headerHeight ?? ""}`).join("|")}`;
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
- drawBackground(data, width, height, input.tick, input.scene);
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 (process.env.RIG_OPENTUI_IMAGE_WORKER === "0")
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
- panelSlot = this.createSlot();
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
- lastMotionFrameIndex = -1;
671
- lastPanelFrameIndex = -1;
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.protocol = getCapabilities().images;
677
- if (this.enabled) {
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 isModernImageTerminal(this.protocol);
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.resetSlot(this.panelSlot);
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, this.panelSlot.worker, this.chromeSlot.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 now = Date.now();
732
- const visualTick = Math.floor(now / (1000 / 60));
733
- const useDynamicSplit = Boolean(this.motionSlot.worker && this.panelSlot.worker && this.chromeSlot.worker && this.transport === "png" && process.env.RIG_OPENTUI_DYNAMIC_SPLIT !== "0");
734
- if (!useDynamicSplit) {
735
- const frameIndex = Math.floor(now / this.frameIntervalMs);
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 chrome = this.pendingFrames.get("panel-chrome");
868
+ const retainedSplit = this.pendingFrames.get("split");
765
869
  this.pendingFrames.clear();
766
- if (chrome)
767
- this.pendingFrames.set("panel-chrome", chrome);
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 === "panel-chrome")
773
- return 0;
774
- if (layer === "background")
876
+ if (layer === "split")
775
877
  return 1;
776
- if (layer === "panel-patches")
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
- if (candidateBytes <= this.outputBudgetBytes) {
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 (pendingLayerKey(frame.key) === "panel-chrome") {
795
- retained.set(pendingLayerKey(frame.key), frame);
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
- if (slot.worker) {
856
- this.enqueueWorkerRender(slot, request);
973
+ slot.latestRequestId = request.requestId;
974
+ if (!slot.worker) {
975
+ this.renderInline(slot, request);
857
976
  return;
858
977
  }
859
- this.renderSync(slot, request);
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", () => this.disableWorker(slot));
868
- worker.once("exit", () => this.disableWorker(slot));
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
- disableWorker(slot) {
871
- if (this.destroyed)
872
- return;
873
- slot.worker = null;
874
- slot.busy = false;
875
- slot.inFlightKey = "";
876
- const queued = slot.queuedRequest;
877
- slot.queuedRequest = null;
878
- slot.queuedKey = "";
879
- if (queued?.layer === "full")
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
- if (!slot.worker) {
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
- if (!slot.worker) {
896
- this.renderSync(slot, request);
897
- return;
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
- slot.worker.postMessage(request);
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
- if (response.requestId <= slot.clearedThroughRequestId)
910
- return;
911
- if (response.error) {
912
- this.disableWorker(slot);
913
- return;
914
- }
915
- if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "panel-chrome")) {
916
- this.stageFrame({ key: response.key, sequence: response.sequence });
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 isModernImageTerminal(getCapabilities().images);
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