@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
@@ -1,48 +1,156 @@
1
1
  // @bun
2
- var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true,
12
- configurable: true,
13
- set: __exportSetter.bind(all, name)
14
- });
15
- };
16
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
2
+ // packages/cli/src/app-opentui/render/image-visual-layer-worker.ts
3
+ import { parentPort } from "worker_threads";
17
4
 
18
5
  // packages/cli/src/app-opentui/render/image-visual-layer.ts
19
6
  import { Worker } from "worker_threads";
20
7
  import { deflateSync } from "zlib";
21
- import { allocateImageId, deleteKittyImage, getCapabilities } from "@earendil-works/pi-tui";
8
+ import { allocateImageId, deleteKittyImage } from "@earendil-works/pi-tui";
9
+
10
+ // packages/cli/src/app-opentui/terminal-capabilities.ts
11
+ import { getCapabilities } from "@earendil-works/pi-tui";
12
+ function getRigTerminalCapabilities() {
13
+ const imageProtocol = getCapabilities().images;
14
+ const supportsKittyImages = imageProtocol === "kitty";
15
+ return {
16
+ imageProtocol,
17
+ supportsKittyImages,
18
+ visualMode: supportsKittyImages ? "kitty-images" : "framebuffer-fallback"
19
+ };
20
+ }
21
+
22
+ // packages/cli/src/app-opentui/render/ascii-fleet.ts
23
+ var LEAD_DRONE = [
24
+ " .-=-. .-=-. ",
25
+ " ( !!! ) ( !!! ) ",
26
+ " '-=-'._ _.'-=-' ",
27
+ " '._ _.' ",
28
+ " '=$$$$$$$=.' ",
29
+ " =$$$$$$$$$$$= ",
30
+ " $$$@@@@@@@@@@$$$ ",
31
+ " $$$@@ @@$$$ ",
32
+ " $$@ ? @$$$ ",
33
+ " $$$@ '-' @$$$ ",
34
+ " $$$@@ @@$$$ ",
35
+ " $$$@@@@@@@@@@$$$ ",
36
+ " =$$$$$$$$$$$= ",
37
+ " '=$$$$$$$=.' ",
38
+ " _.' '._ ",
39
+ " .-=-.' '.-=-. ",
40
+ " ( !!! ) ( !!! ) ",
41
+ " '-=-' '-=-' "
42
+ ];
43
+ var MINI_DRONE = [
44
+ "(!!!) (!!!)",
45
+ " \\%==%/ ",
46
+ " %%?%% ",
47
+ " /%==%\\ ",
48
+ "(!!!) (!!!)"
49
+ ];
50
+ var ROTORS = ["---", "\\\\\\", "|||", "///"];
51
+ var FLEET_GRID_WIDTH = 52;
52
+ var FLEET_GRID_HEIGHT = 27;
53
+ var FLEET = [
54
+ { art: MINI_DRONE, x: 0, y: 2, amp: 1, speed: 0.05, phase: 0, dx: 1, driftSpeed: 0.02 },
55
+ { art: MINI_DRONE, x: 39, y: 2, amp: 1, speed: 0.044, phase: 2.1, dx: 1, driftSpeed: 0.017 },
56
+ { art: MINI_DRONE, x: 1, y: 20, amp: 1, speed: 0.048, phase: 4.2, dx: 1, driftSpeed: 0.023 },
57
+ { art: MINI_DRONE, x: 38, y: 20, amp: 1, speed: 0.041, phase: 1.3, dx: 1, driftSpeed: 0.015 },
58
+ { art: LEAD_DRONE, x: 10, y: 5, amp: 2, speed: 0.04, phase: 0, dx: 0, driftSpeed: 0.011 }
59
+ ];
60
+ function fleetRows(tick, options = {}) {
61
+ const animate = options.animate ?? true;
62
+ const t = animate ? tick : 0;
63
+ const blade = ROTORS[Math.floor(t / 4) % ROTORS.length];
64
+ const pulse = Math.sin(t * 0.07);
65
+ const eye = pulse > 0.45 ? "@" : pulse > -0.1 ? "o" : ".";
66
+ const grid = Array.from({ length: FLEET_GRID_HEIGHT }, () => Array.from({ length: FLEET_GRID_WIDTH }, () => " "));
67
+ for (const drone of FLEET) {
68
+ const yOffset = drone.y + Math.round(drone.amp * Math.sin(t * drone.speed + drone.phase));
69
+ const xOffset = drone.x + Math.round(drone.dx * Math.sin(t * drone.driftSpeed + drone.phase));
70
+ for (let row = 0;row < drone.art.length; row += 1) {
71
+ const source = drone.art[row];
72
+ const targetY = yOffset + row;
73
+ if (targetY < 0 || targetY >= FLEET_GRID_HEIGHT)
74
+ continue;
75
+ for (let col = 0;col < source.length; col += 1) {
76
+ let char = source[col];
77
+ if (char === " ")
78
+ continue;
79
+ const targetX = xOffset + col;
80
+ if (targetX < 0 || targetX >= FLEET_GRID_WIDTH)
81
+ continue;
82
+ if (source.slice(col, col + 3) === "!!!") {
83
+ for (let rotorIndex = 0;rotorIndex < 3; rotorIndex += 1) {
84
+ if (targetX + rotorIndex < FLEET_GRID_WIDTH)
85
+ grid[targetY][targetX + rotorIndex] = blade[rotorIndex];
86
+ }
87
+ col += 2;
88
+ continue;
89
+ }
90
+ if (char === "?")
91
+ char = eye;
92
+ grid[targetY][targetX] = char;
93
+ }
94
+ }
95
+ }
96
+ return grid.map((row) => row.join("").replace(/\s+$/, ""));
97
+ }
98
+
99
+ // packages/cli/src/app-opentui/render/panel-layout.ts
100
+ function panelPixelPlacement(layout, panel, size, minPixels = 8) {
101
+ const cellW = size.width / layout.width;
102
+ const cellH = size.height / layout.height;
103
+ const leftCells = layout.centerLeft + (panel.left ?? 0);
104
+ const topCells = layout.centerTop + panel.top;
105
+ const widthCells = panel.width ?? layout.centerWidth;
106
+ const heightCells = panel.height;
107
+ const left = Math.round(leftCells * cellW);
108
+ const top = Math.round(topCells * cellH);
109
+ const width = Math.round(widthCells * cellW);
110
+ const height = Math.round(heightCells * cellH);
111
+ return width > minPixels && height > minPixels ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
112
+ }
113
+
114
+ // packages/cli/src/app-opentui/render/constants.ts
115
+ var GLOW_FALLOFF_EXP_IMAGE = 2.4;
116
+
117
+ // packages/cli/src/app-opentui/render/image-visual-layer.ts
118
+ var PANEL_MASK_RADIUS_MIN = 14;
119
+ var PANEL_MASK_RADIUS_MAX = 34;
120
+ var PANEL_MASK_RADIUS_FACTOR = 0.035;
121
+ var PANEL_BLUR_RADIUS_MIN = 8;
122
+ var PANEL_BLUR_RADIUS_MAX = 28;
123
+ var PANEL_BLUR_RADIUS_FACTOR = 0.018;
124
+ var BG = [7, 8, 9, 255];
125
+ var PANEL = [16, 17, 21, 184];
126
+ var PANEL_HEADER = [16, 17, 21, 190];
127
+ var PANEL_LINE = [255, 255, 255, 24];
128
+ var LIME = [204, 255, 77, 255];
129
+ var LIME_DIM = [169, 214, 63, 255];
130
+ var CYAN = [86, 216, 255, 255];
131
+ var MAGENTA = [255, 121, 176, 255];
132
+ var INK_DIM = [108, 110, 121, 255];
133
+ var PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
134
+ var CRC_TABLE = makeCrcTable();
135
+ var MAX_IMAGE_BYTES = 48 * 1024 * 1024;
136
+ var KITTY_CHUNK_SIZE = 4096;
137
+ var PANEL_BLUR_SCALE = 1;
138
+ var PANEL_MASK_CACHE_LIMIT = 16;
139
+ var ZLIB_LEVEL = 1;
140
+ var LOAD_BUCKETS = 5;
141
+ function loadBucket(load) {
142
+ if (!Number.isFinite(load ?? NaN))
143
+ return 0;
144
+ return Math.max(0, Math.min(LOAD_BUCKETS, Math.round((load ?? 0) * LOAD_BUCKETS)));
145
+ }
146
+ var panelMaskCache = new Map;
147
+ var BG_WORD = rgbaWord(BG);
22
148
  function imageTransport() {
23
149
  const forced = process.env.RIG_OPENTUI_IMAGE_TRANSPORT;
24
150
  if (forced === "rgba-zlib")
25
151
  return "rgba-zlib";
26
152
  return "png";
27
153
  }
28
- function droneLayerScale() {
29
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_DRONE_SCALE ?? "");
30
- if (Number.isFinite(raw) && raw > 0)
31
- return Math.max(0.15, Math.min(1, raw));
32
- return DEFAULT_DRONE_LAYER_SCALE;
33
- }
34
- function scaledPixelSize(size, scale, layout) {
35
- return {
36
- width: Math.max(layout.width, Math.floor(size.width * scale)),
37
- height: Math.max(layout.height, Math.floor(size.height * scale))
38
- };
39
- }
40
- function imageFps() {
41
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_IMAGE_FPS ?? "");
42
- if (Number.isFinite(raw) && raw > 0)
43
- return Math.max(10, Math.min(60, raw));
44
- return DEFAULT_IMAGE_FPS;
45
- }
46
154
  function rgbaWord(color) {
47
155
  return (color[3] << 24 | color[2] << 16 | color[1] << 8 | color[0]) >>> 0;
48
156
  }
@@ -58,6 +166,121 @@ function fillRgba(data, color, word = rgbaWord(color)) {
58
166
  data[i + 3] = color[3];
59
167
  }
60
168
  }
