@agentdeck/bridge 0.2.2 → 1.0.0

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 (435) hide show
  1. package/assets/fm-helper/agentdeck-fm-helper +0 -0
  2. package/assets/fonts/IBMPlexSans-Bold.ttf +0 -0
  3. package/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
  4. package/assets/fonts/JetBrainsMono-Bold.ttf +0 -0
  5. package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
  6. package/assets/fonts/LICENSES.md +22 -0
  7. package/dist/adapters/claude-code.d.ts +6 -0
  8. package/dist/adapters/claude-code.d.ts.map +1 -1
  9. package/dist/adapters/claude-code.js +8 -0
  10. package/dist/adapters/claude-code.js.map +1 -1
  11. package/dist/adapters/openclaw.d.ts +24 -0
  12. package/dist/adapters/openclaw.d.ts.map +1 -1
  13. package/dist/adapters/openclaw.js +291 -35
  14. package/dist/adapters/openclaw.js.map +1 -1
  15. package/dist/adapters/opencode-adapter.d.ts +59 -0
  16. package/dist/adapters/opencode-adapter.d.ts.map +1 -1
  17. package/dist/adapters/opencode-adapter.js +297 -35
  18. package/dist/adapters/opencode-adapter.js.map +1 -1
  19. package/dist/adb-reverse.d.ts +1 -1
  20. package/dist/adb-reverse.js +1 -1
  21. package/dist/antigravity-local.d.ts +1 -0
  22. package/dist/antigravity-local.d.ts.map +1 -1
  23. package/dist/antigravity-local.js +33 -0
  24. package/dist/antigravity-local.js.map +1 -1
  25. package/dist/apme/adapters/claude-hook.d.ts.map +1 -1
  26. package/dist/apme/adapters/claude-hook.js +5 -0
  27. package/dist/apme/adapters/claude-hook.js.map +1 -1
  28. package/dist/apme/adapters/codex-hook.d.ts +17 -0
  29. package/dist/apme/adapters/codex-hook.d.ts.map +1 -0
  30. package/dist/apme/adapters/codex-hook.js +95 -0
  31. package/dist/apme/adapters/codex-hook.js.map +1 -0
  32. package/dist/apme/adapters/codex-turn-manager.d.ts +86 -0
  33. package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -0
  34. package/dist/apme/adapters/codex-turn-manager.js +496 -0
  35. package/dist/apme/adapters/codex-turn-manager.js.map +1 -0
  36. package/dist/apme/adapters/openclaw-hook.d.ts +99 -0
  37. package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -0
  38. package/dist/apme/adapters/openclaw-hook.js +234 -0
  39. package/dist/apme/adapters/openclaw-hook.js.map +1 -0
  40. package/dist/apme/adapters/opencode-hook.d.ts +56 -0
  41. package/dist/apme/adapters/opencode-hook.d.ts.map +1 -0
  42. package/dist/apme/adapters/opencode-hook.js +182 -0
  43. package/dist/apme/adapters/opencode-hook.js.map +1 -0
  44. package/dist/apme/adapters/timeline.d.ts.map +1 -1
  45. package/dist/apme/adapters/timeline.js +6 -1
  46. package/dist/apme/adapters/timeline.js.map +1 -1
  47. package/dist/apme/antigravity-transcript.d.ts +18 -0
  48. package/dist/apme/antigravity-transcript.d.ts.map +1 -0
  49. package/dist/apme/antigravity-transcript.js +94 -0
  50. package/dist/apme/antigravity-transcript.js.map +1 -0
  51. package/dist/apme/classify-turn.d.ts +13 -0
  52. package/dist/apme/classify-turn.d.ts.map +1 -0
  53. package/dist/apme/classify-turn.js +49 -0
  54. package/dist/apme/classify-turn.js.map +1 -0
  55. package/dist/apme/claude-transcript-reader.d.ts +10 -0
  56. package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
  57. package/dist/apme/claude-transcript-reader.js +35 -0
  58. package/dist/apme/claude-transcript-reader.js.map +1 -1
  59. package/dist/apme/collector.d.ts +133 -6
  60. package/dist/apme/collector.d.ts.map +1 -1
  61. package/dist/apme/collector.js +471 -23
  62. package/dist/apme/collector.js.map +1 -1
  63. package/dist/apme/dashboard-html.d.ts.map +1 -1
  64. package/dist/apme/dashboard-html.js +117 -11
  65. package/dist/apme/dashboard-html.js.map +1 -1
  66. package/dist/apme/http.d.ts.map +1 -1
  67. package/dist/apme/http.js +52 -6
  68. package/dist/apme/http.js.map +1 -1
  69. package/dist/apme/index.d.ts +26 -5
  70. package/dist/apme/index.d.ts.map +1 -1
  71. package/dist/apme/index.js +146 -9
  72. package/dist/apme/index.js.map +1 -1
  73. package/dist/apme/judge-detect.d.ts +26 -0
  74. package/dist/apme/judge-detect.d.ts.map +1 -0
  75. package/dist/apme/judge-detect.js +69 -0
  76. package/dist/apme/judge-detect.js.map +1 -0
  77. package/dist/apme/pareto.d.ts +39 -0
  78. package/dist/apme/pareto.d.ts.map +1 -0
  79. package/dist/apme/pareto.js +59 -0
  80. package/dist/apme/pareto.js.map +1 -0
  81. package/dist/apme/recommend.d.ts +3 -0
  82. package/dist/apme/recommend.d.ts.map +1 -1
  83. package/dist/apme/recommend.js +43 -1
  84. package/dist/apme/recommend.js.map +1 -1
  85. package/dist/apme/runner.d.ts +99 -4
  86. package/dist/apme/runner.d.ts.map +1 -1
  87. package/dist/apme/runner.js +743 -72
  88. package/dist/apme/runner.js.map +1 -1
  89. package/dist/apme/sample-to-timeline.d.ts +26 -0
  90. package/dist/apme/sample-to-timeline.d.ts.map +1 -0
  91. package/dist/apme/sample-to-timeline.js +95 -0
  92. package/dist/apme/sample-to-timeline.js.map +1 -0
  93. package/dist/apme/scorers/index.d.ts +47 -0
  94. package/dist/apme/scorers/index.d.ts.map +1 -0
  95. package/dist/apme/scorers/index.js +96 -0
  96. package/dist/apme/scorers/index.js.map +1 -0
  97. package/dist/apme/settings.d.ts +17 -11
  98. package/dist/apme/settings.d.ts.map +1 -1
  99. package/dist/apme/settings.js +30 -9
  100. package/dist/apme/settings.js.map +1 -1
  101. package/dist/apme/store.d.ts +33 -1
  102. package/dist/apme/store.d.ts.map +1 -1
  103. package/dist/apme/store.js +334 -25
  104. package/dist/apme/store.js.map +1 -1
  105. package/dist/awaiting-overlay.d.ts +112 -0
  106. package/dist/awaiting-overlay.d.ts.map +1 -0
  107. package/dist/awaiting-overlay.js +179 -0
  108. package/dist/awaiting-overlay.js.map +1 -0
  109. package/dist/ble-sync-spawn.d.ts +63 -0
  110. package/dist/ble-sync-spawn.d.ts.map +1 -0
  111. package/dist/ble-sync-spawn.js +196 -0
  112. package/dist/ble-sync-spawn.js.map +1 -0
  113. package/dist/bridge-core.d.ts +34 -1
  114. package/dist/bridge-core.d.ts.map +1 -1
  115. package/dist/bridge-core.js +207 -24
  116. package/dist/bridge-core.js.map +1 -1
  117. package/dist/broadcast.d.ts +3 -0
  118. package/dist/broadcast.d.ts.map +1 -0
  119. package/dist/broadcast.js +122 -0
  120. package/dist/broadcast.js.map +1 -0
  121. package/dist/check-deps.d.ts.map +1 -1
  122. package/dist/check-deps.js +25 -9
  123. package/dist/check-deps.js.map +1 -1
  124. package/dist/claude-permission-rules.d.ts +55 -0
  125. package/dist/claude-permission-rules.d.ts.map +1 -0
  126. package/dist/claude-permission-rules.js +197 -0
  127. package/dist/claude-permission-rules.js.map +1 -0
  128. package/dist/cli.d.ts +15 -1
  129. package/dist/cli.d.ts.map +1 -1
  130. package/dist/cli.js +1030 -93
  131. package/dist/cli.js.map +1 -1
  132. package/dist/codex-auth.d.ts +21 -0
  133. package/dist/codex-auth.d.ts.map +1 -1
  134. package/dist/codex-auth.js +52 -1
  135. package/dist/codex-auth.js.map +1 -1
  136. package/dist/codex-output-parser.d.ts +8 -0
  137. package/dist/codex-output-parser.d.ts.map +1 -1
  138. package/dist/codex-output-parser.js +110 -25
  139. package/dist/codex-output-parser.js.map +1 -1
  140. package/dist/codex-rate-limits.d.ts +16 -0
  141. package/dist/codex-rate-limits.d.ts.map +1 -0
  142. package/dist/codex-rate-limits.js +197 -0
  143. package/dist/codex-rate-limits.js.map +1 -0
  144. package/dist/codex-rollout-response.d.ts +31 -0
  145. package/dist/codex-rollout-response.d.ts.map +1 -0
  146. package/dist/codex-rollout-response.js +135 -0
  147. package/dist/codex-rollout-response.js.map +1 -0
  148. package/dist/d200h/image-renderer.d.ts +4 -16
  149. package/dist/d200h/image-renderer.d.ts.map +1 -1
  150. package/dist/d200h/image-renderer.js +46 -212
  151. package/dist/d200h/image-renderer.js.map +1 -1
  152. package/dist/daemon-server.d.ts +85 -0
  153. package/dist/daemon-server.d.ts.map +1 -1
  154. package/dist/daemon-server.js +1975 -159
  155. package/dist/daemon-server.js.map +1 -1
  156. package/dist/daemon.js +15 -6
  157. package/dist/daemon.js.map +1 -1
  158. package/dist/display-dim.d.ts +9 -0
  159. package/dist/display-dim.d.ts.map +1 -0
  160. package/dist/display-dim.js +53 -0
  161. package/dist/display-dim.js.map +1 -0
  162. package/dist/display-monitor.d.ts +15 -2
  163. package/dist/display-monitor.d.ts.map +1 -1
  164. package/dist/display-monitor.js +104 -26
  165. package/dist/display-monitor.js.map +1 -1
  166. package/dist/esp32-serial.d.ts +190 -1
  167. package/dist/esp32-serial.d.ts.map +1 -1
  168. package/dist/esp32-serial.js +1090 -156
  169. package/dist/esp32-serial.js.map +1 -1
  170. package/dist/fallback-task-timeline.d.ts +42 -0
  171. package/dist/fallback-task-timeline.d.ts.map +1 -0
  172. package/dist/fallback-task-timeline.js +119 -0
  173. package/dist/fallback-task-timeline.js.map +1 -0
  174. package/dist/foundation-models-helper.d.ts +11 -0
  175. package/dist/foundation-models-helper.d.ts.map +1 -0
  176. package/dist/foundation-models-helper.js +241 -0
  177. package/dist/foundation-models-helper.js.map +1 -0
  178. package/dist/hook-migration.js +1 -1
  179. package/dist/hook-migration.js.map +1 -1
  180. package/dist/hook-server.d.ts +2 -3
  181. package/dist/hook-server.d.ts.map +1 -1
  182. package/dist/hook-server.js +23 -10
  183. package/dist/hook-server.js.map +1 -1
  184. package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +33 -0
  185. package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -0
  186. package/dist/idotmatrix/idotmatrix-daemon-sync.js +164 -0
  187. package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -0
  188. package/dist/idotmatrix/idotmatrix-discover.d.ts +18 -0
  189. package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -0
  190. package/dist/idotmatrix/idotmatrix-discover.js +85 -0
  191. package/dist/idotmatrix/idotmatrix-discover.js.map +1 -0
  192. package/dist/idotmatrix/idotmatrix-settings.d.ts +20 -0
  193. package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -0
  194. package/dist/idotmatrix/idotmatrix-settings.js +57 -0
  195. package/dist/idotmatrix/idotmatrix-settings.js.map +1 -0
  196. package/dist/index.d.ts.map +1 -1
  197. package/dist/index.js +217 -179
  198. package/dist/index.js.map +1 -1
  199. package/dist/log-stream.d.ts +15 -16
  200. package/dist/log-stream.d.ts.map +1 -1
  201. package/dist/log-stream.js +18 -96
  202. package/dist/log-stream.js.map +1 -1
  203. package/dist/logger.d.ts +3 -1
  204. package/dist/logger.d.ts.map +1 -1
  205. package/dist/logger.js +18 -5
  206. package/dist/logger.js.map +1 -1
  207. package/dist/mdns.d.ts +17 -0
  208. package/dist/mdns.d.ts.map +1 -1
  209. package/dist/mdns.js +52 -18
  210. package/dist/mdns.js.map +1 -1
  211. package/dist/model-catalog.d.ts +1 -1
  212. package/dist/model-catalog.d.ts.map +1 -1
  213. package/dist/model-catalog.js +2 -4
  214. package/dist/model-catalog.js.map +1 -1
  215. package/dist/modules/adb-module.d.ts +1 -1
  216. package/dist/modules/adb-module.d.ts.map +1 -1
  217. package/dist/modules/adb-module.js +4 -3
  218. package/dist/modules/adb-module.js.map +1 -1
  219. package/dist/modules/broadcast-module.d.ts +17 -0
  220. package/dist/modules/broadcast-module.d.ts.map +1 -0
  221. package/dist/modules/broadcast-module.js +26 -0
  222. package/dist/modules/broadcast-module.js.map +1 -0
  223. package/dist/modules/d200h-module.d.ts +22 -0
  224. package/dist/modules/d200h-module.d.ts.map +1 -1
  225. package/dist/modules/d200h-module.js +200 -65
  226. package/dist/modules/d200h-module.js.map +1 -1
  227. package/dist/modules/idotmatrix-module.d.ts +9 -0
  228. package/dist/modules/idotmatrix-module.d.ts.map +1 -0
  229. package/dist/modules/idotmatrix-module.js +46 -0
  230. package/dist/modules/idotmatrix-module.js.map +1 -0
  231. package/dist/modules/index.d.ts +3 -1
  232. package/dist/modules/index.d.ts.map +1 -1
  233. package/dist/modules/index.js +9 -3
  234. package/dist/modules/index.js.map +1 -1
  235. package/dist/modules/pixoo-module.d.ts.map +1 -1
  236. package/dist/modules/pixoo-module.js +14 -4
  237. package/dist/modules/pixoo-module.js.map +1 -1
  238. package/dist/modules/serial-module.d.ts +4 -0
  239. package/dist/modules/serial-module.d.ts.map +1 -1
  240. package/dist/modules/serial-module.js +14 -12
  241. package/dist/modules/serial-module.js.map +1 -1
  242. package/dist/modules/timebox-module.d.ts +9 -0
  243. package/dist/modules/timebox-module.d.ts.map +1 -0
  244. package/dist/modules/timebox-module.js +46 -0
  245. package/dist/modules/timebox-module.js.map +1 -0
  246. package/dist/modules/trmnl-module.d.ts +38 -0
  247. package/dist/modules/trmnl-module.d.ts.map +1 -0
  248. package/dist/modules/trmnl-module.js +111 -0
  249. package/dist/modules/trmnl-module.js.map +1 -0
  250. package/dist/modules/types.d.ts +4 -2
  251. package/dist/modules/types.d.ts.map +1 -1
  252. package/dist/observed-steering.d.ts +93 -0
  253. package/dist/observed-steering.d.ts.map +1 -0
  254. package/dist/observed-steering.js +247 -0
  255. package/dist/observed-steering.js.map +1 -0
  256. package/dist/openclaw-session.d.ts +18 -0
  257. package/dist/openclaw-session.d.ts.map +1 -0
  258. package/dist/openclaw-session.js +34 -0
  259. package/dist/openclaw-session.js.map +1 -0
  260. package/dist/opencode-steering.d.ts +36 -0
  261. package/dist/opencode-steering.d.ts.map +1 -0
  262. package/dist/opencode-steering.js +84 -0
  263. package/dist/opencode-steering.js.map +1 -0
  264. package/dist/output-parser.d.ts +30 -0
  265. package/dist/output-parser.d.ts.map +1 -1
  266. package/dist/output-parser.js +172 -18
  267. package/dist/output-parser.js.map +1 -1
  268. package/dist/passive-observer.d.ts +32 -0
  269. package/dist/passive-observer.d.ts.map +1 -1
  270. package/dist/passive-observer.js +288 -49
  271. package/dist/passive-observer.js.map +1 -1
  272. package/dist/permission-resolver.d.ts +55 -0
  273. package/dist/permission-resolver.d.ts.map +1 -0
  274. package/dist/permission-resolver.js +112 -0
  275. package/dist/permission-resolver.js.map +1 -0
  276. package/dist/pixoo/micro-glyphs.d.ts +18 -0
  277. package/dist/pixoo/micro-glyphs.d.ts.map +1 -0
  278. package/dist/pixoo/micro-glyphs.js +143 -0
  279. package/dist/pixoo/micro-glyphs.js.map +1 -0
  280. package/dist/pixoo/official-dot-glyphs.generated.d.ts +8 -0
  281. package/dist/pixoo/official-dot-glyphs.generated.d.ts.map +1 -0
  282. package/dist/pixoo/official-dot-glyphs.generated.js +247 -0
  283. package/dist/pixoo/official-dot-glyphs.generated.js.map +1 -0
  284. package/dist/pixoo/pixoo-bridge.d.ts +16 -8
  285. package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
  286. package/dist/pixoo/pixoo-bridge.js +127 -35
  287. package/dist/pixoo/pixoo-bridge.js.map +1 -1
  288. package/dist/pixoo/pixoo-camera.d.ts +19 -3
  289. package/dist/pixoo/pixoo-camera.d.ts.map +1 -1
  290. package/dist/pixoo/pixoo-camera.js +62 -27
  291. package/dist/pixoo/pixoo-camera.js.map +1 -1
  292. package/dist/pixoo/pixoo-client.d.ts +13 -3
  293. package/dist/pixoo/pixoo-client.d.ts.map +1 -1
  294. package/dist/pixoo/pixoo-client.js +51 -22
  295. package/dist/pixoo/pixoo-client.js.map +1 -1
  296. package/dist/pixoo/pixoo-discover.d.ts +37 -0
  297. package/dist/pixoo/pixoo-discover.d.ts.map +1 -0
  298. package/dist/pixoo/pixoo-discover.js +105 -0
  299. package/dist/pixoo/pixoo-discover.js.map +1 -0
  300. package/dist/pixoo/pixoo-renderer.d.ts +27 -3
  301. package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
  302. package/dist/pixoo/pixoo-renderer.js +533 -135
  303. package/dist/pixoo/pixoo-renderer.js.map +1 -1
  304. package/dist/pixoo/pixoo-settings.d.ts +6 -0
  305. package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
  306. package/dist/pixoo/pixoo-settings.js +9 -0
  307. package/dist/pixoo/pixoo-settings.js.map +1 -1
  308. package/dist/pixoo/pixoo-sprites.d.ts +84 -11
  309. package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
  310. package/dist/pixoo/pixoo-sprites.js +620 -209
  311. package/dist/pixoo/pixoo-sprites.js.map +1 -1
  312. package/dist/pty-manager.d.ts.map +1 -1
  313. package/dist/pty-manager.js +8 -2
  314. package/dist/pty-manager.js.map +1 -1
  315. package/dist/review-runner.d.ts +113 -0
  316. package/dist/review-runner.d.ts.map +1 -0
  317. package/dist/review-runner.js +428 -0
  318. package/dist/review-runner.js.map +1 -0
  319. package/dist/session-activity.d.ts +14 -0
  320. package/dist/session-activity.d.ts.map +1 -0
  321. package/dist/session-activity.js +160 -0
  322. package/dist/session-activity.js.map +1 -0
  323. package/dist/session-aggregator.d.ts +25 -0
  324. package/dist/session-aggregator.d.ts.map +1 -1
  325. package/dist/session-aggregator.js +48 -9
  326. package/dist/session-aggregator.js.map +1 -1
  327. package/dist/session-focus-relay.d.ts.map +1 -1
  328. package/dist/session-focus-relay.js +7 -1
  329. package/dist/session-focus-relay.js.map +1 -1
  330. package/dist/session-registry.d.ts +74 -3
  331. package/dist/session-registry.d.ts.map +1 -1
  332. package/dist/session-registry.js +140 -8
  333. package/dist/session-registry.js.map +1 -1
  334. package/dist/session-timeline-relay.d.ts.map +1 -1
  335. package/dist/session-timeline-relay.js +13 -4
  336. package/dist/session-timeline-relay.js.map +1 -1
  337. package/dist/session-transcript-timeline.d.ts +49 -0
  338. package/dist/session-transcript-timeline.d.ts.map +1 -0
  339. package/dist/session-transcript-timeline.js +276 -0
  340. package/dist/session-transcript-timeline.js.map +1 -0
  341. package/dist/state-machine.d.ts +6 -1
  342. package/dist/state-machine.d.ts.map +1 -1
  343. package/dist/state-machine.js +86 -28
  344. package/dist/state-machine.js.map +1 -1
  345. package/dist/timebox/timebox-daemon-sync.d.ts +19 -0
  346. package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -0
  347. package/dist/timebox/timebox-daemon-sync.js +135 -0
  348. package/dist/timebox/timebox-daemon-sync.js.map +1 -0
  349. package/dist/timebox/timebox-discover.d.ts +19 -0
  350. package/dist/timebox/timebox-discover.d.ts.map +1 -0
  351. package/dist/timebox/timebox-discover.js +87 -0
  352. package/dist/timebox/timebox-discover.js.map +1 -0
  353. package/dist/timebox/timebox-settings.d.ts +32 -0
  354. package/dist/timebox/timebox-settings.d.ts.map +1 -0
  355. package/dist/timebox/timebox-settings.js +80 -0
  356. package/dist/timebox/timebox-settings.js.map +1 -0
  357. package/dist/timeline-store.d.ts +112 -3
  358. package/dist/timeline-store.d.ts.map +1 -1
  359. package/dist/timeline-store.js +460 -14
  360. package/dist/timeline-store.js.map +1 -1
  361. package/dist/timeline-summarizer.d.ts.map +1 -1
  362. package/dist/timeline-summarizer.js +29 -5
  363. package/dist/timeline-summarizer.js.map +1 -1
  364. package/dist/trmnl/byos-server.d.ts +39 -0
  365. package/dist/trmnl/byos-server.d.ts.map +1 -0
  366. package/dist/trmnl/byos-server.js +301 -0
  367. package/dist/trmnl/byos-server.js.map +1 -0
  368. package/dist/trmnl/frame-cache.d.ts +37 -0
  369. package/dist/trmnl/frame-cache.d.ts.map +1 -0
  370. package/dist/trmnl/frame-cache.js +157 -0
  371. package/dist/trmnl/frame-cache.js.map +1 -0
  372. package/dist/trmnl/image-renderer.d.ts +27 -0
  373. package/dist/trmnl/image-renderer.d.ts.map +1 -0
  374. package/dist/trmnl/image-renderer.js +235 -0
  375. package/dist/trmnl/image-renderer.js.map +1 -0
  376. package/dist/trmnl/trmnl-settings.d.ts +96 -0
  377. package/dist/trmnl/trmnl-settings.d.ts.map +1 -0
  378. package/dist/trmnl/trmnl-settings.js +177 -0
  379. package/dist/trmnl/trmnl-settings.js.map +1 -0
  380. package/dist/trmnl/trmnl-telemetry.d.ts +47 -0
  381. package/dist/trmnl/trmnl-telemetry.d.ts.map +1 -0
  382. package/dist/trmnl/trmnl-telemetry.js +56 -0
  383. package/dist/trmnl/trmnl-telemetry.js.map +1 -0
  384. package/dist/tui/ansi.d.ts +0 -4
  385. package/dist/tui/ansi.d.ts.map +1 -1
  386. package/dist/tui/ansi.js +14 -7
  387. package/dist/tui/ansi.js.map +1 -1
  388. package/dist/tui/dashboard.d.ts +1 -0
  389. package/dist/tui/dashboard.d.ts.map +1 -1
  390. package/dist/tui/dashboard.js +38 -11
  391. package/dist/tui/dashboard.js.map +1 -1
  392. package/dist/tui/renderer.d.ts +36 -0
  393. package/dist/tui/renderer.d.ts.map +1 -1
  394. package/dist/tui/renderer.js +355 -121
  395. package/dist/tui/renderer.js.map +1 -1
  396. package/dist/tui/terrarium.d.ts.map +1 -1
  397. package/dist/tui/terrarium.js +44 -51
  398. package/dist/tui/terrarium.js.map +1 -1
  399. package/dist/types.d.ts +2 -2
  400. package/dist/types.d.ts.map +1 -1
  401. package/dist/types.js +2 -2
  402. package/dist/types.js.map +1 -1
  403. package/dist/usage-api.d.ts.map +1 -1
  404. package/dist/usage-api.js +7 -1
  405. package/dist/usage-api.js.map +1 -1
  406. package/dist/usage-event.d.ts +10 -3
  407. package/dist/usage-event.d.ts.map +1 -1
  408. package/dist/usage-event.js +98 -13
  409. package/dist/usage-event.js.map +1 -1
  410. package/dist/utility-proxy.d.ts.map +1 -1
  411. package/dist/utility-proxy.js +8 -0
  412. package/dist/utility-proxy.js.map +1 -1
  413. package/dist/utils/project-name.d.ts +3 -0
  414. package/dist/utils/project-name.d.ts.map +1 -1
  415. package/dist/utils/project-name.js +44 -2
  416. package/dist/utils/project-name.js.map +1 -1
  417. package/dist/version-check.d.ts.map +1 -1
  418. package/dist/version-check.js +1 -17
  419. package/dist/version-check.js.map +1 -1
  420. package/dist/windows-service.d.ts +26 -0
  421. package/dist/windows-service.d.ts.map +1 -0
  422. package/dist/windows-service.js +145 -0
  423. package/dist/windows-service.js.map +1 -0
  424. package/dist/ws-server.d.ts +17 -3
  425. package/dist/ws-server.d.ts.map +1 -1
  426. package/dist/ws-server.js +78 -8
  427. package/dist/ws-server.js.map +1 -1
  428. package/fm-helper/AgentDeckFMHelper.swift +112 -0
  429. package/package.json +10 -4
  430. package/scripts/build-fm-helper.mjs +26 -0
  431. package/LICENSE +0 -21
  432. package/dist/apme/tuner.d.ts +0 -64
  433. package/dist/apme/tuner.d.ts.map +0 -1
  434. package/dist/apme/tuner.js +0 -297
  435. package/dist/apme/tuner.js.map +0 -1