169
+ var ASCII_GLYPHS = {
170
+ ".": ["00000", "00000", "00000", "00000", "00000", "00100", "00100"],
171
+ "'": ["00100", "00100", "00000", "00000", "00000", "00000", "00000"],
172
+ "-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
173
+ _: ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
174
+ "=": ["00000", "11111", "00000", "00000", "11111", "00000", "00000"],
175
+ "|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
176
+ "/": ["00001", "00010", "00010", "00100", "01000", "01000", "10000"],
177
+ "\\": ["10000", "01000", "01000", "00100", "00010", "00010", "00001"],
178
+ "(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
179
+ ")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
180
+ "!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
181
+ "%": ["11001", "11010", "00100", "01000", "10110", "00110", "00000"],
182
+ $: ["00100", "11110", "10100", "11110", "00101", "11110", "00100"],
183
+ "@": ["01110", "10001", "10111", "10101", "10111", "10000", "01111"],
184
+ o: ["00000", "01110", "10001", "10001", "10001", "01110", "00000"],
185
+ "*": ["00100", "10101", "01110", "11111", "01110", "10101", "00100"]
186
+ };
187
+ function sceneStaticTick(scene) {
188
+ let hash = 0;
189
+ for (let index = 0;index < scene.length; index += 1)
190
+ hash = hash * 33 + scene.charCodeAt(index) >>> 0;
191
+ return 11 + hash % 97;
192
+ }
193
+ function charColor(char, row, totalRows) {
194
+ if (char === "@" || char === "$" || char === "o")
195
+ return LIME;
196
+ if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
197
+ return CYAN;
198
+ if (char === "." || char === "'" || char === "_" || row < 3 || row > totalRows - 4)
199
+ return INK_DIM;
200
+ if (char === "-" || char === "=" || char === "(" || char === ")")
201
+ return LIME_DIM;
202
+ return MAGENTA;
203
+ }
204
+ function fillRectAlpha(data, width, height, left, top, rectWidth, rectHeight, color, alpha) {
205
+ const x0 = Math.max(0, Math.floor(left));
206
+ const y0 = Math.max(0, Math.floor(top));
207
+ const x1 = Math.min(width, Math.ceil(left + rectWidth));
208
+ const y1 = Math.min(height, Math.ceil(top + rectHeight));
209
+ for (let y = y0;y < y1; y += 1) {
210
+ for (let x = x0;x < x1; x += 1)
211
+ blendOver(data, (y * width + x) * 4, color, alpha);
212
+ }
213
+ }
214
+ function drawAsciiGlyph(data, width, height, x, y, cellWidth, cellHeight, char, color, alpha) {
215
+ const pattern = ASCII_GLYPHS[char] ?? ASCII_GLYPHS["*"];
216
+ const glyphWidth = Math.max(1, cellWidth * 0.56);
217
+ const glyphHeight = Math.max(1, cellHeight * 0.7);
218
+ const originX = x + (cellWidth - glyphWidth) * 0.5;
219
+ const originY = y + (cellHeight - glyphHeight) * 0.5;
220
+ const pixelWidth = Math.max(1, glyphWidth / pattern[0].length);
221
+ const pixelHeight = Math.max(1, glyphHeight / pattern.length);
222
+ for (let row = 0;row < pattern.length; row += 1) {
223
+ const bits = pattern[row];
224
+ for (let col = 0;col < bits.length; col += 1) {
225
+ if (bits[col] !== "1")
226
+ continue;
227
+ fillRectAlpha(data, width, height, originX + col * pixelWidth, originY + row * pixelHeight, pixelWidth * 0.72, pixelHeight * 0.72, color, alpha);
228
+ }
229
+ }
230
+ }
231
+ function drawStaticAsciiFleetBackground(data, width, height, scene, layout, load, activeRuns) {
232
+ const rows = fleetRows(sceneStaticTick(scene), { animate: true });
233
+ const cellWidth = width / Math.max(1, layout.width);
234
+ const cellHeight = height / Math.max(1, layout.height);
235
+ const sideBySide = layout.width >= FLEET_GRID_WIDTH * 1.9;
236
+ const leftCells = sideBySide ? Math.max(1, Math.floor(layout.width * 0.3 - FLEET_GRID_WIDTH / 2)) : Math.floor((layout.width - FLEET_GRID_WIDTH) / 2);
237
+ const topCells = Math.floor((layout.height - FLEET_GRID_HEIGHT) / 2);
238
+ const fleetLeft = leftCells * cellWidth;
239
+ const fleetTop = topCells * cellHeight;
240
+ drawGlow(data, width, height, width * 0.5, height * 0.5, Math.min(width, height) * 0.4, LIME, 0.022 + load * 0.03);
241
+ drawGlow(data, width, height, width * 0.18, height * 0.22, Math.min(width, height) * 0.28, CYAN, 0.018 + load * 0.012);
242
+ drawGlow(data, width, height, width * 0.82, height * 0.78, Math.min(width, height) * 0.22, MAGENTA, 0.012 + load * 0.008);
243
+ let droneTotal = 0;
244
+ for (const line of rows)
245
+ for (const char of line)
246
+ if (char === "@" || char === "$" || char === "%")
247
+ droneTotal += 1;
248
+ const litTarget = Math.max(0, Math.min(droneTotal, activeRuns));
249
+ let droneIndex = 0;
250
+ for (let row = 0;row < rows.length; row += 1) {
251
+ const line = rows[row];
252
+ for (let col = 0;col < line.length; col += 1) {
253
+ const char = line[col];
254
+ if (char === " ")
255
+ continue;
256
+ const x = fleetLeft + col * cellWidth;
257
+ const y = fleetTop + row * cellHeight;
258
+ const color = charColor(char, row, rows.length);
259
+ const isDrone = char === "@" || char === "$" || char === "%";
260
+ const lit = isDrone && droneIndex < litTarget;
261
+ drawAsciiGlyph(data, width, height, x + cellWidth * 0.07, y + cellHeight * 0.05, cellWidth, cellHeight, char, color, lit ? 0.72 : 0.46);
262
+ if (isDrone) {
263
+ droneIndex += 1;
264
+ drawGlow(data, width, height, x + cellWidth * 0.5, y + cellHeight * 0.5, Math.max(cellWidth, cellHeight) * 0.95, color, lit ? 0.044 : 0.014);
265
+ }
266
+ }
267
+ }
268
+ }
269
+ function applyStaticAtmosphere(data, width, height, load) {
270
+ const cx = width * 0.5;
271
+ const cy = height * 0.42;
272
+ const radius = Math.max(width, height) * 0.78;
273
+ const minVignette = 0.1 - load * 0.02;
274
+ for (let y = 0;y < height; y += 1) {
275
+ for (let x = 0;x < width; x += 1) {
276
+ const distance = Math.hypot(x - cx, y - cy) / radius;
277
+ const alpha = Math.max(minVignette, Math.min(0.82, 0.12 + Math.pow(distance, 1.65) * 0.62));
278
+ blendOver(data, (y * width + x) * 4, BG, alpha);
279
+ }
280
+ }
281
+ drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.2, LIME, 0.024 + load * 0.012);
282
+ drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.16, CYAN, 0.018 + load * 0.008);
283
+ }
61
284
  function makeCrcTable() {
62
285
  const table = new Uint32Array(256);
63
286
  for (let n = 0;n < 256; n += 1) {
@@ -128,9 +351,6 @@ function kittyImageSequence(base64, options) {
128
351
  }
129
352
  return chunks.join("");
130
353
  }
131
- function isModernImageTerminal(protocol) {
132
- return protocol === "kitty";
133
- }
134
354
  function clampByte(value) {
135
355
  return Math.max(0, Math.min(255, Math.round(value)));
136
356
  }
@@ -171,7 +391,7 @@ function drawGlow(data, width, height, cx, cy, radius, color, alpha = 0.18) {
171
391
  const d = Math.sqrt(dx * dx + dy * dy);
172
392
  if (d > radius)
173
393
  continue;
174
- const falloff = Math.pow(1 - d / Math.max(1, radius), 2.4);
394
+ const falloff = Math.pow(1 - d / Math.max(1, radius), GLOW_FALLOFF_EXP_IMAGE);
175
395
  blendOver(data, (y * width + x) * 4, color, alpha * falloff);
176
396
  }
177
397
  }
@@ -185,21 +405,6 @@ function drawLine(data, width, height, x0, y0, x1, y1, color, alpha = 0.5, thick
185
405
  drawDisc(data, width, height, x0 + dx * t, y0 + dy * t, thickness, color, alpha);
186
406
  }
187
407
  }
188
- function drawDrone(data, width, height, x, y, scale, tick, color) {
189
- const wobble = Math.sin(tick * 0.055) * scale * 0.35;
190
- const cy = y + wobble;
191
- drawGlow(data, width, height, x, cy, scale * 7, color, 0.18);
192
- drawDisc(data, width, height, x, cy, scale * 1.45, color, 0.92);
193
- drawDisc(data, width, height, x, cy, scale * 0.55, [235, 255, 176, 255], 0.85);
194
- const arm = scale * 4.1;
195
- const rotor = scale * 1.6;
196
- const arms = [[-arm, -arm * 0.72], [arm, -arm * 0.72], [-arm, arm * 0.72], [arm, arm * 0.72]];
197
- for (const [dx, dy] of arms) {
198
- drawLine(data, width, height, x, cy, x + dx, cy + dy, color, 0.42, Math.max(1, scale * 0.22));
199
- drawGlow(data, width, height, x + dx, cy + dy, rotor * 2.6, color, 0.1);
200
- drawDisc(data, width, height, x + dx, cy + dy, rotor, color, 0.34 + 0.12 * Math.sin(tick * 0.15));
201
- }
202
- }
203
408
  function roundRectAlpha(px, py, x, y, w, h, r) {
204
409
  const rx = Math.max(x + r, Math.min(px, x + w - r));
205
410
  const ry = Math.max(y + r, Math.min(py, y + h - r));
@@ -209,17 +414,7 @@ function roundRectAlpha(px, py, x, y, w, h, r) {
209
414
  return Math.max(0, Math.min(1, r + 0.75 - dist));
210
415
  }
211
416
  function panelRect(layout, panel, size) {
212
- const cellW = size.width / layout.width;
213
- const cellH = size.height / layout.height;
214
- const leftCells = layout.centerLeft + (panel.left ?? 0);
215
- const topCells = layout.centerTop + panel.top;
216
- const widthCells = panel.width ?? layout.centerWidth;
217
- const heightCells = panel.height;
218
- const left = Math.round(leftCells * cellW);
219
- const top = Math.round(topCells * cellH);
220
- const width = Math.round(widthCells * cellW);
221
- const height = Math.round(heightCells * cellH);
222
- return width > 8 && height > 8 ? { left, top, width, height } : null;
417
+ return panelPixelPlacement(layout, panel, size);
223
418
  }
224
419
  function downsampleRegion(data, canvasW, canvasH, rect, scale) {
225
420
  const w = Math.max(1, Math.ceil(rect.width / scale));
@@ -313,7 +508,7 @@ function getPanelMask(width, height, rect, headerPx, blurScale) {
313
508
  const cached = panelMaskCache.get(key);
314
509
  if (cached)
315
510
  return cached;
316
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(rect.width, rect.height) * 0.035)));
511
+ 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)));
317
512
  const sampledWidth = Math.max(1, Math.ceil(rect.width / blurScale));
318
513
  const sampledHeight = Math.max(1, Math.ceil(rect.height / blurScale));
319
514
  const x0 = Math.max(0, rect.left);
@@ -368,7 +563,8 @@ function getPanelMask(width, height, rect, headerPx, blurScale) {
368
563
  function compositePanel(data, width, height, rect, headerPx) {
369
564
  const blurScale = PANEL_BLUR_SCALE;
370
565
  const sampled = downsampleRegion(data, width, height, rect, blurScale);
371
- const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, 4);
566
+ 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)));
567
+ const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, blurRadius);
372
568
  const mask = getPanelMask(width, height, rect, headerPx, blurScale);
373
569
  for (let i = 0;i < mask.indices.length; i += 1) {
374
570
  const idx = mask.indices[i];
@@ -386,167 +582,49 @@ function compositePanel(data, width, height, rect, headerPx) {
386
582
  blendOver(data, mask.dividerIndices[i], PANEL_LINE, 0.28);
387
583
  }
388
584
  }