@@ -23,6 +23,8 @@
23
23
  * zoom 2.0 → octopus ~14px wide, crayfish ~24px wide
24
24
  */
25
25
  import { worldToScreen, isVisible } from './pixoo-camera.js';
26
+ import { State, STATE_COLORS } from '@agentdeck/shared';
27
+ import { OFFICIAL_DOT_GLYPHS, OFFICIAL_DOT_GLYPH_SIZE, } from './official-dot-glyphs.generated.js';
26
28
  // ===== Cell Types (shared with ESP32/Android) =====
27
29
  const EMPTY = 0;
28
30
  const BODY = 1;
@@ -32,132 +34,324 @@ const RIGHT_ARM = 4;
32
34
  const LEFT_LEG = 5;
33
35
  const RIGHT_LEG = 6;
34
36
  const ANTENNA = 7;
35
- // ===== Octopus 13×13Pixoo64 LED pixel art (square pixels, no PIXEL_ASPECT) =====
36
- //
37
- // Designed for LED matrix: solid filled pixels, eyes as negative space (black).
38
- // No outline needed LED pixel glow provides natural edge definition.
39
- // At zoom 3.2 with cellSize = zoom/3 1.07, sprite 14×14 screen pixels.
40
- //
41
- export const OCTOPUS_GRID = [
42
- [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // flat top (9) angular
43
- [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // (11)
44
- [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // (11)
45
- [0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 0], // eyes top col 4,8 (11)
46
- [0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 0], // eyes bottom 2px vertical (11)
47
- [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4], // arms start + body (13)
48
- [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // arms thick (13) 2 rows, 2-3px wide
49
- [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // arms continue (13)
50
- [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // narrowing (9)
51
- [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // (9)
52
- [0, 0, 5, 0, 5, 0, 1, 0, 6, 0, 6, 0, 0], // tentacles + center
53
- [0, 0, 5, 0, 5, 0, 0, 0, 6, 0, 6, 0, 0], // tentacles
54
- [0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0], // outer tentacles only
37
+ // ===== Octopus HD 24×24Claude Code mascot aligned with SVG viewBox =====
38
+ export const OCTOPUS_GRID_HD = [
39
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
40
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
41
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
42
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
43
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
44
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
45
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
46
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
47
+ [0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0],
48
+ [0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0],
49
+ [0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0],
50
+ [3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4],
51
+ [3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4],
52
+ [3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4],
53
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
54
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
55
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
56
+ [0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0],
57
+ [0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0],
58
+ [0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0],
59
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
60
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
61
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
62
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
55
63
  ];
56
- const OCTO_COLS = 13;
57
- const OCTO_ROWS = 13;
64
+ const OCTO_HD_COLS = 24;
65
+ const OCTO_HD_ROWS = 24;
66
+ // ===== Octopus MD 14×13 — Claude Code boxy mascot =====
67
+ export const OCTOPUS_GRID_MD = [
68
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // Flat top (12)
69
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // (14)
70
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // (14)
71
+ [1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1], // Wide eye holes (2px)
72
+ [1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1], // Eye holes bottom
73
+ [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Arms + body (14)
74
+ [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Arms + body (14)
75
+ [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Arms + body (14)
76
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // Slightly tapered waist (12)
77
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // (12)
78
+ [0, 5, 5, 0, 5, 5, 0, 0, 6, 6, 0, 6, 6, 0], // Thick 2px legs for distortion-free downscaling
79
+ [0, 5, 5, 0, 5, 5, 0, 0, 6, 6, 0, 6, 6, 0],
80
+ [0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0]
81
+ ];
82
+ const OCTO_GRID_MD_COLS = 14;
83
+ const OCTO_GRID_MD_ROWS = 13;
84
+ export const OCTOPUS_GRID = OCTOPUS_GRID_HD; // alias for compatibility
85
+ const OCTO_COLS = OCTO_HD_COLS;
86
+ const OCTO_ROWS = OCTO_HD_ROWS;
58
87
  /** World width of octopus in normalized coords. */
59
88
  export const OCTO_WORLD_W = 7 / 64;
60
89
  // ===== Octopus LOD 7×7 — chunky grid for zoom < 1.3 =====
61
- const OCTOPUS_LOD = [
62
- [0, 1, 1, 1, 1, 1, 0], // flat top (5) — angular
63
- [1, 1, 1, 1, 1, 1, 1], // full width (7)
64
- [1, 1, 2, 1, 2, 1, 1], // eyes (7)
65
- [1, 1, 1, 1, 1, 1, 1], // body (7)
66
- [0, 1, 1, 1, 1, 1, 0], // waist (5)
67
- [0, 0, 5, 1, 6, 0, 0], // tentacles + center
68
- [0, 0, 5, 0, 6, 0, 0], // tentacle tips
90
+ export const OCTOPUS_LOD = [
91
+ [0, 1, 1, 1, 1, 1, 0],
92
+ [1, 1, 1, 1, 1, 1, 1],
93
+ [1, 2, 2, 1, 2, 2, 1], // Bold eye holes
94
+ [3, 1, 1, 1, 1, 1, 4], // Arms + body
95
+ [0, 1, 1, 1, 1, 1, 0],
96
+ [0, 5, 5, 1, 6, 6, 0], // Thick limbs
97
+ [0, 5, 0, 0, 0, 6, 0],
69
98
  ];
70
99
  const OCTO_LOD_COLS = 7;
71
100
  const OCTO_LOD_ROWS = 7;
72
- // ===== Crayfish 12×8front-facing, matches OpenClaw SVG =====
73
- //
74
- // OpenClaw SVG: rounded body, compact side claws, antennae from top,
75
- // dark eyes with teal (#00e5cc) highlights, red gradient body.
76
- // Body shape: oval (narrow top wide middle narrow bottom).
77
- //
78
- export const CRAYFISH_GRID = [
79
- [0, 0, 7, 0, 0, 0, 0, 0, 0, 7, 0, 0], // antennae tips (wide spread)
80
- [0, 0, 0, 7, 0, 0, 0, 0, 7, 0, 0, 0], // antennae shafts
81
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], // head dome top (6w rounder)
82
- [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // head + claw tips (8w body + 2+2 claws)
83
- [0, 3, 1, 1, 2, 1, 1, 2, 1, 1, 4, 0], // eyes + claw arms (8w body)
84
- [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // thorax widest (8w)
85
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], // abdomen (6w)
86
- [0, 0, 5, 0, 0, 1, 1, 0, 0, 6, 0, 0], // tail + walking legs
101
+ // ===== Crayfish HD 24×24OpenClaw mascot, round full body matching design/brand/openclaw.svg =====
102
+ // Round body, antennae curving to the top corners, two separated side-claw blobs (3/4), two leg
103
+ // stubs (5/6). Eyes are NOT in the grid drawn as a teal+black overlay at eyeRow/eyeCols below.
104
+ export const CRAYFISH_GRID_HD = [
105
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
106
+ [0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0],
107
+ [0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0],
108
+ [0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0],
109
+ [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
110
+ [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
111
+ [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
112
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
113
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
114
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], // eyes overlaid at cols 8,14
115
+ [0, 3, 3, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 4, 0],
116
+ [3, 3, 3, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 4, 4, 4],
117
+ [3, 3, 3, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 4, 4, 4],
118
+ [0, 3, 3, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 4, 0],
119
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
120
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
121
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
122
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
123
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
124
+ [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
125
+ [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
126
+ [0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 1, 1, 1, 1, 6, 6, 1, 0, 0, 0, 0, 0, 0, 0],
127
+ [0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 1, 1, 1, 1, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0],
128
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
129
+ ];
130
+ const CF_HD_COLS = 24;
131
+ const CF_HD_ROWS = 24;
132
+ // ===== Crayfish MD 12×8 — OpenClaw mascot, round body (eyes overlaid at row 3, cols 4/7) =====
133
+ export const CRAYFISH_GRID_MD = [
134
+ [0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0], // Antennae tips at the corners
135
+ [0, 0, 7, 0, 0, 1, 1, 0, 0, 7, 0, 0], // Antennae + head crown
136
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], // Rounded head dome
137
+ [0, 3, 3, 1, 1, 1, 1, 1, 1, 4, 4, 0], // Claws + body (eyes overlay row 3)
138
+ [3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Wide claws + body
139
+ [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // Thorax
140
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], // Lower body
141
+ [0, 0, 0, 5, 5, 0, 0, 6, 6, 0, 0, 0] // Walking legs
87
142
  ];
88
- const CF_COLS = 12;
89
- const CF_ROWS = 8;
90
- /** World width of crayfish in normalized coords (12 world-pixels / 64). */
143
+ const CF_GRID_MD_COLS = 12;
144
+ const CF_GRID_MD_ROWS = 8;
145
+ export const CRAYFISH_GRID = CRAYFISH_GRID_HD; // alias for compatibility
146
+ const CF_COLS = CF_HD_COLS;
147
+ const CF_ROWS = CF_HD_ROWS;
148
+ /** World width of crayfish in normalized coords. */
91
149
  export const CF_WORLD_W = 12 / 64;
92
- // ===== Crayfish LOD 8×6 — compact for zoom < 1.3 =====
93
- const CRAYFISH_LOD = [
94
- [0, 7, 0, 0, 0, 0, 7, 0], // antennae (wider spread)
95
- [0, 0, 1, 1, 1, 1, 0, 0], // head dome (4w — rounder)
96
- [3, 1, 1, 1, 1, 1, 1, 4], // claws + body (6w wider oval)
97
- [0, 1, 2, 1, 1, 2, 1, 0], // eyes at col 2,5 (spaced for 3×3 rendering)
98
- [0, 0, 1, 1, 1, 1, 0, 0], // lower body (4w)
99
- [0, 5, 0, 1, 1, 0, 6, 0], // legs + tail
150
+ // ===== Crayfish LOD 8×6 — compact round body (eyes overlaid at row 2, cols 2/5) =====
151
+ export const CRAYFISH_LOD = [
152
+ [0, 7, 0, 0, 0, 0, 7, 0], // Antennae at corners
153
+ [0, 0, 1, 1, 1, 1, 0, 0], // Head crown
154
+ [3, 3, 1, 1, 1, 1, 4, 4], // Claws + body (eyes overlay row 2)
155
+ [0, 1, 1, 1, 1, 1, 1, 0], // Body
156
+ [0, 1, 1, 1, 1, 1, 1, 0], // Body
157
+ [0, 0, 5, 0, 0, 6, 0, 0], // Legs
100
158
  ];
101
159
  const CF_LOD_COLS = 8;
102
160
  const CF_LOD_ROWS = 6;
103
- // ===== Jellyfish 10×86-lobe cloud (matches TUI/Android CloudCreature) =====
104
- //
105
- // Codex CLI creature: clover-shaped cloud with >_ terminal prompt marking.
106
- // Cell types: 0=empty, 1=body, 2=marking(>_), 3=edge(breathe/contract)
107
- //
108
- export const JELLYFISH_GRID = [
109
- [0, 0, 1, 1, 0, 0, 1, 1, 0, 0], // top lobes
110
- [0, 1, 1, 1, 1, 1, 1, 1, 1, 0], // upper merge
111
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // widest side lobes
112
- [3, 1, 2, 2, 1, 1, 2, 1, 1, 3], // >_ marking + breathe edges
113
- [3, 1, 1, 1, 1, 1, 1, 1, 1, 3], // center + breathe edges
114
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // widest
115
- [0, 1, 1, 1, 1, 1, 1, 1, 1, 0], // taper
116
- [0, 0, 1, 1, 0, 0, 1, 1, 0, 0], // bottom lobes
161
+ // ===== Jellyfish HD 24×24Codex cloud mascot aligned with SVG viewBox =====
162
+ export const JELLYFISH_GRID_HD = [
163
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
164
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
165
+ [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
166
+ [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
167
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
168
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
169
+ [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
170
+ [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
171
+ [0, 0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // prompt `>`
172
+ [0, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
173
+ [0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
174
+ [0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
175
+ [0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
176
+ [0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
177
+ [0, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
178
+ [0, 0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0], // prompt `_`
179
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0],
180
+ [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
181
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
182
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
183
+ [0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0], // bottom lobes are BREATHE_EDGE
184
+ [0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0],
185
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
186
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
187
+ ];
188
+ const JF_HD_COLS = 24;
189
+ const JF_HD_ROWS = 24;
190
+ // ===== Jellyfish MD 13×11 — Codex mascot =====
191
+ export const JELLYFISH_GRID_MD = [
192
+ [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], // Rounded top lobe
193
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
194
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
195
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
196
+ [3, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3], // `>` top stroke (2px thick)
197
+ [3, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 1, 3], // `>` point + long `_` prompt (4px)
198
+ [3, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3], // `>` bottom stroke
199
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
200
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
201
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
202
+ [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0] // Bottom cloud lobe, not dangling legs
117
203
  ];
118
- const JF_COLS = 10;
119
- const JF_ROWS = 8;
204
+ const JF_GRID_MD_COLS = 13;
205
+ const JF_GRID_MD_ROWS = 11;
206
+ export const JELLYFISH_GRID = JELLYFISH_GRID_HD; // alias for compatibility
207
+ const JF_COLS = JF_HD_COLS;
208
+ const JF_ROWS = JF_HD_ROWS;
120
209
  /** World width of jellyfish in normalized coords. */
121
- export const JF_WORLD_W = 10 / 64;
122
- // ===== Jellyfish LOD 7×5 — compact for zoom < 1.3 =====
123
- const JELLYFISH_LOD = [
124
- [0, 1, 1, 0, 1, 1, 0], // top lobes
125
- [1, 1, 1, 1, 1, 1, 1], // widest
126
- [1, 1, 2, 1, 2, 1, 1], // marking
127
- [1, 1, 1, 1, 1, 1, 1], // widest
128
- [0, 1, 1, 0, 1, 1, 0], // bottom lobes
210
+ export const JF_WORLD_W = 13 / 64;
211
+ // ===== Jellyfish LOD 7 — compact for zoom < 1.3 =====
212
+ export const JELLYFISH_LOD = [
213
+ [0, 0, 1, 1, 1, 1, 1, 0, 0],
214
+ [0, 1, 1, 1, 1, 1, 1, 1, 0],
215
+ [1, 2, 2, 1, 1, 1, 1, 1, 1], // `>` upper stroke (2px)
216
+ [1, 1, 2, 2, 1, 2, 2, 2, 1], // `>` point + `_` prompt (3px)
217
+ [1, 2, 2, 1, 1, 1, 1, 1, 1], // `>` lower stroke
218
+ [0, 1, 1, 1, 1, 1, 1, 1, 0],
219
+ [0, 0, 1, 1, 1, 1, 1, 0, 0],
129
220
  ];
130
- const JF_LOD_COLS = 7;
131
- const JF_LOD_ROWS = 5;
221
+ const JF_LOD_COLS = 9;
222
+ const JF_LOD_ROWS = 7;
132
223
  // Cell type constants for jellyfish
133
224
  const MARKING = 2;
134
225
  const BREATHE_EDGE = 3;
135
- // ===== OpenCode 10×9nested-square logo sprite (simulator SSOT) =====
226
+ // ===== OpenCode HD 24×24OpenCode mascot aligned with SVG viewBox =====
227
+ export const OPENCODE_GRID_HD = [
228
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
229
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
230
+ [0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0],
231
+ [0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0],
232
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
233
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
234
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
235
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
236
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
237
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
238
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
239
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
240
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
241
+ [0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
242
+ [0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9],
243
+ [0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9],
244
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
245
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
246
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
247
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
248
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
249
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
250
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
251
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
252
+ ];
253
+ const OC_HD_COLS = 24;
254
+ const OC_HD_ROWS = 24;
255
+ // ===== OpenCode MD 10×10 — nested-square logo =====
136
256
  const OPENCODE_FRAME = 8;
137
257
  const OPENCODE_CORE = 9;
138
- const OPENCODE_GRID = [
139
- [8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
140
- [8, 0, 0, 0, 0, 0, 0, 0, 0, 8],
141
- [8, 0, 9, 9, 9, 9, 9, 9, 0, 8],
142
- [8, 0, 9, 0, 0, 0, 0, 9, 0, 8],
143
- [8, 0, 9, 0, 0, 0, 0, 9, 0, 8],
144
- [8, 0, 9, 0, 0, 0, 0, 9, 0, 8],
145
- [8, 0, 9, 9, 9, 9, 9, 9, 0, 8],
146
- [8, 0, 0, 0, 0, 0, 0, 0, 0, 8],
147
- [8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
258
+ export const OPENCODE_GRID_MD = [
259
+ [8, 8, 8, 8, 8, 8, 0, 0, 0, 0],
260
+ [8, 0, 0, 0, 0, 8, 0, 0, 0, 0],
261
+ [8, 0, 0, 0, 0, 8, 0, 0, 0, 0],
262
+ [8, 0, 0, 0, 0, 8, 0, 0, 0, 0],
263
+ [8, 0, 0, 0, 9, 8, 9, 9, 9, 9],
264
+ [8, 8, 8, 8, 8, 8, 9, 9, 9, 9],
265
+ [0, 0, 0, 0, 9, 9, 9, 9, 9, 9],
266
+ [0, 0, 0, 0, 9, 9, 9, 9, 9, 9],
267
+ [0, 0, 0, 0, 9, 9, 9, 9, 9, 9],
268
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
148
269
  ];
149
- const OC_COLS = 10;
150
- const OC_ROWS = 9;
151
- const OPENCODE_LOD = [
152
- [8, 8, 8, 8, 8, 8],
153
- [8, 0, 0, 0, 0, 8],
154
- [8, 0, 9, 9, 0, 8],
155
- [8, 0, 9, 9, 0, 8],
156
- [8, 0, 0, 0, 0, 8],
157
- [8, 8, 8, 8, 8, 8],
270
+ const OC_GRID_MD_COLS = 10;
271
+ const OC_GRID_MD_ROWS = 10;
272
+ export const OPENCODE_GRID = OPENCODE_GRID_HD; // alias for compatibility
273
+ const OC_COLS = OC_HD_COLS;
274
+ const OC_ROWS = OC_HD_ROWS;
275
+ export const OPENCODE_LOD = [
276
+ [8, 8, 8, 8, 0, 0],
277
+ [8, 0, 0, 8, 0, 0],
278
+ [8, 0, 9, 8, 9, 9],
279
+ [8, 8, 8, 8, 9, 9],
280
+ [0, 0, 9, 9, 9, 9],
281
+ [0, 0, 9, 9, 9, 9],
158
282
  ];
159
283
  const OC_LOD_COLS = 6;
160
284
  const OC_LOD_ROWS = 6;
285
+ // ===== Antigravity — rainbow peak/arc mark =====
286
+ const ANTIGRAVITY_GRID = [
287
+ '....YOO....',
288
+ '....YOO....',
289
+ '...LYOOR...',
290
+ '...LTORR...',
291
+ '..LLTVPP...',
292
+ '..TTKKVPP..',
293
+ '.TQQK.KVU..',
294
+ '.QQK...KUU.',
295
+ 'NQK.....KUU',
296
+ 'NN.......UU',
297
+ '...........',
298
+ ];
299
+ const AG_COLS = 11;
300
+ const AG_ROWS = 11;
301
+ function selectGrid(zoom, canvasWidth, hd, hdCols, hdRows, md, mdCols, mdRows, lod, lodCols, lodRows) {
302
+ if (canvasWidth <= 32) {
303
+ if (zoom < 1.3) {
304
+ return { grid: lod, cols: lodCols, rows: lodRows };
305
+ }
306
+ else {
307
+ // Zoomed-in on a 32px display (iDotMatrix): use the full HD grid, not MD. Now that cell size
308
+ // is resolution-aware (creatureCellSize), HD fits the 32px frame (~60% width) and keeps the
309
+ // creature's true silhouette instead of the coarser MD approximation. MD/LOD stay for the
310
+ // wide (zoom<1.3) shots where the creature is small and detail would just alias.
311
+ return { grid: hd, cols: hdCols, rows: hdRows };
312
+ }
313
+ }
314
+ else {
315
+ if (zoom < 1.3) {
316
+ return { grid: md, cols: mdCols, rows: mdRows };
317
+ }
318
+ else {
319
+ return { grid: hd, cols: hdCols, rows: hdRows };
320
+ }
321
+ }
322
+ }
323
+ /**
324
+ * On-screen creature width as a fraction of (zoom × output width), independent of LOD grid.
325
+ *
326
+ * A sprite spans `SPRITE_W_FRAC × zoom × w` pixels regardless of which HD/MD/LOD grid is selected
327
+ * (cellSz = span / cols, so the `cols` term cancels). Multiplying by `w` (32 or 64) is what makes
328
+ * sizing resolution-aware — at 32px a creature is exactly half the pixels it is at 64px, so it stays
329
+ * inside the iDotMatrix frame instead of overflowing. 0.1875 reproduces the legacy 64px HD size
330
+ * (24 cols × zoom×0.5 = 12×zoom px = 0.1875 × zoom × 64). See drawOpenCode for the original pattern.
331
+ */
332
+ const SPRITE_W_FRAC = 0.1875;
333
+ /** Resolution-aware square cell size for a creature sprite.
334
+ *
335
+ * Rounded to a whole pixel so every grid cell is a uniform integer block. A
336
+ * fractional cell size makes adjacent cells round to alternating 1/2px widths,
337
+ * and on the 2×2 eye holes that intermittently opens a 1px body-coloured seam
338
+ * between the two eye rows while the creature moves — the eyes appear to "break"
339
+ * into dots. Integer cells render the eye as a solid block every frame.
340
+ *
341
+ * The sizing zoom is itself quantized to a coarse 0.25 step first. The raw zoom
342
+ * lerps continuously during camera transitions, so a cell size sitting right on
343
+ * a rounding threshold (e.g. zoom where `…/cols` ≈ x.5) flickers 1↔2px frame to
344
+ * frame — and since the eye sits at a fixed column, a cellSz change jumps it by
345
+ * `eyeCol` pixels. Snapping the zoom to discrete steps holds cellSz stable
346
+ * through a transition so the eye only ever moves by whole-creature translation. */
347
+ export function creatureCellSize(zoom, canvasW, cols) {
348
+ const zq = Math.round(zoom * 4) / 4; // 0.25 steps — kills threshold-straddle flicker
349
+ return Math.max(1, Math.round((SPRITE_W_FRAC * zq * canvasW) / cols));
350
+ }
351
+ /** Canonical `#rrggbb` → RGB tuple (LED frame buffers take tuples, not hex). */
352
+ function hexRgb(hex) {
353
+ return [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];
354
+ }
161
355
  export const COLORS = {
162
356
  // Octopus (terracotta — Android match)
163
357
  octopusBody: [0xC0, 0x70, 0x58],
@@ -181,7 +375,7 @@ export const COLORS = {
181
375
  // Jellyfish / Codex CLI (indigo — TUI/Android match)
182
376
  jellyfishBody: [0x63, 0x66, 0xF1], // indigo #6366F1
183
377
  jellyfishEdge: [0x4F, 0x46, 0xE5], // darker edge (breathe cells)
184
- jellyfishMarking: [0xA5, 0xB4, 0xFC], // glow #A5B4FC (>_ prompt)
378
+ jellyfishMarking: [0xF5, 0xF7, 0xFF], // near-white (>_ prompt)
185
379
  jellyfishGlow: [0x31, 0x33, 0x78], // dim indigo glow halo
186
380
  jellyfishPulse: [0xA5, 0xB4, 0xFC], // bioluminescent pulse
187
381
  jellyfishSleeping: [0x3A, 0x3C, 0x90], // dimmed indigo
@@ -190,6 +384,17 @@ export const COLORS = {
190
384
  opencodeInner: [0x4B, 0x46, 0x46], // dark core
191
385
  opencodePulse: [0xCF, 0xCE, 0xCD], // pulse state
192
386
  opencodeSleeping: [0x8A, 0x84, 0x84], // sleep dim
387
+ // Antigravity (reference rainbow peak/arc)
388
+ antigravityLime: [0x5C, 0xD6, 0x4D],
389
+ antigravityTeal: [0x1F, 0xC6, 0xB3],
390
+ antigravityCyan: [0x3A, 0xC7, 0xEB],
391
+ antigravityYellow: [0xF5, 0xCB, 0x24],
392
+ antigravityOrange: [0xFF, 0x84, 0x10],
393
+ antigravityRed: [0xFF, 0x52, 0x41],
394
+ antigravityPink: [0xB7, 0x5C, 0xB6],
395
+ antigravityViolet: [0x66, 0x6F, 0xE1],
396
+ antigravityBlue: [0x24, 0x7E, 0xFF],
397
+ antigravitySky: [0x29, 0xB8, 0xEE],
193
398
  // Tetra (neon)
194
399
  tetraNeon: [0x00, 0xE5, 0xFF],
195
400
  tetraBody: [0x1E, 0x40, 0xAF],
@@ -235,11 +440,11 @@ export const COLORS = {
235
440
  // Tank walls (above water)
236
441
  tankWall: [0x06, 0x0A, 0x10],
237
442
  tankWallEdge: [0x10, 0x14, 0x1C],
238
- // State shimmer
239
- stateIdle: [0x22, 0xC5, 0x5E],
240
- stateProcessing: [0x3B, 0x82, 0xF6],
241
- stateAwaiting: [0xF5, 0x9E, 0x0B],
242
- stateError: [0xEF, 0x44, 0x44],
443
+ // State shimmer — derived from the canonical shared palette
444
+ stateIdle: hexRgb(STATE_COLORS[State.IDLE]),
445
+ stateProcessing: hexRgb(STATE_COLORS[State.PROCESSING]),
446
+ stateAwaiting: hexRgb(STATE_COLORS[State.AWAITING_PERMISSION]),
447
+ stateError: [0xEF, 0x44, 0x44], // red — no canonical error state color exists
243
448
  white: [0xFF, 0xFF, 0xFF],
244
449
  black: [0x00, 0x00, 0x00],
245
450
  };
@@ -288,27 +493,45 @@ export function getOpenCodePaletteForSession(sessionIndex = 0) {
288
493
  pulse: scaleColor(COLORS.opencodePulse, tone),
289
494
  };
290
495
  }
496
+ export function getAntigravityPaletteForSession(sessionIndex = 0) {
497
+ const tone = SESSION_TONE_FACTORS[Math.max(0, Math.min(SESSION_TONE_FACTORS.length - 1, sessionIndex))];
498
+ return {
499
+ lime: scaleColor(COLORS.antigravityLime, tone),
500
+ teal: scaleColor(COLORS.antigravityTeal, tone),
501
+ cyan: scaleColor(COLORS.antigravityCyan, tone),
502
+ yellow: scaleColor(COLORS.antigravityYellow, tone),
503
+ orange: scaleColor(COLORS.antigravityOrange, tone),
504
+ red: scaleColor(COLORS.antigravityRed, tone),
505
+ pink: scaleColor(COLORS.antigravityPink, tone),
506
+ violet: scaleColor(COLORS.antigravityViolet, tone),
507
+ blue: scaleColor(COLORS.antigravityBlue, tone),
508
+ sky: scaleColor(COLORS.antigravitySky, tone),
509
+ cutout: COLORS.black,
510
+ };
511
+ }
291
512
  // ===== Pixel Operations =====
292
- /** Set a pixel in the 64×64 RGB buffer. */
513
+ /** Set a pixel in the dynamic RGB buffer. */
293
514
  export function setPixel(buf, x, y, color) {
294
- if (x < 0 || x >= 64 || y < 0 || y >= 64)
515
+ const w = Math.sqrt(buf.length / 3);
516
+ if (x < 0 || x >= w || y < 0 || y >= w)
295
517
  return;
296
518
  const ix = Math.round(x);
297
519
  const iy = Math.round(y);
298
- if (ix < 0 || ix >= 64 || iy < 0 || iy >= 64)
520
+ if (ix < 0 || ix >= w || iy < 0 || iy >= w)
299
521
  return;
300
- const idx = (iy * 64 + ix) * 3;
522
+ const idx = (iy * w + ix) * 3;
301
523
  buf[idx] = color[0];
302
524
  buf[idx + 1] = color[1];
303
525
  buf[idx + 2] = color[2];
304
526
  }
305
527
  /** Alpha-blend a pixel onto existing buffer content. */
306
528
  export function blendPixel(buf, x, y, color, alpha) {
529
+ const w = Math.sqrt(buf.length / 3);
307
530
  const ix = Math.round(x);
308
531
  const iy = Math.round(y);
309
- if (ix < 0 || ix >= 64 || iy < 0 || iy >= 64 || alpha <= 0)
532
+ if (ix < 0 || ix >= w || iy < 0 || iy >= w || alpha <= 0)
310
533
  return;
311
- const idx = (iy * 64 + ix) * 3;
534
+ const idx = (iy * w + ix) * 3;
312
535
  const a = Math.min(1, alpha);
313
536
  const inv = 1 - a;
314
537
  buf[idx] = Math.min(255, Math.round(buf[idx] * inv + color[0] * a));
@@ -317,11 +540,12 @@ export function blendPixel(buf, x, y, color, alpha) {
317
540
  }
318
541
  /** Additive-blend (glow) a pixel. */
319
542
  export function glowPixel(buf, x, y, color, intensity) {
543
+ const w = Math.sqrt(buf.length / 3);
320
544
  const ix = Math.round(x);
321
545
  const iy = Math.round(y);
322
- if (ix < 0 || ix >= 64 || iy < 0 || iy >= 64 || intensity <= 0)
546
+ if (ix < 0 || ix >= w || iy < 0 || iy >= w || intensity <= 0)
323
547
  return;
324
- const idx = (iy * 64 + ix) * 3;
548
+ const idx = (iy * w + ix) * 3;
325
549
  buf[idx] = Math.min(255, buf[idx] + Math.round(color[0] * intensity));
326
550
  buf[idx + 1] = Math.min(255, buf[idx + 1] + Math.round(color[1] * intensity));
327
551
  buf[idx + 2] = Math.min(255, buf[idx + 2] + Math.round(color[2] * intensity));
@@ -336,45 +560,52 @@ export function fillRect(buf, x, y, w, h, color) {
336
560
  }
337
561
  /** Fill a scaled cell (variable size rectangle) — edge rounding for correct PIXEL_ASPECT. */
338
562
  function fillCell(buf, x, y, w, h, color) {
339
- const ix = Math.floor(x);
340
- const iy = Math.floor(y);
341
- const iw = Math.max(1, Math.round(ix + w) - ix); // right edge - left edge
342
- const ih = Math.max(1, Math.round(iy + h) - iy);
563
+ const ix1 = Math.round(x);
564
+ const iy1 = Math.round(y);
565
+ const ix2 = Math.round(x + w);
566
+ const iy2 = Math.round(y + h);
567
+ const iw = Math.max(1, ix2 - ix1);
568
+ const ih = Math.max(1, iy2 - iy1);
343
569
  for (let dy = 0; dy < ih; dy++) {
344
570
  for (let dx = 0; dx < iw; dx++) {
345
- setPixel(buf, ix + dx, iy + dy, color);
571
+ setPixel(buf, ix1 + dx, iy1 + dy, color);
346
572
  }
347
573
  }
348
574
  }
349
575
  /** Fill a scaled cell and track drawn pixels for outline generation. */
350
576
  function fillCellTracked(buf, x, y, w, h, color, pixels) {
351
- const ix = Math.floor(x);
352
- const iy = Math.floor(y);
353
- const iw = Math.max(1, Math.round(ix + w) - ix);
354
- const ih = Math.max(1, Math.round(iy + h) - iy);
577
+ const canvasW = Math.sqrt(buf.length / 3);
578
+ const ix1 = Math.round(x);
579
+ const iy1 = Math.round(y);
580
+ const ix2 = Math.round(x + w);
581
+ const iy2 = Math.round(y + h);
582
+ const iw = Math.max(1, ix2 - ix1);
583
+ const ih = Math.max(1, iy2 - iy1);
355
584
  for (let dy = 0; dy < ih; dy++) {
356
585
  for (let dx = 0; dx < iw; dx++) {
357
- const px = ix + dx;
358
- const py = iy + dy;
359
- if (px >= 0 && px < 64 && py >= 0 && py < 64) {
360
- const idx = (py * 64 + px) * 3;
586
+ const px = ix1 + dx;
587
+ const py = iy1 + dy;
588
+ if (px >= 0 && px < canvasW && py >= 0 && py < canvasW) {
589
+ const idx = (py * canvasW + px) * 3;
361
590
  buf[idx] = color[0];
362
591
  buf[idx + 1] = color[1];
363
592
  buf[idx + 2] = color[2];
364
- pixels.add(py * 64 + px);
593
+ pixels.add(py * canvasW + px);
365
594
  }
366
595
  }
367
596
  }
368
597
  }
369
598
  /** Glow-fill a scaled cell — edge rounding. */
370
599
  function glowCell(buf, x, y, w, h, color, intensity) {
371
- const ix = Math.floor(x);
372
- const iy = Math.floor(y);
373
- const iw = Math.max(1, Math.round(ix + w) - ix);
374
- const ih = Math.max(1, Math.round(iy + h) - iy);
600
+ const ix1 = Math.round(x);
601
+ const iy1 = Math.round(y);
602
+ const ix2 = Math.round(x + w);
603
+ const iy2 = Math.round(y + h);
604
+ const iw = Math.max(1, ix2 - ix1);
605
+ const ih = Math.max(1, iy2 - iy1);
375
606
  for (let dy = 0; dy < ih; dy++) {
376
607
  for (let dx = 0; dx < iw; dx++) {
377
- glowPixel(buf, ix + dx, iy + dy, color, intensity);
608
+ glowPixel(buf, ix1 + dx, iy1 + dy, color, intensity);
378
609
  }
379
610
  }
380
611
  }
@@ -408,6 +639,7 @@ function drawCreatureGlow(buf, cx, cy, rx, ry, glowColor, intensity = 0.15, isLO
408
639
  // ===== Creature Outline =====
409
640
  /** 8-directional colored outline around tracked creature pixels. */
410
641
  function drawCreatureOutline(buf, creaturePixels, bodyColor, alpha = 0.8) {
642
+ const w = Math.sqrt(buf.length / 3);
411
643
  const outlineColor = [
412
644
  Math.round(bodyColor[0] * 0.5),
413
645
  Math.round(bodyColor[1] * 0.5),
@@ -415,17 +647,17 @@ function drawCreatureOutline(buf, creaturePixels, bodyColor, alpha = 0.8) {
415
647
  ];
416
648
  const neighbors = [-1, 0, 1];
417
649
  for (const key of creaturePixels) {
418
- const cx = key % 64;
419
- const cy = Math.floor(key / 64);
650
+ const cx = key % w;
651
+ const cy = Math.floor(key / w);
420
652
  for (const ndx of neighbors) {
421
653
  for (const ndy of neighbors) {
422
654
  if (ndx === 0 && ndy === 0)
423
655
  continue;
424
656
  const nx = cx + ndx;
425
657
  const ny = cy + ndy;
426
- if (nx < 0 || nx >= 64 || ny < 0 || ny >= 64)
658
+ if (nx < 0 || nx >= w || ny < 0 || ny >= w)
427
659
  continue;
428
- if (!creaturePixels.has(ny * 64 + nx)) {
660
+ if (!creaturePixels.has(ny * w + nx)) {
429
661
  blendPixel(buf, nx, ny, outlineColor, alpha);
430
662
  }
431
663
  }
@@ -499,26 +731,37 @@ export function drawOctopus(buf, worldX, worldY, state, animFrame, cam, palette
499
731
  if (!isVisible(worldX, worldY, cam, 0.15))
500
732
  return;
501
733
  const [scx, scy] = worldToScreen(worldX, worldY, cam);
734
+ const w = Math.sqrt(buf.length / 3);
502
735
  // LOD selection
503
- const useLOD = cam.zoom < 1.3;
504
- const grid = useLOD ? OCTOPUS_LOD : OCTOPUS_GRID;
505
- const cols = useLOD ? OCTO_LOD_COLS : OCTO_COLS;
506
- const rows = useLOD ? OCTO_LOD_ROWS : OCTO_ROWS;
507
- // Fixed 1px per cell LED pixel art, no fractional sizes, no tearing
508
- const cellSz = 1;
509
- const spriteW = cols;
510
- const spriteH = rows;
736
+ const select = selectGrid(cam.zoom, w, OCTOPUS_GRID_HD, OCTO_HD_COLS, OCTO_HD_ROWS, OCTOPUS_GRID_MD, OCTO_GRID_MD_COLS, OCTO_GRID_MD_ROWS, OCTOPUS_LOD, OCTO_LOD_COLS, OCTO_LOD_ROWS);
737
+ const grid = select.grid;
738
+ const cols = select.cols;
739
+ const rows = select.rows;
740
+ // Resolution-aware cell sizesprite spans a fixed fraction of (zoom × output width),
741
+ // so it scales down on the 32px iDotMatrix instead of overflowing the frame.
742
+ const cellSz = creatureCellSize(cam.zoom, w, cols);
743
+ const spriteW = cols * cellSz;
744
+ const spriteH = rows * cellSz;
745
+ // Snap the sprite origin to whole pixels. The cells tile from this origin via
746
+ // round(base + col*cellSz); if `base` is fractional, a swimming creature's
747
+ // sub-pixel screen drift shifts the rounding phase every frame, so each cell
748
+ // oscillates ±1px (shimmer). On the small high-contrast eye holes that reads
749
+ // as the eyes "breaking up" while the octopus moves. Rounding the origin makes
750
+ // the cell pattern frame-stable (only an integer translation changes), so the
751
+ // creature moves in clean 1px steps with rock-steady eyes.
511
752
  const baseX = Math.round(scx - spriteW / 2);
512
753
  const baseY = Math.round(scy - spriteH / 2);
513
- // Working: gentle vertical bob (integer pixels only)
754
+ // Working: gentle vertical bob (scaled by cellSz)
514
755
  const breathPx = state === 'working'
515
- ? Math.round(Math.sin(animFrame * 0.3) * 1.5)
756
+ ? Math.round(Math.sin(animFrame * 0.3) * cellSz * 1.5)
516
757
  : 0;
517
758
  // Body color by state
518
759
  const bodyColor = state === 'working' ? palette.starburst
519
760
  : state === 'sleeping' ? palette.sleeping
520
761
  : palette.body;
521
- // Draw all cells solid fill, no outline
762
+ // Track drawn pixels for outline generation
763
+ const trackedPixels = new Set();
764
+ // Draw all cells
522
765
  for (let row = 0; row < rows; row++) {
523
766
  for (let col = 0; col < cols; col++) {
524
767
  const cellType = grid[row][col];
@@ -541,16 +784,18 @@ export function drawOctopus(buf, worldX, worldY, state, animFrame, cam, palette
541
784
  // Tentacle animation only — arms stay fixed (attached to body)
542
785
  let dx = 0;
543
786
  if (state !== 'sleeping' && (cellType === LEFT_LEG || cellType === RIGHT_LEG)) {
544
- dx = Math.round(Math.sin(animFrame * 0.2 + col * 1.8) * 1.5);
787
+ dx = Math.round(Math.sin(animFrame * 0.2 + col * 1.8) * cellSz * 1.5);
545
788
  }
546
- fillCell(buf, baseX + col * cellSz + dx, baseY + row * cellSz + breathPx, cellSz, cellSz, color);
789
+ fillCellTracked(buf, baseX + col * cellSz + dx, baseY + row * cellSz + breathPx, cellSz, cellSz, color, trackedPixels);
547
790
  }
548
791
  }
792
+ // Draw outline to separate octopus from blue water (matches crayfish style)
793
+ drawCreatureOutline(buf, trackedPixels, bodyColor, 0.8);
549
794
  // "?" bubble when asking
550
795
  if (state === 'asking') {
551
796
  const bobY = Math.round(Math.sin(animFrame * 0.25));
552
797
  const bx = scx;
553
- const by = baseY - 3 + bobY;
798
+ const by = baseY - Math.round(cellSz * 3) + bobY;
554
799
  const r = 3;
555
800
  for (let dy = -r; dy <= r; dy++) {
556
801
  for (let dx = -r; dx <= r; dx++) {
@@ -569,7 +814,7 @@ export function drawOctopus(buf, worldX, worldY, state, animFrame, cam, palette
569
814
  // Starburst particles when working
570
815
  if (state === 'working') {
571
816
  const sparkPhase = animFrame * 0.35;
572
- const dist = 5 + Math.sin(animFrame * 0.25) * 3;
817
+ const dist = (5 + Math.sin(animFrame * 0.25) * 3) * cellSz;
573
818
  for (let i = 0; i < 6; i++) {
574
819
  const angle = sparkPhase + (i * Math.PI * 2 / 6);
575
820
  const sx = scx + Math.cos(angle) * dist;
@@ -589,28 +834,29 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
589
834
  if (!isVisible(worldX, worldY, cam, 0.15))
590
835
  return;
591
836
  const [scx, scy] = worldToScreen(worldX, worldY, cam);
837
+ const w = Math.sqrt(buf.length / 3);
592
838
  // LOD selection
593
- const useLOD = cam.zoom < 1.3;
594
- const grid = useLOD ? JELLYFISH_LOD : JELLYFISH_GRID;
595
- const cols = useLOD ? JF_LOD_COLS : JF_COLS;
596
- const rows = useLOD ? JF_LOD_ROWS : JF_ROWS;
597
- // Fixed 1px per cellLED pixel art
598
- const cellSz = 1;
599
- const spriteW = cols;
600
- const spriteH = rows;
601
- const baseX = Math.round(scx - spriteW / 2);
602
- const baseY = Math.round(scy - spriteH / 2);
839
+ const select = selectGrid(cam.zoom, w, JELLYFISH_GRID_HD, JF_HD_COLS, JF_HD_ROWS, JELLYFISH_GRID_MD, JF_GRID_MD_COLS, JF_GRID_MD_ROWS, JELLYFISH_LOD, JF_LOD_COLS, JF_LOD_ROWS);
840
+ const grid = select.grid;
841
+ const cols = select.cols;
842
+ const rows = select.rows;
843
+ // Resolution-aware cell size (see creatureCellSize) scales with output width.
844
+ const cellSz = creatureCellSize(cam.zoom, w, cols);
845
+ const spriteW = cols * cellSz;
846
+ const spriteH = rows * cellSz;
847
+ const baseX = scx - spriteW / 2;
848
+ const baseY = scy - spriteH / 2;
603
849
  // Bell pulse: contraction/expansion
604
850
  const pulseSpeed = state === 'working' ? 0.25 : 0.06;
605
851
  const pulsePhase = Math.sin(animFrame * pulseSpeed);
606
852
  const contracting = pulsePhase < 0;
607
- // Vertical bob when working
853
+ // Vertical bob when working (scaled by cellSz)
608
854
  const breathPx = state === 'working'
609
- ? Math.round(Math.sin(animFrame * 0.3) * 1.5)
855
+ ? Math.round(Math.sin(animFrame * 0.3) * cellSz * 1.5)
610
856
  : 0;
611
857
  // Bioluminescent body color pulse when working
612
858
  const bodyColor = state === 'working'
613
- ? lerpColor(palette.body, palette.pulse, (Math.sin(animFrame * 0.2) + 1) * 0.5)
859
+ ? lerpColor(palette.body, palette.pulse, 0.18 + ((Math.sin(animFrame * 0.2) + 1) * 0.18))
614
860
  : state === 'sleeping' ? palette.sleeping
615
861
  : palette.body;
616
862
  // >_ marking blink
@@ -646,7 +892,7 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
646
892
  if (state === 'asking') {
647
893
  const bobY = Math.round(Math.sin(animFrame * 0.25));
648
894
  const bx = scx;
649
- const by = baseY - 3 + bobY;
895
+ const by = baseY - Math.round(cellSz * 3) + bobY;
650
896
  const r = 3;
651
897
  for (let dy = -r; dy <= r; dy++) {
652
898
  for (let dx = -r; dx <= r; dx++) {
@@ -665,7 +911,7 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
665
911
  // Orbiting glow particles when working (bioluminescent)
666
912
  if (state === 'working') {
667
913
  const orbitPhase = animFrame * 0.2;
668
- const dist = 5 + Math.sin(animFrame * 0.15) * 2;
914
+ const dist = (5 + Math.sin(animFrame * 0.15) * 2) * cellSz;
669
915
  for (let i = 0; i < 4; i++) {
670
916
  const angle = orbitPhase + (i * Math.PI * 2 / 4);
671
917
  const sx = scx + Math.cos(angle) * dist;
@@ -676,48 +922,209 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
676
922
  }
677
923
  // ===== OpenCode — nested-square logo (simulator SSOT) =====
678
924
  export function drawOpenCode(buf, worldX, worldY, state, animFrame, camera, palette) {
679
- const scx = Math.round(((worldX - camera.cx) * camera.zoom + 0.5) * 64);
680
- const scy = Math.round(((worldY - camera.cy) * camera.zoom + 0.5) * 64);
925
+ const w = Math.sqrt(buf.length / 3);
926
+ const [scx, scy] = worldToScreen(worldX, worldY, camera);
927
+ // Canonical opencode mark: a single-color vertical rectangular RING (16:20) with a
928
+ // HOLLOW center — drawn procedurally so it scales cleanly to any panel (64px, 32px,
929
+ // iDotMatrix) instead of grid sprites that overflowed 32px or drew a filled shadow.
681
930
  const worldW = 10 / 64;
682
- const pixW = Math.max(1, Math.round(worldW * camera.zoom * 64 / OC_COLS));
683
- const pixH = pixW;
931
+ const unit = Math.max(1, Math.round((worldW * camera.zoom * w) / OC_GRID_MD_COLS));
932
+ const outerW = Math.max(5, 6 * unit);
933
+ const outerH = Math.max(6, Math.round(outerW * 1.25)); // slightly tall (16:20)
934
+ const thick = Math.max(1, Math.round(outerW * 0.28)); // frame thickness ~ brand 4/16
684
935
  const breathPx = state === 'working'
685
936
  ? Math.round(Math.sin(animFrame * 0.3) * 1.5)
686
937
  : state === 'idle' ? Math.round(Math.sin(animFrame * 0.08) * 0.7) : 0;
687
- const useLod = camera.zoom < 1.3;
688
- const grid = useLod ? OPENCODE_LOD : OPENCODE_GRID;
689
- const gc = useLod ? OC_LOD_COLS : OC_COLS;
690
- const gr = useLod ? OC_LOD_ROWS : OC_ROWS;
691
- const baseX = scx - Math.round((gc * pixW) / 2);
692
- const baseY = scy + breathPx - Math.round((gr * pixH) / 2);
693
- const outerColor = state === 'working'
938
+ const color = state === 'working'
694
939
  ? lerpColor(palette.outer, palette.pulse, 0.5 + Math.sin(animFrame * 0.2) * 0.5)
695
940
  : state === 'sleeping' ? palette.sleeping : palette.outer;
696
- const innerColor = state === 'sleeping'
697
- ? scaleColor(palette.inner, 0.6) : palette.inner;
698
- for (let r = 0; r < gr; r++) {
699
- for (let c = 0; c < gc; c++) {
700
- const cell = grid[r][c];
701
- if (cell === 0)
702
- continue;
703
- const color = (cell === OPENCODE_CORE) ? innerColor : outerColor;
704
- for (let dy = 0; dy < pixH; dy++) {
705
- for (let dx = 0; dx < pixW; dx++) {
706
- setPixel(buf, baseX + c * pixW + dx, baseY + r * pixH + dy, color);
707
- }
708
- }
941
+ const x0 = scx - Math.round(outerW / 2);
942
+ const y0 = scy + breathPx - Math.round(outerH / 2);
943
+ for (let dy = 0; dy < outerH; dy++) {
944
+ for (let dx = 0; dx < outerW; dx++) {
945
+ const onFrame = dx < thick || dx >= outerW - thick || dy < thick || dy >= outerH - thick;
946
+ if (!onFrame)
947
+ continue; // hollow center
948
+ setPixel(buf, x0 + dx, y0 + dy, color);
709
949
  }
710
950
  }
711
951
  if (state === 'asking') {
712
- // Amber three-dot asking indicator
713
- const dotCx = scx + Math.round(gc / 2) + 3;
714
- const dotCy = baseY - 2 + Math.round(Math.sin(animFrame * 0.26) * 1.4);
952
+ // Amber three-dot asking indicator above the mark
953
+ const dotCx = scx + Math.round(outerW / 2) + 3;
954
+ const dotCy = y0 - 2 + Math.round(Math.sin(animFrame * 0.26) * 1.4);
715
955
  for (let i = -1; i <= 1; i++) {
716
956
  blendPixel(buf, dotCx + i * 3, dotCy, COLORS.stateAwaiting, 0.75);
717
957
  blendPixel(buf, dotCx + i * 3 + 1, dotCy, COLORS.stateAwaiting, 0.45);
718
958
  }
719
959
  }
720
960
  }
961
+ function antigravityCellColor(ch, palette) {
962
+ switch (ch) {
963
+ case 'L': return palette.lime;
964
+ case 'T': return palette.teal;
965
+ case 'Q': return palette.cyan;
966
+ case 'Y': return palette.yellow;
967
+ case 'O': return palette.orange;
968
+ case 'R': return palette.red;
969
+ case 'P': return palette.pink;
970
+ case 'V': return palette.violet;
971
+ case 'U': return palette.blue;
972
+ case 'N': return palette.sky;
973
+ case 'K': return palette.cutout;
974
+ default: return null;
975
+ }
976
+ }
977
+ function drawQuestionMark(buf, x, y, color) {
978
+ setPixel(buf, x, y, color);
979
+ setPixel(buf, x + 1, y, color);
980
+ setPixel(buf, x + 1, y + 1, color);
981
+ setPixel(buf, x, y + 2, color);
982
+ setPixel(buf, x, y + 4, color);
983
+ }
984
+ /**
985
+ * Awaiting affordance: a soft white disc with an amber "?" on top.
986
+ *
987
+ * (centerX, centerY) is the CENTER of the disc. The disc is the backing that
988
+ * keeps the mark legible against mid-water blues; the glyph is the 5-pixel
989
+ * question mark above, which is the smallest form that still reads as "?"
990
+ * rather than as noise.
991
+ *
992
+ * Mirror of `drawQuestionBubble` in apple/AgentDeck/Daemon/Modules/PixooRenderer.swift —
993
+ * keep the radius, alpha, and glyph offsets identical.
994
+ */
995
+ export function drawQuestionBubble(buf, centerX, centerY) {
996
+ const r = 3;
997
+ for (let dy = -r; dy <= r; dy++) {
998
+ for (let dx = -r; dx <= r; dx++) {
999
+ if (dx * dx + dy * dy <= r * r)
1000
+ blendPixel(buf, centerX + dx, centerY + dy, COLORS.white, 0.7);
1001
+ }
1002
+ }
1003
+ // 5-px "?" centered in the disc — every pixel stays inside radius 3.
1004
+ drawQuestionMark(buf, centerX, centerY - 2, COLORS.stateAwaiting);
1005
+ }
1006
+ /**
1007
+ * Draw a canonical agent mark rasterized from design/brand/*.svg.
1008
+ *
1009
+ * Pixoo64 and iDotMatrix used to maintain independent hand-drawn creature grids;
1010
+ * the Swift daemon carried another copy. That made the smallest panels look like
1011
+ * approximations of the agents while vector/e-ink surfaces showed their real marks.
1012
+ * This renderer consumes the generated 24×24 alpha masks so both daemon families
1013
+ * preserve the official silhouette and cutouts. State remains a motion/color layer
1014
+ * around the mark, never a geometry rewrite.
1015
+ */
1016
+ export function drawOfficialDotGlyph(buf, glyph, worldX, worldY, state, animFrame, camera, sessionToneIndex = 0, sizeScale = 1.0, sick = false) {
1017
+ if (!isVisible(worldX, worldY, camera, 0.15))
1018
+ return;
1019
+ const [scx, scy] = worldToScreen(worldX, worldY, camera);
1020
+ const canvasW = Math.sqrt(buf.length / 3);
1021
+ // The 8px floor is the legibility limit of the official mask — a crowd shrink
1022
+ // is allowed to reach it but never to go under it and turn a brand mark into mush.
1023
+ const target = Math.max(8, Math.round(0.1875 * sizeScale * camera.zoom * canvasW));
1024
+ const bob = state === 'working' ? Math.round(Math.sin(animFrame * 0.28) * Math.max(1, target / 14)) : 0;
1025
+ const x0 = Math.round(scx - target / 2);
1026
+ const y0 = Math.round(scy - target / 2) + bob;
1027
+ const mask = OFFICIAL_DOT_GLYPHS[glyph];
1028
+ const octopus = getOctopusPaletteForSession(sessionToneIndex);
1029
+ const codex = getJellyfishPaletteForSession(sessionToneIndex);
1030
+ const openCode = getOpenCodePaletteForSession(sessionToneIndex);
1031
+ const processingPulse = 0.28 + 0.18 * ((Math.sin(animFrame * 0.2) + 1) / 2);
1032
+ const solidColor = () => {
1033
+ if (sick)
1034
+ return COLORS.crayfishSick;
1035
+ switch (glyph) {
1036
+ case 'claudeCode':
1037
+ return state === 'working' ? octopus.starburst : state === 'sleeping' ? octopus.sleeping : octopus.body;
1038
+ case 'codex':
1039
+ return state === 'working' ? lerpColor(codex.body, codex.pulse, processingPulse) : state === 'sleeping' ? codex.sleeping : codex.body;
1040
+ case 'openCode':
1041
+ return state === 'working' ? lerpColor(openCode.outer, openCode.pulse, processingPulse) : state === 'sleeping' ? openCode.sleeping : openCode.outer;
1042
+ case 'openClaw': {
1043
+ const base = state === 'working' ? COLORS.crayfishRouting : COLORS.crayfishBody;
1044
+ return sick ? COLORS.crayfishSick : base;
1045
+ }
1046
+ case 'antigravity':
1047
+ return COLORS.white;
1048
+ }
1049
+ };
1050
+ const base = solidColor();
1051
+ for (let dy = 0; dy < target; dy++) {
1052
+ const sy = Math.min(OFFICIAL_DOT_GLYPH_SIZE - 1, Math.floor(dy * OFFICIAL_DOT_GLYPH_SIZE / target));
1053
+ for (let dx = 0; dx < target; dx++) {
1054
+ const sx = Math.min(OFFICIAL_DOT_GLYPH_SIZE - 1, Math.floor(dx * OFFICIAL_DOT_GLYPH_SIZE / target));
1055
+ const alpha = mask[sy * OFFICIAL_DOT_GLYPH_SIZE + sx] / 255;
1056
+ if (alpha <= 0.02)
1057
+ continue;
1058
+ let color = base;
1059
+ if (glyph === 'antigravity') {
1060
+ const t = sx / (OFFICIAL_DOT_GLYPH_SIZE - 1);
1061
+ color = t < 0.25 ? lerpColor([0x5c, 0xd6, 0x4d], [0xf5, 0xcb, 0x24], t * 4)
1062
+ : t < 0.55 ? lerpColor([0xf5, 0xcb, 0x24], [0xff, 0x52, 0x41], (t - 0.25) / 0.3)
1063
+ : t < 0.78 ? lerpColor([0xff, 0x52, 0x41], [0xb7, 0x5c, 0xb6], (t - 0.55) / 0.23)
1064
+ : lerpColor([0xb7, 0x5c, 0xb6], [0x24, 0x7e, 0xff], (t - 0.78) / 0.22);
1065
+ }
1066
+ blendPixel(buf, x0 + dx, y0 + dy, color, alpha);
1067
+ }
1068
+ }
1069
+ // OpenClaw's official mark has eye cutouts too small to survive every 32px
1070
+ // camera position. Re-light the canonical eye coordinates without changing
1071
+ // the silhouette so the hardware keeps its teal OpenClaw signature.
1072
+ if (glyph === 'openClaw' && !sick) {
1073
+ const eye = COLORS.crayfishEye;
1074
+ for (const [vx, vy] of [[9.05, 7.63], [15.38, 7.63]]) {
1075
+ setPixel(buf, x0 + Math.round(vx / 24 * target), y0 + Math.round(vy / 24 * target), eye);
1076
+ }
1077
+ }
1078
+ if (state === 'asking') {
1079
+ drawQuestionBubble(buf, x0 + target + 1, y0);
1080
+ }
1081
+ else if (state === 'working') {
1082
+ const sparkle = glyph === 'antigravity' ? [0xff, 0xd8, 0x50] : lerpColor(base, COLORS.white, 0.45);
1083
+ for (let i = 0; i < 4; i++) {
1084
+ const angle = animFrame * 0.22 + i * Math.PI / 2;
1085
+ const distance = target / 2 + 2;
1086
+ setPixel(buf, Math.round(scx + Math.cos(angle) * distance), Math.round(scy + bob + Math.sin(angle) * distance), sparkle);
1087
+ }
1088
+ }
1089
+ }
1090
+ export function drawAntigravity(buf, worldX, worldY, state, animFrame, camera, palette) {
1091
+ if (!isVisible(worldX, worldY, camera, 0.15))
1092
+ return;
1093
+ const [scx, scy] = worldToScreen(worldX, worldY, camera);
1094
+ const w = Math.sqrt(buf.length / 3);
1095
+ const cellSz = creatureCellSize(camera.zoom, w, AG_COLS);
1096
+ const spriteW = AG_COLS * cellSz;
1097
+ const spriteH = AG_ROWS * cellSz;
1098
+ const breathPx = state === 'working'
1099
+ ? Math.round(Math.sin(animFrame * 0.28) * cellSz)
1100
+ : state === 'idle' ? Math.round(Math.sin(animFrame * 0.08) * 0.5) : 0;
1101
+ const cameraNudgeX = state === 'working' && ((animFrame >> 3) & 1) ? cellSz : 0;
1102
+ const cameraNudgeY = state !== 'idle' && ((animFrame >> 2) & 1) ? -cellSz : 0;
1103
+ const baseX = Math.round(scx - spriteW / 2) + cameraNudgeX;
1104
+ const baseY = Math.round(scy - spriteH / 2) + breathPx + cameraNudgeY;
1105
+ const trackedPixels = new Set();
1106
+ for (let row = 0; row < AG_ROWS; row++) {
1107
+ const line = ANTIGRAVITY_GRID[row];
1108
+ for (let col = 0; col < AG_COLS; col++) {
1109
+ const color = antigravityCellColor(line[col], palette);
1110
+ if (!color)
1111
+ continue;
1112
+ fillCellTracked(buf, baseX + col * cellSz, baseY + row * cellSz, cellSz, cellSz, color, trackedPixels);
1113
+ }
1114
+ }
1115
+ drawCreatureOutline(buf, trackedPixels, palette.violet, 0.45);
1116
+ if (state === 'asking') {
1117
+ drawQuestionMark(buf, Math.round(scx + spriteW * 0.48), Math.round(baseY + spriteH * 0.18), COLORS.stateAwaiting);
1118
+ }
1119
+ if (state === 'working') {
1120
+ const sparkle = lerpColor(palette.yellow, COLORS.white, 0.35);
1121
+ const dist = Math.max(2, cellSz * 3);
1122
+ for (let i = 0; i < 4; i++) {
1123
+ const t = animFrame * 0.22 + i * Math.PI / 2;
1124
+ setPixel(buf, Math.round(scx + Math.cos(t) * dist), Math.round(baseY - 1 + Math.sin(t) * dist * 0.35), sparkle);
1125
+ }
1126
+ }
1127
+ }
721
1128
  /**
722
1129
  * Draw crayfish scaled by camera zoom.
723
1130
  * @param worldX Crayfish center in normalized world X (0~1)
@@ -727,14 +1134,16 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
727
1134
  if (!isVisible(worldX, worldY, cam, 0.15))
728
1135
  return;
729
1136
  const [scx, scy] = worldToScreen(worldX, worldY, cam);
1137
+ const w = Math.sqrt(buf.length / 3);
730
1138
  // LOD selection — matches octopus threshold
731
- const useLOD = cam.zoom < 1.3;
732
- const grid = useLOD ? CRAYFISH_LOD : CRAYFISH_GRID;
733
- const cols = useLOD ? CF_LOD_COLS : CF_COLS;
734
- const rows = useLOD ? CF_LOD_ROWS : CF_ROWS;
735
- // Cell size: detail cellW = Z, LOD cellW = 12*Z/8 = 1.5*Z (bigger cells for fewer cols)
736
- const cellW = useLOD ? cam.zoom * 1.5 : cam.zoom;
737
- const cellH = cellW * 1.5;
1139
+ const select = selectGrid(cam.zoom, w, CRAYFISH_GRID_HD, CF_HD_COLS, CF_HD_ROWS, CRAYFISH_GRID_MD, CF_GRID_MD_COLS, CF_GRID_MD_ROWS, CRAYFISH_LOD, CF_LOD_COLS, CF_LOD_ROWS);
1140
+ const grid = select.grid;
1141
+ const cols = select.cols;
1142
+ const rows = select.rows;
1143
+ // Resolution-aware square cells (see creatureCellSize). Square (cellH = cellW) the old
1144
+ // cellH = cellW*1.5 stretched the round OpenClaw body into a tall pointed shape.
1145
+ const cellW = creatureCellSize(cam.zoom, w, cols);
1146
+ const cellH = cellW;
738
1147
  const spriteW = cols * cellW;
739
1148
  const spriteH = rows * cellH;
740
1149
  const baseX = scx - spriteW / 2;
@@ -752,7 +1161,7 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
752
1161
  // 1. Glow halo (before creature — stronger for LOD)
753
1162
  const glowRx = spriteW / 2 + 2;
754
1163
  const glowRy = spriteH / 2 + 2;
755
- drawCreatureGlow(buf, scx, scy + breathPx, glowRx, glowRy, COLORS.crayfishGlow, 0.12, useLOD);
1164
+ drawCreatureGlow(buf, scx, scy + breathPx, glowRx, glowRy, COLORS.crayfishGlow, 0.12, cols === 8);
756
1165
  // 2. Draw cells with pixel tracking
757
1166
  const trackedPixels = new Set();
758
1167
  for (let row = 0; row < rows; row++) {
@@ -769,7 +1178,7 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
769
1178
  const spd = routing ? 0.35 : 0.15;
770
1179
  const wiggle = Math.sin(animFrame * spd + col * 3) * cellW * 1.5;
771
1180
  // Random-ish twitch: sharp spike every ~30 frames
772
- const twitch = ((animFrame + col * 17) % 60) < 4 ? cellW * 2 * (col < 6 ? -1 : 1) : 0;
1181
+ const twitch = ((animFrame + col * 17) % 60) < 4 ? cellW * 2 * (col < cols / 2 ? -1 : 1) : 0;
773
1182
  dx = ensureMinAmplitude(wiggle + twitch, 1);
774
1183
  }
775
1184
  // Claw animation — routing: slower, wider clap; idle: gentle sway
@@ -800,11 +1209,11 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
800
1209
  }
801
1210
  // 3. Colored outline — moderate alpha for LOD
802
1211
  const bodyColor = routing ? COLORS.crayfishRouting : COLORS.crayfishBody;
803
- const outlineAlpha = useLOD ? 0.6 : 0.8;
1212
+ const outlineAlpha = cols === 8 ? 0.6 : 0.8;
804
1213
  drawCreatureOutline(buf, trackedPixels, bodyColor, outlineAlpha);
805
1214
  // Fixed 3×3 eyes (teal center + black ring) — drawn at grid eye positions
806
- const eyeRow = useLOD ? 2 : 3;
807
- const eyeCols = useLOD ? [2, 5] : [4, 7];
1215
+ const eyeRow = cols === 24 ? 9 : (cols === 8 ? 2 : 3);
1216
+ const eyeCols = cols === 24 ? [8, 14] : (cols === 8 ? [2, 5] : [4, 7]);
808
1217
  const sickEyeCenter = [0x44, 0x66, 0x60]; // dim teal for sick
809
1218
  const eyeCenter = sick ? sickEyeCenter : COLORS.crayfishEye;
810
1219
  const eyeRing = sick ? [0x44, 0x33, 0x33] : COLORS.crayfishEyeRing;
@@ -851,7 +1260,8 @@ export function drawTetra(buf, worldX, worldY, heading, cam) {
851
1260
  if (!isVisible(worldX, worldY, cam, 0.08))
852
1261
  return;
853
1262
  const [sx, sy] = worldToScreen(worldX, worldY, cam);
854
- const px = cam.zoom; // pixel scale
1263
+ const w = Math.sqrt(buf.length / 3);
1264
+ const px = cam.zoom * (w / 64); // resolution-aware pixel scale (half-size on 32px iDotMatrix)
855
1265
  // Body (2px-equivalent)
856
1266
  const bw = Math.max(1, Math.round(px * 2));
857
1267
  const bh = Math.max(1, Math.round(px));
@@ -887,6 +1297,7 @@ const PIXEL_FONT = {
887
1297
  '8': [0b111, 0b101, 0b111, 0b101, 0b111],
888
1298
  '9': [0b111, 0b101, 0b111, 0b001, 0b111],
889
1299
  '%': [0b101, 0b001, 0b010, 0b100, 0b101],
1300
+ '/': [0b001, 0b001, 0b010, 0b100, 0b100],
890
1301
  'h': [0b100, 0b100, 0b111, 0b101, 0b101],
891
1302
  'm': [0b101, 0b111, 0b101, 0b101, 0b101], // two pillars joined — distinct from 'n'
892
1303
  'd': [0b001, 0b001, 0b011, 0b101, 0b011], // lowercase d — for day units (e.g. "6d22h")