389
- function visualSceneKind(scene) {
390
- if (scene === "tasks")
391
- return "loop";
392
- if (scene === "help")
393
- return "carrier";
394
- return "dispatch";
395
- }
396
- function seededUnit(seed) {
397
- let value = seed >>> 0;
398
- value = value * 1664525 + 1013904223 >>> 0;
399
- return value / 4294967295;
400
- }
401
- function sceneCore(width, height, kind) {
402
- if (kind === "dispatch")
403
- return { x: width * 0.3, y: height * 0.45 };
404
- if (kind === "carrier")
405
- return { x: width * 0.5, y: height * 0.54 };
406
- return { x: width * 0.09, y: height * 0.56 };
407
- }
408
- function dispatchLane(width, index, count) {
409
- const a = seededUnit(28672 + index * 101);
410
- const b = seededUnit(36864 + index * 131);
411
- const c = seededUnit(45056 + index * 151);
412
- const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
413
- return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
414
- }
415
- function lanePoint(width, height, lane, p, kind = "dispatch") {
416
- const core = sceneCore(width, height, kind);
417
- const dist = lane.len * p;
418
- const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
419
- const ux = Math.cos(lane.ang);
420
- const uy = Math.sin(lane.ang);
421
- return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
422
- }
423
- function drawMarketingRails(data, width, height, kind, tick) {
424
- const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
425
- ys.forEach((ratio, index) => {
426
- const y = height * ratio + Math.sin(tick * 0.018 + index) * Math.max(2, height * 0.0025);
427
- const color = index === 2 ? CYAN : index === 1 ? LIME : LIME_DIM;
428
- drawLine(data, width, height, width * 0.06, y, width * 0.94, y + Math.sin(index) * height * 0.002, color, index === 1 ? 0.11 : 0.07, Math.max(0.8, height * 0.0012));
429
- for (let p = (tick * 0.004 + index * 0.21) % 1;p < 1; p += 0.18 + index * 0.02) {
430
- const x = width * (0.06 + 0.88 * p);
431
- drawDisc(data, width, height, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.24);
432
- drawGlow(data, width, height, x, y, Math.max(5, height * 0.006), color, 0.035);
433
- }
434
- });
435
- }
436
- function drawDispatchScene(data, width, height, tick) {
437
- const core = sceneCore(width, height, "dispatch");
585
+ var RIG_GLYPH_STROKES = {
586
+ 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]],
587
+ I: [[0.5, 0, 0.5, 1], [0.18, 0, 0.82, 0], [0.18, 1, 0.82, 1]],
588
+ 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]]
589
+ };
590
+ function drawBigRigWordmark(data, width, height, layout, load) {
591
+ const sideBySide = layout.width >= FLEET_GRID_WIDTH * 1.9;
592
+ if (!sideBySide)
593
+ return;
594
+ const letters = "RIG";
438
595
  const min = Math.min(width, height);
439
- drawGlow(data, width, height, core.x, core.y, min * 0.085, LIME, 0.12);
440
- drawDisc(data, width, height, core.x, core.y, min * 0.0065, LIME, 0.85);
441
- const laneCount = width < 1000 ? 7 : 12;
442
- const lanes = Array.from({ length: laneCount }, (_, index) => dispatchLane(width, index, laneCount));
443
- lanes.forEach((lane) => {
444
- let prev = lanePoint(width, height, lane, 0);
445
- for (let step = 1;step <= 32; step += 1) {
446
- const next = lanePoint(width, height, lane, step / 32);
447
- drawLine(data, width, height, prev.x, prev.y, next.x, next.y, lane.phase > Math.PI ? CYAN : LIME_DIM, 0.045, Math.max(0.7, min * 0.0009));
448
- prev = next;
596
+ const glyphH = height * 0.42;
597
+ const glyphW = width * 0.13;
598
+ const gap = glyphW * 0.3;
599
+ const totalW = letters.length * glyphW + (letters.length - 1) * gap;
600
+ const centerX = width * 0.71;
601
+ const startX = centerX - totalW / 2;
602
+ const top = (height - glyphH) / 2;
603
+ const stroke = Math.max(2, min * 0.013);
604
+ drawGlow(data, width, height, centerX, top + glyphH / 2, Math.max(glyphW, glyphH) * 0.9, LIME, 0.022 + load * 0.02);
605
+ const strokeAlpha = 0.1 + load * 0.06;
606
+ letters.split("").forEach((letter, index) => {
607
+ const segments = RIG_GLYPH_STROKES[letter];
608
+ if (!segments)
609
+ return;
610
+ const ox = startX + index * (glyphW + gap);
611
+ for (const [x0, y0, x1, y1] of segments) {
612
+ drawLine(data, width, height, ox + x0 * glyphW, top + y0 * glyphH, ox + x1 * glyphW, top + y1 * glyphH, LIME_DIM, strokeAlpha, stroke);
449
613
  }
450
614
  });
451
- const packets = width < 1000 ? 70 : 150;
452
- for (let i = 0;i < packets; i += 1) {
453
- const lane = lanes[i % lanes.length];
454
- const speed = 0.003 + seededUnit(12288 + i * 17) * 0.009;
455
- const p = (seededUnit(16384 + i * 19) + tick * speed) % 1;
456
- const pt = lanePoint(width, height, lane, p);
457
- const wobble = (seededUnit(20480 + i * 23) - 0.5) * min * 0.012;
458
- const fade = p < 0.1 ? p / 0.1 : p > 0.88 ? (1 - p) / 0.12 : 1;
459
- const color = seededUnit(24576 + i * 29) < 0.22 ? CYAN : LIME_DIM;
460
- const y = pt.y + wobble * Math.sin(p * Math.PI * 2);
461
- drawLine(data, width, height, pt.x - min * 0.012, y - min * 0.002, pt.x, y, color, 0.08 * fade, Math.max(0.7, min * 0.0008));
462
- drawDisc(data, width, height, pt.x, y, Math.max(1.3, min * 0.0022), color, 0.5 * fade);
463
- drawGlow(data, width, height, pt.x, y, Math.max(4, min * 0.008), color, 0.045 * fade);
464
- }
465
- for (let i = 0;i < (width < 1000 ? 4 : 8); i += 1) {
466
- const lane = lanes[i % lanes.length];
467
- const p = (tick * 0.002 + i * 0.13) % 1;
468
- const pt = lanePoint(width, height, lane, p);
469
- drawDrone(data, width, height, pt.x, pt.y + Math.sin(tick * 0.04 + i) * min * 0.004, min * (0.006 + i % 2 * 0.0015), tick + i * 11, i % 3 === 0 ? CYAN : LIME_DIM);
470
- }
471
- }
472
- function drawLoopScene(data, width, height, tick) {
473
- const min = Math.min(width, height);
474
- const y = height * 0.56;
475
- const x0 = width * 0.09;
476
- const x1 = width * 0.91;
477
- drawLine(data, width, height, x0, y, x1, y, LIME_DIM, 0.12, Math.max(0.8, min * 0.001));
478
- const gates = width < 1000 ? 4 : 5;
479
- const active = Math.floor(tick / 16) % gates;
480
- for (let i = 0;i < gates; i += 1) {
481
- const x = x0 + (x1 - x0) * (i / Math.max(1, gates - 1));
482
- const color = i === active ? LIME : i <= active ? LIME_DIM : INK_DIM;
483
- drawLine(data, width, height, x, y - min * 0.05, x, y + min * 0.05, color, i === active ? 0.25 : 0.08, Math.max(0.8, min * 0.001));
484
- drawDisc(data, width, height, x, y, i === active ? min * 0.006 : min * 0.0035, color, i === active ? 0.72 : 0.25);
485
- drawGlow(data, width, height, x, y, i === active ? min * 0.018 : min * 0.01, color, i === active ? 0.1 : 0.035);
486
- }
487
- const packets = width < 1000 ? 36 : 84;
488
- for (let i = 0;i < packets; i += 1) {
489
- const p = (seededUnit(33024 + i * 13) + tick * (0.0022 + seededUnit(33280 + i * 17) * 0.004)) % 1;
490
- const x = x0 + (x1 - x0) * p;
491
- const segment = Math.min(gates - 1, Math.floor(p * gates));
492
- const color = segment < active ? LIME_DIM : segment === active ? CYAN : INK_DIM;
493
- const py = y + (seededUnit(33536 + i * 19) - 0.5) * min * 0.018 * Math.sin(p * Math.PI * 2);
494
- drawLine(data, width, height, x - min * 0.014, py, x, py, color, 0.1, Math.max(0.7, min * 0.0008));
495
- drawDisc(data, width, height, x, py, Math.max(1.2, min * 0.002), color, color === INK_DIM ? 0.22 : 0.58);
496
- }
497
- drawDrone(data, width, height, x0 + (x1 - x0) * (tick * 0.003 % 1), y - min * 0.04, min * 0.0065, tick, LIME_DIM);
498
- drawDrone(data, width, height, x0 + (x1 - x0) * ((tick * 0.002 + 0.5) % 1), y + min * 0.036, min * 0.0055, tick + 40, MAGENTA);
499
615
  }
500
- function drawCarrierScene(data, width, height, tick) {
501
- const min = Math.min(width, height);
502
- const core = sceneCore(width, height, "carrier");
503
- drawGlow(data, width, height, core.x, core.y, min * 0.075, LIME, 0.12);
504
- drawDisc(data, width, height, core.x, core.y, min * 0.0065, LIME, 0.92);
505
- const nodes = width < 1000 ? 5 : 7;
506
- const rx = width * 0.36;
507
- const ry = height * 0.22;
508
- for (let i = 0;i < nodes; i += 1) {
509
- const a = i / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006;
510
- const x = core.x + Math.cos(a) * rx;
511
- const y = core.y + Math.sin(a) * ry;
512
- const color = i % 2 ? CYAN : LIME_DIM;
513
- drawLine(data, width, height, core.x, core.y, x, y, color, 0.055, Math.max(0.7, min * 0.0008));
514
- drawGlow(data, width, height, x, y, min * 0.018, color, 0.055);
515
- drawDisc(data, width, height, x, y, min * 0.0048, color, 0.62);
516
- drawDrone(data, width, height, x, y - min * 0.018, min * 0.005, tick + i * 9, color);
517
- }
518
- const packets = width < 1000 ? 30 : 72;
519
- for (let i = 0;i < packets; i += 1) {
520
- const lane = i % nodes;
521
- const a = lane / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006;
522
- const nx = core.x + Math.cos(a) * rx;
523
- const ny = core.y + Math.sin(a) * ry;
524
- const p = (seededUnit(37120 + i * 37) + tick * (0.003 + seededUnit(37376 + i * 43) * 0.009)) % 1;
525
- const x = core.x + (nx - core.x) * p;
526
- const y = core.y + (ny - core.y) * p;
527
- const color = seededUnit(37632 + i * 47) < 0.35 ? CYAN : LIME_DIM;
528
- drawDisc(data, width, height, x, y, Math.max(1.2, min * 0.002), color, 0.48);
529
- drawGlow(data, width, height, x, y, min * 0.008, color, 0.035);
530
- }
531
- }
532
- function drawBackground(data, width, height, tick, scene) {
616
+ function drawBackground(data, width, height, _tick, scene, layout, load, activeRuns) {
533
617
  fillRgba(data, BG, BG_WORD);
534
- const kind = visualSceneKind(scene);
535
- drawGlow(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME, 0.035);
536
- drawGlow(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN, 0.025);
537
- drawMarketingRails(data, width, height, kind, tick);
538
- if (kind === "loop")
539
- drawLoopScene(data, width, height, tick);
540
- else if (kind === "carrier")
541
- drawCarrierScene(data, width, height, tick);
542
- else
543
- drawDispatchScene(data, width, height, tick);
618
+ drawStaticAsciiFleetBackground(data, width, height, scene, layout, load, activeRuns);
619
+ drawBigRigWordmark(data, width, height, layout, load);
620
+ applyStaticAtmosphere(data, width, height, load);
544
621
  }
545
622
  function imageVisualFrameKey(input) {
546
623
  const width = Math.max(1, Math.floor(input.pixelSize.width));
547
624
  const height = Math.max(1, Math.floor(input.pixelSize.height));
548
625
  const layer = "layer" in input ? input.layer ?? "full" : "full";
549
- 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("|")}`;
626
+ const load = loadBucket(input.load);
627
+ 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("|")}`;
550
628
  }
551
629
  function encodeKittyFrame(width, height, data, input) {
552
630
  if (input.transport === "png") {
@@ -565,14 +643,78 @@ function encodeKittyFrame(width, height, data, input) {
565
643
  compressed: true
566
644
  });
567
645
  }
646
+ function cropRgba(data, sourceWidth, rect) {
647
+ const out = new Uint8ClampedArray(rect.width * rect.height * 4);
648
+ for (let y = 0;y < rect.height; y += 1) {
649
+ const sourceStart = ((rect.top + y) * sourceWidth + rect.left) * 4;
650
+ out.set(data.subarray(sourceStart, sourceStart + rect.width * 4), y * rect.width * 4);
651
+ }
652
+ return out;
653
+ }
654
+ function encodePanelPatch(data, sourceWidth, placement, imageId, zIndex) {
655
+ const patch = cropRgba(data, sourceWidth, placement);
656
+ const png = encodePng(placement.width, placement.height, patch);
657
+ const sequence = kittyImageSequence(png.toString("base64"), {
658
+ columns: Math.max(1, Math.round(placement.widthCells)),
659
+ rows: Math.max(1, Math.round(placement.heightCells)),
660
+ imageId,
661
+ zIndex,
662
+ format: 100
663
+ });
664
+ return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
665
+ }
568
666
  function renderImageVisualFrame(input) {
569
667
  const width = Math.max(1, Math.floor(input.pixelSize.width));
570
668
  const height = Math.max(1, Math.floor(input.pixelSize.height));
571
669
  const byteCount = width * height * 4;
572
670
  if (byteCount > MAX_IMAGE_BYTES)
573
671
  return null;
672
+ const layer = input.layer ?? "full";
574
673
  const data = new Uint8ClampedArray(byteCount);
575
- drawBackground(data, width, height, input.tick, input.scene);
674
+ const load = loadBucket(input.load) / LOAD_BUCKETS;
675
+ const activeRuns = Math.max(0, Math.floor(input.activeRuns ?? 0));
676
+ drawBackground(data, width, height, 0, input.scene, input.layout, load, activeRuns);
677
+ if (layer === "split") {
678
+ const sequences = [encodeKittyFrame(width, height, data, input)];
679
+ let panelIndex = 0;
680
+ for (const panel of input.panels) {
681
+ if (panel.chrome !== "ad-terminal")
682
+ continue;
683
+ const placement = panelPixelPlacement(input.layout, panel, { width, height });
684
+ if (!placement)
685
+ continue;
686
+ const cellH = height / input.layout.height;
687
+ const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
688
+ compositePanel(data, width, height, placement, headerPx);
689
+ const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
690
+ sequences.push(encodePanelPatch(data, width, placement, imageId, -12));
691
+ panelIndex += 1;
692
+ }
693
+ return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
694
+ }
695
+ if (layer === "panel-chrome")
696
+ return { key: imageVisualFrameKey(input), sequence: "" };
697
+ if (layer === "background") {
698
+ return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
699
+ }
700
+ if (layer === "panel-patches") {
701
+ const sequences = [];
702
+ let panelIndex = 0;
703
+ for (const panel of input.panels) {
704
+ if (panel.chrome !== "ad-terminal")
705
+ continue;
706
+ const placement = panelPixelPlacement(input.layout, panel, { width, height });
707
+ if (!placement)
708
+ continue;
709
+ const cellH = height / input.layout.height;
710
+ const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
711
+ compositePanel(data, width, height, placement, headerPx);
712
+ const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
713
+ sequences.push(encodePanelPatch(data, width, placement, imageId, input.zIndex));
714
+ panelIndex += 1;
715
+ }
716
+ return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
717
+ }
576
718
  for (const panel of input.panels) {
577
719
  if (panel.chrome !== "ad-terminal")
578
720
  continue;
@@ -585,8 +727,11 @@ function renderImageVisualFrame(input) {
585
727
  }
586
728
  return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
587
729
  }
730
+ function shouldRenderImagesInline() {
731
+ return process.env.RIG_OPENTUI_IMAGE_WORKER === "0" || import.meta.url.includes("$bunfs");
732
+ }
588
733
  function createImageWorker() {
589
- if (process.env.RIG_OPENTUI_IMAGE_WORKER === "0")
734
+ if (shouldRenderImagesInline())
590
735
  return null;
591
736
  const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
592
737
  const workerUrl = new URL(isTypeScriptRuntime ? "./image-visual-layer-worker.ts" : "./image-visual-layer-worker.js", import.meta.url);
@@ -601,18 +746,9 @@ function createImageWorker() {
601
746
  return null;
602
747
  }
603
748
  }
604
- function panelFps() {
605
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_PANEL_FPS ?? "");
606
- if (Number.isFinite(raw) && raw > 0)
607
- return Math.max(4, Math.min(60, raw));
608
- return DEFAULT_PANEL_FPS;
609
- }
610
- function panelLayerScale() {
611
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_PANEL_SCALE ?? "");
612
- if (Number.isFinite(raw) && raw > 0)
613
- return Math.max(0.18, Math.min(1, raw));
614
- return DEFAULT_PANEL_LAYER_SCALE;
615
- }
749
+ var RECENT_WRITTEN_LIMIT = 24;
750
+ var DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
751
+ var DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
616
752
  function imageOutputBacklogBytes() {
617
753
  const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BACKLOG_BYTES ?? "", 10);
618
754
  if (Number.isFinite(raw) && raw > 0)
@@ -634,36 +770,30 @@ class ImageVisualLayer {
634
770
  panelImageIds = Array.from({ length: 8 }, () => allocateImageId());
635
771
  panelChromeImageIds = Array.from({ length: 8 }, () => allocateImageId());
636
772
  stdout;
773
+ onFrameReady;
637
774
  protocol;
638
775
  transport = imageTransport();
639
- frameIntervalMs = 1000 / imageFps();
640
- panelIntervalMs = 1000 / panelFps();
641
776
  maxOutputBacklogBytes = imageOutputBacklogBytes();
642
777
  outputBytesPerSecond = imageOutputBytesPerSecond();
643
778
  outputBudgetBytes = imageOutputBytesPerSecond();
644
779
  lastBudgetAtMs = Date.now();
645
780
  motionSlot = this.createSlot();
646
- panelSlot = this.createSlot();
647
- chromeSlot = this.createSlot();
781
+ workersStarted = false;
648
782
  pendingFrames = new Map;
649
783
  recentWrittenKeys = [];
650
784
  imagesVisible = false;
785
+ flushPaused = false;
651
786
  requestId = 0;
652
- lastMotionFrameIndex = -1;
653
- lastPanelFrameIndex = -1;
654
- lastChromeKey = "";
787
+ lastBackgroundKey = "";
788
+ fatalWorkerError = null;
655
789
  destroyed = false;
656
- constructor(stdout = process.stdout) {
790
+ constructor(stdout = process.stdout, onFrameReady) {
657
791
  this.stdout = stdout;
658
- this.protocol = getCapabilities().images;
659
- if (this.enabled) {
660
- this.startWorker(this.motionSlot);
661
- this.startWorker(this.panelSlot);
662
- this.startWorker(this.chromeSlot);
663
- }
792
+ this.onFrameReady = onFrameReady;
793
+ this.protocol = getRigTerminalCapabilities().imageProtocol;
664
794
  }
665
795
  get enabled() {
666
- return isModernImageTerminal(this.protocol);
796
+ return this.protocol === "kitty";
667
797
  }
668
798
  clear() {
669
799
  if (this.enabled) {
@@ -673,16 +803,10 @@ class ImageVisualLayer {
673
803
  this.pendingFrames.clear();
674
804
  this.recentWrittenKeys.length = 0;
675
805
  this.resetSlot(this.motionSlot);
676
- this.resetSlot(this.panelSlot);
677
- this.resetSlot(this.chromeSlot);
678
- this.lastMotionFrameIndex = -1;
679
- this.lastPanelFrameIndex = -1;
680
- this.lastChromeKey = "";
806
+ this.lastBackgroundKey = "";
681
807
  this.outputBudgetBytes = this.outputBytesPerSecond;
682
808
  this.lastBudgetAtMs = Date.now();
683
809
  this.motionSlot.clearedThroughRequestId = this.requestId;
684
- this.panelSlot.clearedThroughRequestId = this.requestId;
685
- this.chromeSlot.clearedThroughRequestId = this.requestId;
686
810
  }
687
811
  }
688
812
  destroy() {
@@ -690,19 +814,31 @@ class ImageVisualLayer {
690
814
  return;
691
815
  this.clear();
692
816
  this.destroyed = true;
693
- const workers = [this.motionSlot.worker, this.panelSlot.worker, this.chromeSlot.worker];
817
+ const workers = [this.motionSlot.worker];
694
818
  this.motionSlot.worker = null;
695
- this.panelSlot.worker = null;
696
- this.chromeSlot.worker = null;
697
819
  for (const worker of workers)
698
820
  if (worker)
699
821
  worker.terminate().catch(() => {
700
822
  return;
701
823
  });
702
824
  }
825
+ setFlushPaused(paused) {
826
+ this.flushPaused = paused;
827
+ }
828
+ isReady(input) {
829
+ if (!this.enabled || this.destroyed)
830
+ return true;
831
+ this.throwIfFatal();
832
+ const splitKey = imageVisualFrameKey({ ...input, tick: 0, layer: "split" });
833
+ return this.recentWrittenKeys.includes(splitKey) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === splitKey);
834
+ }
703
835
  render(input) {
704
836
  if (!this.enabled || this.destroyed)
705
837
  return;
838
+ this.throwIfFatal();
839
+ if (this.transport !== "png")
840
+ this.failWorker("renderer", new Error("OpenTUI image visuals require PNG transport for static-background rendering."));
841
+ this.ensureWorkersStarted();
706
842
  const width = Math.max(1, Math.floor(input.pixelSize.width));
707
843
  const height = Math.max(1, Math.floor(input.pixelSize.height));
708
844
  if (width * height * 4 > MAX_IMAGE_BYTES)
@@ -710,54 +846,32 @@ class ImageVisualLayer {
710
846
  if (this.isOutputBackedUp())
711
847
  return;
712
848
  this.refillOutputBudget();
713
- const now = Date.now();
714
- const visualTick = Math.floor(now / (1000 / 60));
715
- const useDynamicSplit = Boolean(this.motionSlot.worker && this.panelSlot.worker && this.chromeSlot.worker && this.transport === "png" && process.env.RIG_OPENTUI_DYNAMIC_SPLIT !== "0");
716
- if (!useDynamicSplit) {
717
- const frameIndex = Math.floor(now / this.frameIntervalMs);
718
- if (!input.force && frameIndex === this.lastMotionFrameIndex)
719
- return;
720
- this.scheduleRender(this.motionSlot, { ...input, tick: visualTick, layer: "full" }, this.imageId, -10, input.force);
721
- this.lastMotionFrameIndex = frameIndex;
722
- return;
723
- }
724
- const motionFrameIndex = Math.floor(now / this.frameIntervalMs);
725
- if ((input.force || motionFrameIndex !== this.lastMotionFrameIndex) && this.outputBudgetBytes >= MIN_BACKGROUND_BUDGET_BYTES) {
726
- this.scheduleRender(this.motionSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, droneLayerScale(), input.layout), panels: [], tick: visualTick, layer: "background" }, this.imageId, -30, input.force);
727
- this.lastMotionFrameIndex = motionFrameIndex;
728
- }
729
- const panelFrameIndex = Math.floor(now / this.panelIntervalMs);
730
- if ((input.force || panelFrameIndex !== this.lastPanelFrameIndex) && this.outputBudgetBytes >= MIN_PANEL_BUDGET_BYTES) {
731
- this.scheduleRender(this.panelSlot, { ...input, pixelSize: scaledPixelSize(input.pixelSize, panelLayerScale(), input.layout), tick: visualTick, layer: "panel-patches" }, this.imageId, -12, input.force, this.panelImageIds);
732
- this.lastPanelFrameIndex = panelFrameIndex;
733
- }
734
- const chromeInput = { ...input, tick: 0, layer: "panel-chrome" };
735
- const chromeKey = imageVisualFrameKey(chromeInput);
736
- if (input.force || chromeKey !== this.lastChromeKey) {
737
- this.scheduleRender(this.chromeSlot, chromeInput, this.imageId, -8, input.force, this.panelChromeImageIds);
738
- this.lastChromeKey = chromeKey;
849
+ const splitInput = { ...input, tick: 0, layer: "split" };
850
+ const splitKey = imageVisualFrameKey(splitInput);
851
+ if (input.force || splitKey !== this.lastBackgroundKey) {
852
+ this.scheduleRender(this.motionSlot, splitInput, this.imageId, -30, input.force, this.panelImageIds);
853
+ this.lastBackgroundKey = splitKey;
739
854
  }
740
855
  }
741
856
  flush() {
857
+ this.throwIfFatal();
858
+ if (this.flushPaused)
859
+ return false;
742
860
  if (this.pendingFrames.size === 0)
743
861
  return false;
744
862
  this.refillOutputBudget();
745
863
  if (this.isOutputBackedUp()) {
746
- const chrome = this.pendingFrames.get("panel-chrome");
864
+ const retainedSplit = this.pendingFrames.get("split");
747
865
  this.pendingFrames.clear();
748
- if (chrome)
749
- this.pendingFrames.set("panel-chrome", chrome);
866
+ if (retainedSplit)
867
+ this.pendingFrames.set("split", retainedSplit);
750
868
  return false;
751
869
  }
752
870
  const framePriority = (frame) => {
753
871
  const layer = pendingLayerKey(frame.key);
754
- if (layer === "panel-chrome")
755
- return 0;
756
- if (layer === "background")
872
+ if (layer === "split")
757
873
  return 1;
758
- if (layer === "panel-patches")
759
- return 2;
760
- return 2;
874
+ return 4;
761
875
  };
762
876
  const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
763
877
  const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
@@ -769,12 +883,14 @@ class ImageVisualLayer {
769
883
  for (const frame of frames) {
770
884
  const candidate = sequence + frame.sequence;
771
885
  const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
772
- if (candidateBytes <= this.outputBudgetBytes) {
886
+ const layer = pendingLayerKey(frame.key);
887
+ const allowOneStaticFrame = selected.length === 0 && layer === "split";
888
+ if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
773
889
  selected.push(frame);
774
890
  sequence = candidate;
775
891
  byteLength = candidateBytes;
776
- } else if (pendingLayerKey(frame.key) === "panel-chrome") {
777
- retained.set(pendingLayerKey(frame.key), frame);
892
+ } else if (layer === "split") {
893
+ retained.set(layer, frame);
778
894
  }
779
895
  }
780
896
  this.pendingFrames.clear();
@@ -793,7 +909,7 @@ class ImageVisualLayer {
793
909
  return accepted;
794
910
  }
795
911
  createSlot() {
796
- return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0 };
912
+ return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0, latestRequestId: 0 };
797
913
  }
798
914
  resetSlot(slot) {
799
915
  slot.busy = false;
@@ -821,7 +937,23 @@ class ImageVisualLayer {
821
937
  isKnownKey(slot, key) {
822
938
  return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
823
939
  }
940
+ ensureWorkersStarted() {
941
+ if (this.workersStarted)
942
+ return;
943
+ this.workersStarted = true;
944
+ this.startWorker(this.motionSlot, "static-visuals");
945
+ }
946
+ failWorker(name, error) {
947
+ const detail = error instanceof Error ? error.message : String(error);
948
+ this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${detail}`);
949
+ throw this.fatalWorkerError;
950
+ }
951
+ throwIfFatal() {
952
+ if (this.fatalWorkerError)
953
+ throw this.fatalWorkerError;
954
+ }
824
955
  scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
956
+ this.throwIfFatal();
825
957
  const key = imageVisualFrameKey(input);
826
958
  if (!force && this.isKnownKey(slot, key))
827
959
  return;
@@ -834,38 +966,41 @@ class ImageVisualLayer {
834
966
  requestId: ++this.requestId,
835
967
  key
836
968
  };
837
- if (slot.worker) {
838
- this.enqueueWorkerRender(slot, request);
969
+ slot.latestRequestId = request.requestId;
970
+ if (!slot.worker) {
971
+ this.renderInline(slot, request);
839
972
  return;
840
973
  }
841
- this.renderSync(slot, request);
974
+ this.enqueueWorkerRender(slot, request);
842
975
  }
843
- startWorker(slot) {
976
+ startWorker(slot, name) {
844
977
  const worker = createImageWorker();
845
978
  if (!worker)
846
979
  return;
847
980
  slot.worker = worker;
848
981
  worker.on("message", (message) => this.handleWorkerMessage(slot, message));
849
- worker.once("error", () => this.disableWorker(slot));
850
- worker.once("exit", () => this.disableWorker(slot));
982
+ worker.once("error", (error) => {
983
+ if (!this.destroyed)
984
+ this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${error instanceof Error ? error.message : String(error)}`);
985
+ });
986
+ worker.once("exit", (code) => {
987
+ if (!this.destroyed)
988
+ this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} exited unexpectedly with code ${code}.`);
989
+ });
851
990
  }
852
- disableWorker(slot) {
853
- if (this.destroyed)
854
- return;
855
- slot.worker = null;
856
- slot.busy = false;
857
- slot.inFlightKey = "";
858
- const queued = slot.queuedRequest;
859
- slot.queuedRequest = null;
860
- slot.queuedKey = "";
861
- if (queued?.layer === "full")
862
- this.renderSync(slot, queued);
991
+ renderInline(slot, request) {
992
+ try {
993
+ const result = renderImageVisualFrame(request);
994
+ if (result?.sequence && !this.isKnownKey(slot, result.key) && (!this.isOutputBackedUp() || pendingLayerKey(result.key) === "split")) {
995
+ this.stageFrame({ key: result.key, sequence: result.sequence });
996
+ this.onFrameReady?.();
997
+ }
998
+ } catch (error) {
999
+ this.failWorker(String(request.layer ?? "visual"), error);
1000
+ }
863
1001
  }
864
1002
  enqueueWorkerRender(slot, request) {
865
- if (!slot.worker) {
866
- this.renderSync(slot, request);
867
- return;
868
- }
1003
+ this.throwIfFatal();
869
1004
  if (slot.busy) {
870
1005
  slot.queuedRequest = request;
871
1006
  slot.queuedKey = request.key;
@@ -874,13 +1009,16 @@ class ImageVisualLayer {
874
1009
  this.dispatchWorkerRender(slot, request);
875
1010
  }
876
1011
  dispatchWorkerRender(slot, request) {
877
- if (!slot.worker) {
878
- this.renderSync(slot, request);
879
- return;
880
- }
1012
+ this.throwIfFatal();
1013
+ if (!slot.worker)
1014
+ this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
881
1015
  slot.busy = true;
882
1016
  slot.inFlightKey = request.key;
883
- slot.worker.postMessage(request);
1017
+ try {
1018
+ slot.worker.postMessage(request);
1019
+ } catch (error) {
1020
+ this.failWorker(String(request.layer ?? "visual"), error);
1021
+ }
884
1022
  }
885
1023
  handleWorkerMessage(slot, message) {
886
1024
  if (this.destroyed || !message || typeof message !== "object")
@@ -888,14 +1026,14 @@ class ImageVisualLayer {
888
1026
  const response = message;
889
1027
  slot.busy = false;
890
1028
  slot.inFlightKey = "";
891
- if (response.requestId <= slot.clearedThroughRequestId)
892
- return;
893
- if (response.error) {
894
- this.disableWorker(slot);
895
- return;
896
- }
897
- if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "panel-chrome")) {
898
- this.stageFrame({ key: response.key, sequence: response.sequence });
1029
+ const isStale = response.requestId <= slot.clearedThroughRequestId || response.requestId < slot.latestRequestId;
1030
+ if (!isStale) {
1031
+ if (response.error)
1032
+ this.failWorker(pendingLayerKey(response.key), new Error(response.error));
1033
+ if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "split")) {
1034
+ this.stageFrame({ key: response.key, sequence: response.sequence });
1035
+ this.onFrameReady?.();
1036
+ }
899
1037
  }
900
1038
  const next = slot.queuedRequest;
901
1039
  slot.queuedRequest = null;
@@ -903,611 +1041,12 @@ class ImageVisualLayer {
903
1041
  if (next && !this.isKnownKey(slot, next.key))
904
1042
  this.dispatchWorkerRender(slot, next);
905
1043
  }
906
- renderSync(slot, request) {
907
- const result = renderImageVisualFrame(request);
908
- if (!result || this.isKnownKey(slot, result.key) || this.isOutputBackedUp())
909
- return;
910
- this.stageFrame(result);
911
- }
912
- }
913
- var BG, PANEL, PANEL_HEADER, PANEL_LINE, LIME, LIME_DIM, CYAN, MAGENTA, INK_DIM, PNG_SIGNATURE, CRC_TABLE, MAX_IMAGE_BYTES, KITTY_CHUNK_SIZE = 4096, PANEL_BLUR_SCALE = 5, PANEL_MASK_CACHE_LIMIT = 16, ZLIB_LEVEL = 1, DEFAULT_DRONE_LAYER_SCALE = 0.18, DEFAULT_IMAGE_FPS = 24, panelMaskCache, BG_WORD, RECENT_WRITTEN_LIMIT = 24, DEFAULT_PANEL_FPS = 24, DEFAULT_PANEL_LAYER_SCALE = 0.22, DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES, DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND, MIN_BACKGROUND_BUDGET_BYTES, MIN_PANEL_BUDGET_BYTES;
914
- var init_image_visual_layer = __esm(() => {
915
- BG = [7, 8, 9, 255];
916
- PANEL = [16, 17, 21, 184];
917
- PANEL_HEADER = [16, 17, 21, 190];
918
- PANEL_LINE = [255, 255, 255, 24];
919
- LIME = [204, 255, 77, 255];
920
- LIME_DIM = [169, 214, 63, 255];
921
- CYAN = [86, 216, 255, 255];
922
- MAGENTA = [255, 121, 176, 255];
923
- INK_DIM = [108, 110, 121, 255];
924
- PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
925
- CRC_TABLE = makeCrcTable();
926
- MAX_IMAGE_BYTES = 48 * 1024 * 1024;
927
- panelMaskCache = new Map;
928
- BG_WORD = rgbaWord(BG);
929
- DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
930
- DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
931
- MIN_BACKGROUND_BUDGET_BYTES = 24 * 1024;
932
- MIN_PANEL_BUDGET_BYTES = 56 * 1024;
933
- });
934
-
935
- // packages/cli/src/app-opentui/render/image-visual-layer-native-canvas.ts
936
- var exports_image_visual_layer_native_canvas = {};
937
- __export(exports_image_visual_layer_native_canvas, {
938
- renderNativeCanvasImageVisualFrame: () => renderNativeCanvasImageVisualFrame
939
- });
940
- import { mkdirSync, writeFileSync } from "fs";
941
- import { join } from "path";
942
- import { tmpdir } from "os";
943
- import { clearAllCache, createCanvas } from "@napi-rs/canvas";
944
- function droneLayerScale2() {
945
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_DRONE_SCALE ?? "");
946
- if (Number.isFinite(raw) && raw > 0)
947
- return Math.max(0.15, Math.min(1, raw));
948
- return DEFAULT_DRONE_LAYER_SCALE2;
949
- }
950
- function kittyTransmission() {
951
- return process.env.RIG_OPENTUI_KITTY_TRANSMISSION === "temp-file" ? "temp-file" : "direct";
952
- }
953
- function motionDensity() {
954
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_MOTION_DENSITY ?? "");
955
- if (Number.isFinite(raw) && raw > 0)
956
- return Math.max(0.2, Math.min(1.4, raw));
957
- return DEFAULT_MOTION_DENSITY;
958
- }
959
- function motionSpeed() {
960
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_MOTION_SPEED ?? "");
961
- if (Number.isFinite(raw) && raw > 0)
962
- return Math.max(0.25, Math.min(1.5, raw));
963
- return DEFAULT_MOTION_SPEED;
964
- }
965
- function resetContext(ctx, width, height) {
966
- ctx.reset();
967
- ctx.clearRect(0, 0, width, height);
968
- ctx.imageSmoothingEnabled = true;
969
- ctx.imageSmoothingQuality = "high";
970
- }
971
- function reusableCanvas(width, height) {
972
- if (!frameCanvas || !frameCtx || frameCanvas.width !== width || frameCanvas.height !== height) {
973
- frameCanvas = createCanvas(width, height);
974
- frameCtx = frameCanvas.getContext("2d");
975
- }
976
- resetContext(frameCtx, width, height);
977
- return { canvas: frameCanvas, ctx: frameCtx };
978
- }
979
- function reusableSceneCanvas(width, height) {
980
- if (!sceneCanvas || !sceneCtx || sceneCanvas.width !== width || sceneCanvas.height !== height) {
981
- sceneCanvas = createCanvas(width, height);
982
- sceneCtx = sceneCanvas.getContext("2d");
983
- }
984
- resetContext(sceneCtx, width, height);
985
- return { canvas: sceneCanvas, ctx: sceneCtx };
986
- }
987
- function reusablePatchCanvas(width, height) {
988
- if (!patchCanvas || !patchCtx || patchCanvas.width !== width || patchCanvas.height !== height) {
989
- patchCanvas = createCanvas(width, height);
990
- patchCtx = patchCanvas.getContext("2d");
991
- }
992
- resetContext(patchCtx, width, height);
993
- return { canvas: patchCanvas, ctx: patchCtx };
994
- }
995
- function css(color, alphaScale = 1) {
996
- return `rgba(${color[0]},${color[1]},${color[2]},${Math.max(0, Math.min(1, color[3] / 255 * alphaScale))})`;
997
- }
998
- function visualSceneKind2(scene) {
999
- if (scene === "tasks")
1000
- return "loop";
1001
- if (scene === "help")
1002
- return "carrier";
1003
- return "dispatch";
1004
- }
1005
- function seededUnit2(seed) {
1006
- let value = seed >>> 0;
1007
- value = value * 1664525 + 1013904223 >>> 0;
1008
- return value / 4294967295;
1009
- }
1010
- function sceneCore2(width, height, kind) {
1011
- if (kind === "dispatch")
1012
- return { x: width * 0.3, y: height * 0.45 };
1013
- if (kind === "carrier")
1014
- return { x: width * 0.5, y: height * 0.54 };
1015
- return { x: width * 0.09, y: height * 0.56 };
1016
- }
1017
- function dispatchLane2(width, index, count) {
1018
- const a = seededUnit2(28672 + index * 101);
1019
- const b = seededUnit2(36864 + index * 131);
1020
- const c = seededUnit2(45056 + index * 151);
1021
- const ang = (-0.9 + 1.8 * (index / Math.max(1, count - 1))) * (Math.PI * 0.5);
1022
- return { ang, len: width * (0.32 + a * 0.42), curve: (b - 0.5) * 0.7, phase: c * Math.PI * 2 };
1023
- }
1024
- function lanePoint2(width, height, lane, p, kind = "dispatch") {
1025
- const core = sceneCore2(width, height, kind);
1026
- const dist = lane.len * p;
1027
- const bend = Math.sin(p * Math.PI) * lane.curve * Math.min(width, height) * 0.035;
1028
- const ux = Math.cos(lane.ang);
1029
- const uy = Math.sin(lane.ang);
1030
- return { x: core.x + ux * dist + -uy * bend, y: core.y + uy * dist + ux * bend };
1031
- }
1032
- function drawGlow2(ctx, cx, cy, radius, color, alpha = 0.18) {
1033
- if (radius <= 0)
1034
- return;
1035
- const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, radius);
1036
- gradient.addColorStop(0, css(color, alpha));
1037
- gradient.addColorStop(0.45, css(color, alpha * 0.28));
1038
- gradient.addColorStop(1, css(color, 0));
1039
- ctx.fillStyle = gradient;
1040
- ctx.beginPath();
1041
- ctx.arc(cx, cy, radius, 0, Math.PI * 2);
1042
- ctx.fill();
1043
- }
1044
- function drawDisc2(ctx, cx, cy, radius, color, alpha = 1) {
1045
- if (radius <= 0)
1046
- return;
1047
- ctx.fillStyle = css(color, alpha);
1048
- ctx.beginPath();
1049
- ctx.arc(cx, cy, radius, 0, Math.PI * 2);
1050
- ctx.fill();
1051
- }
1052
- function drawLine2(ctx, x0, y0, x1, y1, color, alpha = 0.5, thickness = 1.4) {
1053
- ctx.save();
1054
- ctx.strokeStyle = css(color, alpha);
1055
- ctx.lineWidth = thickness * 2;
1056
- ctx.lineCap = "round";
1057
- ctx.lineJoin = "round";
1058
- ctx.beginPath();
1059
- ctx.moveTo(x0, y0);
1060
- ctx.lineTo(x1, y1);
1061
- ctx.stroke();
1062
- ctx.restore();
1063
- }
1064
- function drawDrone2(ctx, x, y, scale, tick, color) {
1065
- const speed = motionSpeed();
1066
- const wobble = Math.sin(tick * 0.055 * speed) * scale * 0.35;
1067
- const cy = y + wobble;
1068
- drawGlow2(ctx, x, cy, scale * 7, color, 0.18);
1069
- drawDisc2(ctx, x, cy, scale * 1.45, color, 0.92);
1070
- drawDisc2(ctx, x, cy, scale * 0.55, [235, 255, 176, 255], 0.85);
1071
- const arm = scale * 4.1;
1072
- const rotor = scale * 1.6;
1073
- const arms = [[-arm, -arm * 0.72], [arm, -arm * 0.72], [-arm, arm * 0.72], [arm, arm * 0.72]];
1074
- for (const [dx, dy] of arms) {
1075
- drawLine2(ctx, x, cy, x + dx, cy + dy, color, 0.42, Math.max(1, scale * 0.22));
1076
- drawGlow2(ctx, x + dx, cy + dy, rotor * 2.6, color, 0.1);
1077
- drawDisc2(ctx, x + dx, cy + dy, rotor, color, 0.34 + 0.12 * Math.sin(tick * 0.15 * speed));
1078
- }
1079
- }
1080
- function drawMarketingRails2(ctx, width, height, kind, tick) {
1081
- const density = motionDensity();
1082
- const speed = motionSpeed();
1083
- const ys = kind === "loop" ? [0.18, 0.5, 0.82] : kind === "carrier" ? [0.24, 0.54, 0.82] : [0.18, 0.58, 0.82];
1084
- ys.forEach((ratio, index) => {
1085
- const y = height * ratio + Math.sin(tick * 0.018 * speed + index) * Math.max(2, height * 0.0025);
1086
- const color = index === 2 ? CYAN2 : index === 1 ? LIME2 : LIME_DIM2;
1087
- drawLine2(ctx, width * 0.06, y, width * 0.94, y + Math.sin(index) * height * 0.002, color, index === 1 ? 0.09 : 0.055, Math.max(0.8, height * 0.0012));
1088
- const spacing = (0.18 + index * 0.02) / density;
1089
- for (let p = (tick * 0.004 * speed + index * 0.21) % 1;p < 1; p += spacing) {
1090
- const x = width * (0.06 + 0.88 * p);
1091
- drawDisc2(ctx, x, y, Math.max(1.2, height * (0.0017 + index * 0.0004)), color, 0.18);
1092
- drawGlow2(ctx, x, y, Math.max(5, height * 0.006), color, 0.026);
1093
- }
1094
- });
1095
- }
1096
- function drawDispatchScene2(ctx, width, height, tick) {
1097
- const density = motionDensity();
1098
- const speed = motionSpeed();
1099
- const core = sceneCore2(width, height, "dispatch");
1100
- const min = Math.min(width, height);
1101
- drawGlow2(ctx, core.x, core.y, min * 0.085, LIME2, 0.12);
1102
- drawDisc2(ctx, core.x, core.y, min * 0.0065, LIME2, 0.85);
1103
- const laneCount = Math.max(4, Math.round((width < 1000 ? 7 : 12) * (0.65 + density * 0.35)));
1104
- const lanes = Array.from({ length: laneCount }, (_, index) => dispatchLane2(width, index, laneCount));
1105
- lanes.forEach((lane) => {
1106
- let prev = lanePoint2(width, height, lane, 0);
1107
- ctx.beginPath();
1108
- ctx.moveTo(prev.x, prev.y);
1109
- for (let step = 1;step <= 32; step += 1) {
1110
- const next = lanePoint2(width, height, lane, step / 32);
1111
- ctx.lineTo(next.x, next.y);
1112
- prev = next;
1113
- }
1114
- ctx.strokeStyle = css(lane.phase > Math.PI ? CYAN2 : LIME_DIM2, 0.045);
1115
- ctx.lineWidth = Math.max(0.7, min * 0.0009) * 2;
1116
- ctx.lineCap = "round";
1117
- ctx.stroke();
1118
- });
1119
- const packets = Math.max(24, Math.round((width < 1000 ? 70 : 150) * density));
1120
- for (let i = 0;i < packets; i += 1) {
1121
- const lane = lanes[i % lanes.length];
1122
- const packetSpeed = (0.003 + seededUnit2(12288 + i * 17) * 0.009) * speed;
1123
- const p = (seededUnit2(16384 + i * 19) + tick * packetSpeed) % 1;
1124
- const pt = lanePoint2(width, height, lane, p);
1125
- const wobble = (seededUnit2(20480 + i * 23) - 0.5) * min * 0.012;
1126
- const fade = p < 0.1 ? p / 0.1 : p > 0.88 ? (1 - p) / 0.12 : 1;
1127
- const color = seededUnit2(24576 + i * 29) < 0.22 ? CYAN2 : LIME_DIM2;
1128
- const y = pt.y + wobble * Math.sin(p * Math.PI * 2);
1129
- drawLine2(ctx, pt.x - min * 0.012, y - min * 0.002, pt.x, y, color, 0.08 * fade, Math.max(0.7, min * 0.0008));
1130
- drawDisc2(ctx, pt.x, y, Math.max(1.3, min * 0.0022), color, 0.5 * fade);
1131
- drawGlow2(ctx, pt.x, y, Math.max(4, min * 0.008), color, 0.045 * fade);
1132
- }
1133
- const droneCount = Math.max(2, Math.round((width < 1000 ? 4 : 8) * density));
1134
- for (let i = 0;i < droneCount; i += 1) {
1135
- const lane = lanes[i % lanes.length];
1136
- const p = (tick * 0.002 * speed + i * 0.13) % 1;
1137
- const pt = lanePoint2(width, height, lane, p);
1138
- drawDrone2(ctx, pt.x, pt.y + Math.sin(tick * 0.04 * speed + i) * min * 0.004, min * (0.006 + i % 2 * 0.0015), tick + i * 11, i % 3 === 0 ? CYAN2 : LIME_DIM2);
1139
- }
1140
- }
1141
- function drawLoopScene2(ctx, width, height, tick) {
1142
- const density = motionDensity();
1143
- const speed = motionSpeed();
1144
- const min = Math.min(width, height);
1145
- const y = height * 0.56;
1146
- const x0 = width * 0.09;
1147
- const x1 = width * 0.91;
1148
- drawLine2(ctx, x0, y, x1, y, LIME_DIM2, 0.12, Math.max(0.8, min * 0.001));
1149
- const gates = width < 1000 ? 4 : 5;
1150
- const active = Math.floor(tick * speed / 20) % gates;
1151
- for (let i = 0;i < gates; i += 1) {
1152
- const x = x0 + (x1 - x0) * (i / Math.max(1, gates - 1));
1153
- const color = i === active ? LIME2 : i <= active ? LIME_DIM2 : INK_DIM2;
1154
- drawLine2(ctx, x, y - min * 0.05, x, y + min * 0.05, color, i === active ? 0.25 : 0.08, Math.max(0.8, min * 0.001));
1155
- drawDisc2(ctx, x, y, i === active ? min * 0.006 : min * 0.0035, color, i === active ? 0.72 : 0.25);
1156
- drawGlow2(ctx, x, y, i === active ? min * 0.018 : min * 0.01, color, i === active ? 0.1 : 0.035);
1157
- }
1158
- const packets = Math.max(16, Math.round((width < 1000 ? 36 : 84) * density));
1159
- for (let i = 0;i < packets; i += 1) {
1160
- const p = (seededUnit2(33024 + i * 13) + tick * (0.0022 + seededUnit2(33280 + i * 17) * 0.004) * speed) % 1;
1161
- const x = x0 + (x1 - x0) * p;
1162
- const segment = Math.min(gates - 1, Math.floor(p * gates));
1163
- const color = segment < active ? LIME_DIM2 : segment === active ? CYAN2 : INK_DIM2;
1164
- const py = y + (seededUnit2(33536 + i * 19) - 0.5) * min * 0.018 * Math.sin(p * Math.PI * 2);
1165
- drawLine2(ctx, x - min * 0.014, py, x, py, color, 0.1, Math.max(0.7, min * 0.0008));
1166
- drawDisc2(ctx, x, py, Math.max(1.2, min * 0.002), color, color === INK_DIM2 ? 0.22 : 0.58);
1167
- }
1168
- drawDrone2(ctx, x0 + (x1 - x0) * (tick * 0.003 * speed % 1), y - min * 0.04, min * 0.0065, tick, LIME_DIM2);
1169
- drawDrone2(ctx, x0 + (x1 - x0) * ((tick * 0.002 * speed + 0.5) % 1), y + min * 0.036, min * 0.0055, tick + 40, MAGENTA2);
1170
- }
1171
- function drawCarrierScene2(ctx, width, height, tick) {
1172
- const density = motionDensity();
1173
- const speed = motionSpeed();
1174
- const min = Math.min(width, height);
1175
- const core = sceneCore2(width, height, "carrier");
1176
- drawGlow2(ctx, core.x, core.y, min * 0.075, LIME2, 0.12);
1177
- drawDisc2(ctx, core.x, core.y, min * 0.0065, LIME2, 0.92);
1178
- const nodes = Math.max(4, Math.round((width < 1000 ? 5 : 7) * (0.75 + density * 0.25)));
1179
- const rx = width * 0.36;
1180
- const ry = height * 0.22;
1181
- for (let i = 0;i < nodes; i += 1) {
1182
- const a = i / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006 * speed;
1183
- const x = core.x + Math.cos(a) * rx;
1184
- const y = core.y + Math.sin(a) * ry;
1185
- const color = i % 2 ? CYAN2 : LIME_DIM2;
1186
- drawLine2(ctx, core.x, core.y, x, y, color, 0.055, Math.max(0.7, min * 0.0008));
1187
- drawGlow2(ctx, x, y, min * 0.018, color, 0.055);
1188
- drawDisc2(ctx, x, y, min * 0.0048, color, 0.62);
1189
- drawDrone2(ctx, x, y - min * 0.018, min * 0.005, tick + i * 9, color);
1190
- }
1191
- const packets = Math.max(14, Math.round((width < 1000 ? 30 : 72) * density));
1192
- for (let i = 0;i < packets; i += 1) {
1193
- const lane = i % nodes;
1194
- const a = lane / nodes * Math.PI * 2 - Math.PI / 2 + tick * 0.006 * speed;
1195
- const nx = core.x + Math.cos(a) * rx;
1196
- const ny = core.y + Math.sin(a) * ry;
1197
- const p = (seededUnit2(37120 + i * 37) + tick * (0.003 + seededUnit2(37376 + i * 43) * 0.009) * speed) % 1;
1198
- const x = core.x + (nx - core.x) * p;
1199
- const y = core.y + (ny - core.y) * p;
1200
- const color = seededUnit2(37632 + i * 47) < 0.35 ? CYAN2 : LIME_DIM2;
1201
- drawDisc2(ctx, x, y, Math.max(1.2, min * 0.002), color, 0.48);
1202
- drawGlow2(ctx, x, y, min * 0.008, color, 0.035);
1203
- }
1204
- }
1205
- function drawBackground2(ctx, width, height, tick, scene) {
1206
- ctx.fillStyle = css(BG2);
1207
- ctx.fillRect(0, 0, width, height);
1208
- const kind = visualSceneKind2(scene);
1209
- drawGlow2(ctx, width * 0.82, height * 0.04, Math.min(width, height) * 0.22, LIME2, 0.035);
1210
- drawGlow2(ctx, width * 0.12, height * 0.18, Math.min(width, height) * 0.17, CYAN2, 0.025);
1211
- drawMarketingRails2(ctx, width, height, kind, tick);
1212
- if (kind === "loop")
1213
- drawLoopScene2(ctx, width, height, tick);
1214
- else if (kind === "carrier")
1215
- drawCarrierScene2(ctx, width, height, tick);
1216
- else
1217
- drawDispatchScene2(ctx, width, height, tick);
1218
- }
1219
- function drawScaledDroneLayer(ctx, width, height, tick, scene) {
1220
- const scale = droneLayerScale2();
1221
- if (scale >= 0.99) {
1222
- drawBackground2(ctx, width, height, tick, scene);
1223
- return;
1224
- }
1225
- const sceneWidth = Math.max(1, Math.round(width * scale));
1226
- const sceneHeight = Math.max(1, Math.round(height * scale));
1227
- const { canvas, ctx: sceneCtx2 } = reusableSceneCanvas(sceneWidth, sceneHeight);
1228
- drawBackground2(sceneCtx2, sceneWidth, sceneHeight, tick, scene);
1229
- ctx.save();
1230
- ctx.imageSmoothingEnabled = true;
1231
- ctx.imageSmoothingQuality = "high";
1232
- ctx.drawImage(canvas, 0, 0, sceneWidth, sceneHeight, 0, 0, width, height);
1233
- ctx.restore();
1234
- }
1235
- function panelPlacement(layout, panel, size) {
1236
- const cellW = size.width / layout.width;
1237
- const cellH = size.height / layout.height;
1238
- const leftCells = layout.centerLeft + (panel.left ?? 0);
1239
- const topCells = layout.centerTop + panel.top;
1240
- const widthCells = panel.width ?? layout.centerWidth;
1241
- const heightCells = panel.height;
1242
- const left = Math.round(leftCells * cellW);
1243
- const top = Math.round(topCells * cellH);
1244
- const width = Math.round(widthCells * cellW);
1245
- const height = Math.round(heightCells * cellH);
1246
- return width > 8 && height > 8 ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
1247
- }
1248
- function panelRect2(layout, panel, size) {
1249
- return panelPlacement(layout, panel, size);
1250
- }
1251
- function clipRoundRect(ctx, rect, radius) {
1252
- ctx.beginPath();
1253
- ctx.roundRect(rect.left, rect.top, rect.width, rect.height, radius);
1254
- ctx.clip();
1255
- }
1256
- function strokeRoundRect(ctx, rect, radius) {
1257
- ctx.beginPath();
1258
- ctx.roundRect(rect.left + 0.75, rect.top + 0.75, rect.width - 1.5, rect.height - 1.5, Math.max(1, radius - 0.75));
1259
- ctx.stroke();
1260
- }
1261
- function compositePanel2(ctx, sourceCanvas, rect, headerPx) {
1262
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(rect.width, rect.height) * 0.035)));
1263
- const { canvas: patch, ctx: patchCtx2 } = reusablePatchCanvas(rect.width, rect.height);
1264
- patchCtx2.drawImage(sourceCanvas, rect.left, rect.top, rect.width, rect.height, 0, 0, rect.width, rect.height);
1265
- ctx.save();
1266
- clipRoundRect(ctx, rect, radius);
1267
- ctx.filter = `blur(${Math.max(10, Math.round(Math.min(rect.width, rect.height) * 0.018))}px)`;
1268
- ctx.drawImage(patch, 0, 0, rect.width, rect.height, rect.left, rect.top, rect.width, rect.height);
1269
- ctx.filter = "none";
1270
- ctx.fillStyle = css(PANEL2);
1271
- ctx.fillRect(rect.left, rect.top, rect.width, rect.height);
1272
- ctx.fillStyle = css(PANEL_HEADER2);
1273
- ctx.fillRect(rect.left, rect.top, rect.width, headerPx);
1274
- ctx.restore();
1275
- ctx.save();
1276
- ctx.strokeStyle = css(PANEL_LINE2, 0.8);
1277
- ctx.lineWidth = 1.5;
1278
- strokeRoundRect(ctx, rect, radius);
1279
- ctx.restore();
1280
- const dividerY = Math.round(rect.top + headerPx);
1281
- if (dividerY > rect.top && dividerY < rect.top + rect.height) {
1282
- drawLine2(ctx, rect.left + radius, dividerY, rect.left + rect.width - radius, dividerY, PANEL_LINE2, 0.28, 0.7);
1283
- }
1284
- }
1285
- function compositePanelPatch(ctx, sourceCanvas, width, height, headerPx) {
1286
- const rect = { left: 0, top: 0, width, height };
1287
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(width, height) * 0.035)));
1288
- ctx.save();
1289
- clipRoundRect(ctx, rect, radius);
1290
- ctx.filter = `blur(${Math.max(10, Math.round(Math.min(width, height) * 0.018))}px)`;
1291
- ctx.drawImage(sourceCanvas, 0, 0, width, height);
1292
- ctx.filter = "none";
1293
- ctx.fillStyle = css(PANEL2);
1294
- ctx.fillRect(0, 0, width, height);
1295
- ctx.fillStyle = css(PANEL_HEADER2);
1296
- ctx.fillRect(0, 0, width, headerPx);
1297
- ctx.restore();
1298
- }
1299
- function compositePanelChromePatch(ctx, width, height, headerPx) {
1300
- const radius = Math.max(14, Math.min(34, Math.round(Math.min(width, height) * 0.035)));
1301
- ctx.clearRect(0, 0, width, height);
1302
- const dividerY = Math.round(headerPx);
1303
- if (dividerY > 0 && dividerY < height)
1304
- drawLine2(ctx, radius, dividerY, width - radius, dividerY, PANEL_LINE2, 0.22, 0.65);
1305
- }
1306
- async function encodePanelPatch(input, placement, imageId, headerPx) {
1307
- const { canvas: sourceCanvas, ctx: sourceCtx } = reusableSceneCanvas(placement.width, placement.height);
1308
- sourceCtx.save();
1309
- sourceCtx.translate(-placement.left, -placement.top);
1310
- drawScaledDroneLayer(sourceCtx, input.pixelSize.width, input.pixelSize.height, input.tick, input.scene);
1311
- sourceCtx.restore();
1312
- const { canvas: patchCanvas2, ctx: patchCtx2 } = reusablePatchCanvas(placement.width, placement.height);
1313
- compositePanelPatch(patchCtx2, sourceCanvas, placement.width, placement.height, headerPx);
1314
- const png = await patchCanvas2.encode("png");
1315
- const sequence = kittyPngFrameSequence(png, { columns: Math.max(1, Math.round(placement.widthCells)), rows: Math.max(1, Math.round(placement.heightCells)), imageId, zIndex: -12 });
1316
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
1317
- }
1318
- async function encodePanelChromePatch(placement, imageId, headerPx) {
1319
- const { canvas: patchCanvas2, ctx: patchCtx2 } = reusablePatchCanvas(placement.width, placement.height);
1320
- compositePanelChromePatch(patchCtx2, placement.width, placement.height, headerPx);
1321
- const png = await patchCanvas2.encode("png");
1322
- const sequence = kittyPngFrameSequence(png, { columns: Math.max(1, Math.round(placement.widthCells)), rows: Math.max(1, Math.round(placement.heightCells)), imageId, zIndex: -8 });
1323
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
1324
- }
1325
- function kittyImageSequence2(base64, options) {
1326
- const params = [`a=T`, `f=100`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
1327
- if (base64.length <= KITTY_CHUNK_SIZE2)
1328
- return `\x1B_G${params.join(",")};${base64}\x1B\\`;
1329
- const chunks = [];
1330
- for (let offset = 0;offset < base64.length; offset += KITTY_CHUNK_SIZE2) {
1331
- const part = base64.slice(offset, offset + KITTY_CHUNK_SIZE2);
1332
- const last = offset + KITTY_CHUNK_SIZE2 >= base64.length;
1333
- chunks.push(`\x1B_G${offset === 0 ? `${params.join(",")},` : ""}m=${last ? 0 : 1};${part}\x1B\\`);
1334
- }
1335
- return chunks.join("");
1336
1044
  }
1337
- function kittyTempFileImageSequence(png, options) {
1338
- mkdirSync(TEMP_GRAPHICS_DIR, { recursive: true });
1339
- const path = join(TEMP_GRAPHICS_DIR, `frame-${process.pid}-${Date.now()}-${encodedFrames}.tty-graphics-protocol.png`);
1340
- writeFileSync(path, png);
1341
- const payload = Buffer.from(path).toString("base64");
1342
- const params = [`a=T`, `f=100`, `t=t`, `S=${png.byteLength}`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
1343
- return `\x1B_G${params.join(",")};${payload}\x1B\\`;
1344
- }
1345
- function kittyPngFrameSequence(png, options) {
1346
- if (kittyTransmission() === "temp-file")
1347
- return kittyTempFileImageSequence(png, options);
1348
- return kittyImageSequence2(png.toString("base64"), options);
1349
- }
1350
- async function renderNativeCanvasImageVisualFrame(input) {
1351
- if (input.transport !== "png")
1352
- return null;
1353
- const width = Math.max(1, Math.floor(input.pixelSize.width));
1354
- const height = Math.max(1, Math.floor(input.pixelSize.height));
1355
- if (width * height * 4 > MAX_IMAGE_BYTES2)
1356
- return null;
1357
- const layer = input.layer ?? "full";
1358
- if (layer === "split") {
1359
- const backgroundScale = droneLayerScale2();
1360
- const backgroundWidth = Math.max(input.layout.width, Math.round(width * backgroundScale));
1361
- const backgroundHeight = Math.max(input.layout.height, Math.round(height * backgroundScale));
1362
- const { canvas: backgroundCanvas, ctx: backgroundCtx } = reusableCanvas(backgroundWidth, backgroundHeight);
1363
- drawBackground2(backgroundCtx, backgroundWidth, backgroundHeight, input.tick, input.scene);
1364
- const backgroundPng = await backgroundCanvas.encode("png");
1365
- const sequences = [kittyPngFrameSequence(backgroundPng, { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: -30 })];
1366
- let panelIndex = 0;
1367
- for (const panel of input.panels) {
1368
- if (panel.chrome !== "ad-terminal")
1369
- continue;
1370
- const placement = panelPlacement(input.layout, panel, { width, height });
1371
- if (!placement)
1372
- continue;
1373
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
1374
- const cellH = height / input.layout.height;
1375
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1376
- sequences.push(await encodePanelPatch(input, placement, imageId, headerPx));
1377
- panelIndex += 1;
1378
- }
1379
- encodedFrames += 1;
1380
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1381
- clearAllCache();
1382
- return {
1383
- key: imageVisualFrameKey(input),
1384
- sequence: sequences.join("")
1385
- };
1386
- }
1387
- if (layer === "panel-patches") {
1388
- const sequences = [];
1389
- let panelIndex = 0;
1390
- for (const panel of input.panels) {
1391
- if (panel.chrome !== "ad-terminal")
1392
- continue;
1393
- const placement = panelPlacement(input.layout, panel, { width, height });
1394
- if (!placement)
1395
- continue;
1396
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
1397
- const cellH = height / input.layout.height;
1398
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1399
- sequences.push(await encodePanelPatch(input, placement, imageId, headerPx));
1400
- panelIndex += 1;
1401
- }
1402
- encodedFrames += 1;
1403
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1404
- clearAllCache();
1405
- return {
1406
- key: imageVisualFrameKey(input),
1407
- sequence: sequences.join("")
1408
- };
1409
- }
1410
- if (layer === "panel-chrome") {
1411
- const sequences = [];
1412
- let panelIndex = 0;
1413
- for (const panel of input.panels) {
1414
- if (panel.chrome !== "ad-terminal")
1415
- continue;
1416
- const placement = panelPlacement(input.layout, panel, { width, height });
1417
- if (!placement)
1418
- continue;
1419
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 2000 + panelIndex >>> 0 || panelIndex + 1);
1420
- const cellH = height / input.layout.height;
1421
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1422
- sequences.push(await encodePanelChromePatch(placement, imageId, headerPx));
1423
- panelIndex += 1;
1424
- }
1425
- encodedFrames += 1;
1426
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1427
- clearAllCache();
1428
- return {
1429
- key: imageVisualFrameKey(input),
1430
- sequence: sequences.join("")
1431
- };
1432
- }
1433
- const { canvas, ctx } = reusableCanvas(width, height);
1434
- if (layer === "background") {
1435
- drawBackground2(ctx, width, height, input.tick, input.scene);
1436
- } else if (layer === "panels") {
1437
- const { canvas: sourceCanvas, ctx: sourceCtx } = reusableSceneCanvas(width, height);
1438
- drawScaledDroneLayer(sourceCtx, width, height, input.tick, input.scene);
1439
- for (const panel of input.panels) {
1440
- if (panel.chrome !== "ad-terminal")
1441
- continue;
1442
- const rect = panelRect2(input.layout, panel, { width, height });
1443
- if (!rect)
1444
- continue;
1445
- const cellH = height / input.layout.height;
1446
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1447
- compositePanel2(ctx, sourceCanvas, rect, headerPx);
1448
- }
1449
- } else {
1450
- drawScaledDroneLayer(ctx, width, height, input.tick, input.scene);
1451
- for (const panel of input.panels) {
1452
- if (panel.chrome !== "ad-terminal")
1453
- continue;
1454
- const rect = panelRect2(input.layout, panel, { width, height });
1455
- if (!rect)
1456
- continue;
1457
- const cellH = height / input.layout.height;
1458
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
1459
- compositePanel2(ctx, canvas, rect, headerPx);
1460
- }
1461
- }
1462
- const png = await canvas.encode("png");
1463
- encodedFrames += 1;
1464
- if (encodedFrames % SKIA_CACHE_CLEAR_INTERVAL === 0)
1465
- clearAllCache();
1466
- return {
1467
- key: imageVisualFrameKey(input),
1468
- sequence: kittyPngFrameSequence(png, { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: input.zIndex })
1469
- };
1470
- }
1471
- var BG2, PANEL2, PANEL_HEADER2, PANEL_LINE2, LIME2, LIME_DIM2, CYAN2, MAGENTA2, INK_DIM2, KITTY_CHUNK_SIZE2 = 4096, MAX_IMAGE_BYTES2, SKIA_CACHE_CLEAR_INTERVAL = 240, DEFAULT_DRONE_LAYER_SCALE2 = 0.18, DEFAULT_MOTION_DENSITY = 0.42, DEFAULT_MOTION_SPEED = 0.55, TEMP_GRAPHICS_DIR, frameCanvas = null, frameCtx = null, sceneCanvas = null, sceneCtx = null, patchCanvas = null, patchCtx = null, encodedFrames = 0;
1472
- var init_image_visual_layer_native_canvas = __esm(() => {
1473
- init_image_visual_layer();
1474
- BG2 = [7, 8, 9, 255];
1475
- PANEL2 = [16, 17, 21, 184];
1476
- PANEL_HEADER2 = [16, 17, 21, 190];
1477
- PANEL_LINE2 = [255, 255, 255, 24];
1478
- LIME2 = [204, 255, 77, 255];
1479
- LIME_DIM2 = [169, 214, 63, 255];
1480
- CYAN2 = [86, 216, 255, 255];
1481
- MAGENTA2 = [255, 121, 176, 255];
1482
- INK_DIM2 = [108, 110, 121, 255];
1483
- MAX_IMAGE_BYTES2 = 48 * 1024 * 1024;
1484
- TEMP_GRAPHICS_DIR = join(tmpdir(), "rig-tty-graphics-protocol");
1485
- });
1486
1045
 
1487
1046
  // packages/cli/src/app-opentui/render/image-visual-layer-worker.ts
1488
- init_image_visual_layer();
1489
- import { parentPort } from "worker_threads";
1490
- var nativeCanvasRenderer;
1491
1047
  async function renderFrame(request) {
1492
- if (process.env.RIG_OPENTUI_IMAGE_BACKEND !== "ts" && request.transport === "png") {
1493
- if (nativeCanvasRenderer === undefined) {
1494
- try {
1495
- const mod = await Promise.resolve().then(() => (init_image_visual_layer_native_canvas(), exports_image_visual_layer_native_canvas));
1496
- nativeCanvasRenderer = mod.renderNativeCanvasImageVisualFrame;
1497
- } catch {
1498
- nativeCanvasRenderer = null;
1499
- }
1500
- }
1501
- if (nativeCanvasRenderer) {
1502
- try {
1503
- const native = await nativeCanvasRenderer(request);
1504
- if (native)
1505
- return native;
1506
- } catch {
1507
- nativeCanvasRenderer = null;
1508
- }
1509
- }
1510
- }
1048
+ if (request.transport !== "png")
1049
+ throw new Error("OpenTUI image workers require PNG transport.");
1511
1050
  return renderImageVisualFrame(request);
1512
1051
  }
1513
1052
  function isImageVisualWorkerRequest(value) {