@sirenodeck/sirenodeck 0.1.1-alpha.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 (485) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/bin/_wrapper-shared.js +42 -0
  4. package/bin/dev.js +95 -0
  5. package/bin/sirenodeck.js +42 -0
  6. package/config/default-config.yml +117 -0
  7. package/config/starter-config.yml +54 -0
  8. package/dist/main.mjs +64702 -0
  9. package/package.json +140 -0
  10. package/src/action/executor.ts +156 -0
  11. package/src/action/index.ts +7 -0
  12. package/src/addon/api-types.ts +15 -0
  13. package/src/addon/api.ts +461 -0
  14. package/src/addon/check-runner.ts +84 -0
  15. package/src/addon/index.ts +5 -0
  16. package/src/addon/loader.ts +441 -0
  17. package/src/addon/registry.ts +230 -0
  18. package/src/addon/spec.ts +69 -0
  19. package/src/api/addon.ts +33 -0
  20. package/src/api/asset-url.ts +24 -0
  21. package/src/api/index.ts +50 -0
  22. package/src/api/protocol-internal.ts +656 -0
  23. package/src/api/protocol.ts +2 -0
  24. package/src/api/react/index.ts +11 -0
  25. package/src/api/react/registry.ts +91 -0
  26. package/src/api/react/use-addon-channel.ts +26 -0
  27. package/src/api/react/use-button-state.tsx +23 -0
  28. package/src/api/react/use-deck.ts +24 -0
  29. package/src/assets/logo.png +0 -0
  30. package/src/assets/logo72x72.png +0 -0
  31. package/src/assets/logoFull.png +0 -0
  32. package/src/builtin-addons/brightness/README.md +51 -0
  33. package/src/builtin-addons/brightness/buttons/brightness/backend.ts +13 -0
  34. package/src/builtin-addons/brightness/buttons/brightness/config.ts +10 -0
  35. package/src/builtin-addons/brightness/buttons/brightness/frontend.tsx +50 -0
  36. package/src/builtin-addons/brightness/domain/macos.ts +46 -0
  37. package/src/builtin-addons/brightness/index.d.ts +5 -0
  38. package/src/builtin-addons/brightness/index.ts +20 -0
  39. package/src/builtin-addons/brightness/poller.ts +27 -0
  40. package/src/builtin-addons/brightness/providers/darwin.ts +107 -0
  41. package/src/builtin-addons/brightness/providers/index.ts +81 -0
  42. package/src/builtin-addons/brightness/providers/linux.ts +108 -0
  43. package/src/builtin-addons/brightness/providers/windows.ts +77 -0
  44. package/src/builtin-addons/brightness/sirenodeck.json +6 -0
  45. package/src/builtin-addons/coding-agents/assets/claude-code.svg +1 -0
  46. package/src/builtin-addons/coding-agents/assets/opencode-dark-square.svg +1 -0
  47. package/src/builtin-addons/coding-agents/buttons/agent/backend.ts +82 -0
  48. package/src/builtin-addons/coding-agents/buttons/agent/config.ts +13 -0
  49. package/src/builtin-addons/coding-agents/buttons/agent/frontend.tsx +130 -0
  50. package/src/builtin-addons/coding-agents/buttons/agent/pages.tsx +150 -0
  51. package/src/builtin-addons/coding-agents/buttons/summary/backend.ts +48 -0
  52. package/src/builtin-addons/coding-agents/buttons/summary/config.ts +11 -0
  53. package/src/builtin-addons/coding-agents/buttons/summary/frontend.tsx +254 -0
  54. package/src/builtin-addons/coding-agents/decks/agents.ts +69 -0
  55. package/src/builtin-addons/coding-agents/global/backend.ts +402 -0
  56. package/src/builtin-addons/coding-agents/global-entry.ts +7 -0
  57. package/src/builtin-addons/coding-agents/hook/script.ts +123 -0
  58. package/src/builtin-addons/coding-agents/index.ts +15 -0
  59. package/src/builtin-addons/coding-agents/manifest.ts +115 -0
  60. package/src/builtin-addons/coding-agents/providers/claude-code.ts +470 -0
  61. package/src/builtin-addons/coding-agents/providers/claude-instances.ts +184 -0
  62. package/src/builtin-addons/coding-agents/providers/opencode-instances.ts +108 -0
  63. package/src/builtin-addons/coding-agents/providers/opencode.ts +636 -0
  64. package/src/builtin-addons/coding-agents/providers/registry.ts +120 -0
  65. package/src/builtin-addons/coding-agents/providers/spawn.ts +112 -0
  66. package/src/builtin-addons/coding-agents/shared/claude-hook-events.ts +55 -0
  67. package/src/builtin-addons/coding-agents/shared/claude-status.ts +236 -0
  68. package/src/builtin-addons/coding-agents/shared/live-count.ts +39 -0
  69. package/src/builtin-addons/coding-agents/shared/notifier.ts +58 -0
  70. package/src/builtin-addons/coding-agents/shared/opencode-status.ts +82 -0
  71. package/src/builtin-addons/coding-agents/shared/snapshot.ts +66 -0
  72. package/src/builtin-addons/coding-agents/shared/state.ts +72 -0
  73. package/src/builtin-addons/coding-agents/sirenodeck.json +6 -0
  74. package/src/builtin-addons/coding-agents/types/types.ts +33 -0
  75. package/src/builtin-addons/core/README.md +89 -0
  76. package/src/builtin-addons/core/buttons/action/backend.ts +7 -0
  77. package/src/builtin-addons/core/buttons/action/config.ts +13 -0
  78. package/src/builtin-addons/core/buttons/action/frontend.tsx +16 -0
  79. package/src/builtin-addons/core/buttons/change-deck/backend.ts +18 -0
  80. package/src/builtin-addons/core/buttons/change-deck/config.ts +15 -0
  81. package/src/builtin-addons/core/buttons/change-deck/frontend.tsx +18 -0
  82. package/src/builtin-addons/core/buttons/media-sample/backend.ts +8 -0
  83. package/src/builtin-addons/core/buttons/media-sample/config.ts +7 -0
  84. package/src/builtin-addons/core/buttons/media-sample/frontend.tsx +2 -0
  85. package/src/builtin-addons/core/buttons/page-nav/backend.ts +28 -0
  86. package/src/builtin-addons/core/buttons/page-nav/config.ts +9 -0
  87. package/src/builtin-addons/core/buttons/page-nav/frontend.tsx +68 -0
  88. package/src/builtin-addons/core/buttons/toggle/backend.ts +119 -0
  89. package/src/builtin-addons/core/buttons/toggle/config.ts +57 -0
  90. package/src/builtin-addons/core/buttons/toggle/frontend.tsx +57 -0
  91. package/src/builtin-addons/core/buttons/toggle/global-service.ts +174 -0
  92. package/src/builtin-addons/core/index.ts +120 -0
  93. package/src/builtin-addons/core/sirenodeck.json +6 -0
  94. package/src/builtin-addons/date-time/README.md +73 -0
  95. package/src/builtin-addons/date-time/buttons/analog-clock/backend.ts +10 -0
  96. package/src/builtin-addons/date-time/buttons/analog-clock/config.ts +4 -0
  97. package/src/builtin-addons/date-time/buttons/analog-clock/frontend.tsx +88 -0
  98. package/src/builtin-addons/date-time/buttons/custom/backend.ts +10 -0
  99. package/src/builtin-addons/date-time/buttons/custom/config.ts +9 -0
  100. package/src/builtin-addons/date-time/buttons/custom/frontend.tsx +26 -0
  101. package/src/builtin-addons/date-time/buttons/date/backend.ts +10 -0
  102. package/src/builtin-addons/date-time/buttons/date/config.ts +10 -0
  103. package/src/builtin-addons/date-time/buttons/date/frontend.tsx +42 -0
  104. package/src/builtin-addons/date-time/buttons/time/backend.ts +10 -0
  105. package/src/builtin-addons/date-time/buttons/time/config.ts +9 -0
  106. package/src/builtin-addons/date-time/buttons/time/frontend.tsx +29 -0
  107. package/src/builtin-addons/date-time/index.d.ts +5 -0
  108. package/src/builtin-addons/date-time/index.ts +30 -0
  109. package/src/builtin-addons/date-time/poller.ts +11 -0
  110. package/src/builtin-addons/date-time/shared/format-date.ts +36 -0
  111. package/src/builtin-addons/date-time/shared/format.ts +105 -0
  112. package/src/builtin-addons/date-time/shared/use-now.ts +10 -0
  113. package/src/builtin-addons/date-time/sirenodeck.json +6 -0
  114. package/src/builtin-addons/emoji-selector/README.md +45 -0
  115. package/src/builtin-addons/emoji-selector/assets/launcher.svg +10 -0
  116. package/src/builtin-addons/emoji-selector/assets/smileys.svg +8 -0
  117. package/src/builtin-addons/emoji-selector/buttons/category/backend.ts +14 -0
  118. package/src/builtin-addons/emoji-selector/buttons/category/config.ts +8 -0
  119. package/src/builtin-addons/emoji-selector/buttons/category/frontend.tsx +19 -0
  120. package/src/builtin-addons/emoji-selector/buttons/emoji/backend.ts +7 -0
  121. package/src/builtin-addons/emoji-selector/buttons/emoji/config.ts +9 -0
  122. package/src/builtin-addons/emoji-selector/buttons/emoji/frontend.tsx +11 -0
  123. package/src/builtin-addons/emoji-selector/buttons/launcher/backend.ts +13 -0
  124. package/src/builtin-addons/emoji-selector/buttons/launcher/config.ts +6 -0
  125. package/src/builtin-addons/emoji-selector/buttons/launcher/frontend.tsx +31 -0
  126. package/src/builtin-addons/emoji-selector/data/categories.ts +1084 -0
  127. package/src/builtin-addons/emoji-selector/decks/index.ts +137 -0
  128. package/src/builtin-addons/emoji-selector/index.d.ts +5 -0
  129. package/src/builtin-addons/emoji-selector/index.ts +29 -0
  130. package/src/builtin-addons/emoji-selector/sirenodeck.json +6 -0
  131. package/src/builtin-addons/emoji-selector/support.ts +49 -0
  132. package/src/builtin-addons/index.ts +13 -0
  133. package/src/builtin-addons/internal-settings/README.md +26 -0
  134. package/src/builtin-addons/internal-settings/backend.ts +58 -0
  135. package/src/builtin-addons/internal-settings/buttons/app-info/backend.ts +7 -0
  136. package/src/builtin-addons/internal-settings/buttons/app-info/config.ts +13 -0
  137. package/src/builtin-addons/internal-settings/buttons/app-info/frontend.tsx +18 -0
  138. package/src/builtin-addons/internal-settings/buttons/brightness/common/frontend.tsx +50 -0
  139. package/src/builtin-addons/internal-settings/buttons/brightness-down/backend.ts +16 -0
  140. package/src/builtin-addons/internal-settings/buttons/brightness-down/config.ts +6 -0
  141. package/src/builtin-addons/internal-settings/buttons/brightness-down/frontend.tsx +8 -0
  142. package/src/builtin-addons/internal-settings/buttons/brightness-up/backend.ts +16 -0
  143. package/src/builtin-addons/internal-settings/buttons/brightness-up/config.ts +6 -0
  144. package/src/builtin-addons/internal-settings/buttons/brightness-up/frontend.tsx +8 -0
  145. package/src/builtin-addons/internal-settings/decks/settings.ts +25 -0
  146. package/src/builtin-addons/internal-settings/index.d.ts +4 -0
  147. package/src/builtin-addons/internal-settings/index.ts +53 -0
  148. package/src/builtin-addons/internal-settings/logo72x72.png +0 -0
  149. package/src/builtin-addons/internal-settings/sirenodeck.json +6 -0
  150. package/src/builtin-addons/media/README.md +45 -0
  151. package/src/builtin-addons/media/backend.ts +143 -0
  152. package/src/builtin-addons/media/buttons/media-player/backend.ts +19 -0
  153. package/src/builtin-addons/media/buttons/media-player/components/MediaStatus.tsx +25 -0
  154. package/src/builtin-addons/media/buttons/media-player/components/MediaSurface.tsx +52 -0
  155. package/src/builtin-addons/media/buttons/media-player/components/ProgressBar.tsx +21 -0
  156. package/src/builtin-addons/media/buttons/media-player/components/status-meta.ts +36 -0
  157. package/src/builtin-addons/media/buttons/media-player/config.ts +4 -0
  158. package/src/builtin-addons/media/buttons/media-player/frontend.tsx +29 -0
  159. package/src/builtin-addons/media/buttons/volume/common/frontend.tsx +52 -0
  160. package/src/builtin-addons/media/buttons/volume/mute/backend.ts +13 -0
  161. package/src/builtin-addons/media/buttons/volume/mute/config.ts +4 -0
  162. package/src/builtin-addons/media/buttons/volume/mute/frontend.tsx +26 -0
  163. package/src/builtin-addons/media/buttons/volume/volume-down/backend.ts +14 -0
  164. package/src/builtin-addons/media/buttons/volume/volume-down/config.ts +6 -0
  165. package/src/builtin-addons/media/buttons/volume/volume-down/frontend.tsx +10 -0
  166. package/src/builtin-addons/media/buttons/volume/volume-up/backend.ts +14 -0
  167. package/src/builtin-addons/media/buttons/volume/volume-up/config.ts +6 -0
  168. package/src/builtin-addons/media/buttons/volume/volume-up/frontend.tsx +8 -0
  169. package/src/builtin-addons/media/checks.ts +104 -0
  170. package/src/builtin-addons/media/executor.ts +36 -0
  171. package/src/builtin-addons/media/index.d.ts +5 -0
  172. package/src/builtin-addons/media/index.ts +53 -0
  173. package/src/builtin-addons/media/progress.ts +16 -0
  174. package/src/builtin-addons/media/providers/darwin.ts +391 -0
  175. package/src/builtin-addons/media/providers/index.ts +27 -0
  176. package/src/builtin-addons/media/providers/linux.ts +196 -0
  177. package/src/builtin-addons/media/providers/types.ts +48 -0
  178. package/src/builtin-addons/media/providers/windows.ts +121 -0
  179. package/src/builtin-addons/media/sirenodeck.json +6 -0
  180. package/src/builtin-addons/media/state.ts +19 -0
  181. package/src/builtin-addons/register-builtins.ts +65 -0
  182. package/src/builtin-addons/session/README.md +14 -0
  183. package/src/builtin-addons/session/buttons/session-info/backend.ts +8 -0
  184. package/src/builtin-addons/session/buttons/session-info/config.ts +4 -0
  185. package/src/builtin-addons/session/buttons/session-info/frontend.tsx +2 -0
  186. package/src/builtin-addons/session/buttons/time/backend.ts +7 -0
  187. package/src/builtin-addons/session/buttons/time/config.ts +6 -0
  188. package/src/builtin-addons/session/buttons/time/frontend.tsx +2 -0
  189. package/src/builtin-addons/session/index.d.ts +5 -0
  190. package/src/builtin-addons/session/index.ts +18 -0
  191. package/src/builtin-addons/session/sirenodeck.json +6 -0
  192. package/src/builtin-addons/system-status/README.md +169 -0
  193. package/src/builtin-addons/system-status/buttons/_shared.ts +131 -0
  194. package/src/builtin-addons/system-status/buttons/system-status/frontend.tsx +224 -0
  195. package/src/builtin-addons/system-status/buttons/system-status/schemas.ts +67 -0
  196. package/src/builtin-addons/system-status/domain/catalog.ts +53 -0
  197. package/src/builtin-addons/system-status/domain/chart-sampler.ts +58 -0
  198. package/src/builtin-addons/system-status/domain/display-metrics.ts +128 -0
  199. package/src/builtin-addons/system-status/domain/index.ts +10 -0
  200. package/src/builtin-addons/system-status/domain/live-metrics.ts +1021 -0
  201. package/src/builtin-addons/system-status/domain/metric-ids.ts +0 -0
  202. package/src/builtin-addons/system-status/index.ts +16 -0
  203. package/src/builtin-addons/system-status/manifest.ts +105 -0
  204. package/src/builtin-addons/system-status/shared/metrics-catalog.ts +316 -0
  205. package/src/builtin-addons/system-status/sirenodeck.json +6 -0
  206. package/src/builtin-addons/test-buildin/decks/action-buttons-test.ts +56 -0
  207. package/src/builtin-addons/test-buildin/index.d.ts +5 -0
  208. package/src/builtin-addons/test-buildin/index.ts +16 -0
  209. package/src/builtin-addons/value-display/README.md +59 -0
  210. package/src/builtin-addons/value-display/backend.ts +86 -0
  211. package/src/builtin-addons/value-display/buttons/value-display/backend.ts +17 -0
  212. package/src/builtin-addons/value-display/buttons/value-display/config.ts +36 -0
  213. package/src/builtin-addons/value-display/buttons/value-display/frontend.tsx +54 -0
  214. package/src/builtin-addons/value-display/index.d.ts +5 -0
  215. package/src/builtin-addons/value-display/index.ts +23 -0
  216. package/src/builtin-addons/value-display/sirenodeck.json +6 -0
  217. package/src/builtin-addons/weather/README.md +46 -0
  218. package/src/builtin-addons/weather/backend.ts +121 -0
  219. package/src/builtin-addons/weather/buttons/weather/backend.ts +27 -0
  220. package/src/builtin-addons/weather/buttons/weather/config.ts +29 -0
  221. package/src/builtin-addons/weather/buttons/weather/frontend.tsx +63 -0
  222. package/src/builtin-addons/weather/buttons/weather/icons.tsx +88 -0
  223. package/src/builtin-addons/weather/buttons/weather/pages.tsx +109 -0
  224. package/src/builtin-addons/weather/domain/codes.ts +35 -0
  225. package/src/builtin-addons/weather/domain/fetch.ts +47 -0
  226. package/src/builtin-addons/weather/index.d.ts +5 -0
  227. package/src/builtin-addons/weather/index.ts +23 -0
  228. package/src/builtin-addons/weather/poller.ts +70 -0
  229. package/src/builtin-addons/weather/provider/city-key.ts +9 -0
  230. package/src/builtin-addons/weather/provider/codes.ts +35 -0
  231. package/src/builtin-addons/weather/provider/fetch.ts +97 -0
  232. package/src/builtin-addons/weather/provider/geocode.ts +67 -0
  233. package/src/builtin-addons/weather/provider/types.ts +42 -0
  234. package/src/builtin-addons/weather/sirenodeck.json +6 -0
  235. package/src/cli/coding-agents-onboarding.ts +434 -0
  236. package/src/cli/commands/addon-decks.ts +441 -0
  237. package/src/cli/commands/addon-registry.ts +639 -0
  238. package/src/cli/commands/emulator-mode.ts +289 -0
  239. package/src/cli/commands/install.ts +128 -0
  240. package/src/cli/commands/logs.ts +74 -0
  241. package/src/cli/commands/network-bind.ts +111 -0
  242. package/src/cli/commands/pipeline/helpers.ts +75 -0
  243. package/src/cli/commands/pipeline/preflight.ts +20 -0
  244. package/src/cli/commands/port-identity.ts +152 -0
  245. package/src/cli/commands/reload.ts +37 -0
  246. package/src/cli/commands/restart.ts +139 -0
  247. package/src/cli/commands/run.ts +2270 -0
  248. package/src/cli/commands/service/install.ts +121 -0
  249. package/src/cli/commands/service/render-template.ts +158 -0
  250. package/src/cli/commands/service-manager.ts +184 -0
  251. package/src/cli/commands/spawn-daemon.ts +251 -0
  252. package/src/cli/commands/start.ts +1061 -0
  253. package/src/cli/commands/status.ts +110 -0
  254. package/src/cli/commands/stop.ts +121 -0
  255. package/src/cli/commands/subprocess-supervisor.ts +203 -0
  256. package/src/cli/commands/system-requirements.ts +699 -0
  257. package/src/cli/cwd.ts +28 -0
  258. package/src/cli/first-run.ts +149 -0
  259. package/src/cli/http-server.ts +244 -0
  260. package/src/cli/index.ts +355 -0
  261. package/src/cli/install-root.ts +26 -0
  262. package/src/cli/main.ts +194 -0
  263. package/src/cli/package-manager.ts +175 -0
  264. package/src/cli/probe-deps.ts +45 -0
  265. package/src/cli/prompt.ts +22 -0
  266. package/src/cli/startup-display.ts +452 -0
  267. package/src/config/builtin-icons.ts +57 -0
  268. package/src/config/config-diff.ts +54 -0
  269. package/src/config/discovery.ts +64 -0
  270. package/src/config/icon-resolver.ts +90 -0
  271. package/src/config/include-resolver.ts +202 -0
  272. package/src/config/index.ts +9 -0
  273. package/src/config/loader.ts +187 -0
  274. package/src/config/mutation.ts +514 -0
  275. package/src/config/reference-expander.ts +84 -0
  276. package/src/config/schemas.ts +223 -0
  277. package/src/config/validation.ts +254 -0
  278. package/src/core/asset-id.ts +21 -0
  279. package/src/core/gesture-state.ts +344 -0
  280. package/src/core/icon-asset-registry.ts +127 -0
  281. package/src/core/icon-source.ts +86 -0
  282. package/src/core/index.ts +30 -0
  283. package/src/core/mime.ts +16 -0
  284. package/src/core/pagination.ts +90 -0
  285. package/src/core/pub-sub.ts +126 -0
  286. package/src/core/settings.ts +153 -0
  287. package/src/core/store.ts +88 -0
  288. package/src/core/watcher.ts +73 -0
  289. package/src/deck/addon-handler-bridge.ts +516 -0
  290. package/src/deck/deck-config.ts +271 -0
  291. package/src/deck/editor-surfaces.ts +87 -0
  292. package/src/deck/host-context.ts +18 -0
  293. package/src/deck/index.ts +102 -0
  294. package/src/deck/macro-parse.ts +182 -0
  295. package/src/deck/methods.ts +348 -0
  296. package/src/deck/paginate-deck.ts +98 -0
  297. package/src/deck/position-buttons.ts +57 -0
  298. package/src/deck/runtime/index.ts +5 -0
  299. package/src/deck/runtime/runtime.ts +1060 -0
  300. package/src/deck/runtime-subscriptions.ts +45 -0
  301. package/src/deck/system-back-injection.ts +62 -0
  302. package/src/deck/system-buttons/registry.tsx +97 -0
  303. package/src/deck/system-buttons/types.ts +14 -0
  304. package/src/device/black-frame.ts +73 -0
  305. package/src/device/index.ts +13 -0
  306. package/src/device/linux-udev.ts +36 -0
  307. package/src/device/models.ts +89 -0
  308. package/src/device/reconnecting.ts +193 -0
  309. package/src/device/registry.ts +38 -0
  310. package/src/device/stream-deck.ts +155 -0
  311. package/src/index.ts +74 -0
  312. package/src/outputClient/emulator.ts +357 -0
  313. package/src/outputClient/index.ts +26 -0
  314. package/src/outputClient/open-browser.ts +26 -0
  315. package/src/outputClient/real.ts +393 -0
  316. package/src/outputClient/types.ts +83 -0
  317. package/src/render/browser-renderer.ts +240 -0
  318. package/src/render/buffer-hash.ts +20 -0
  319. package/src/render/editor-handler.ts +197 -0
  320. package/src/render/emulator-server.ts +128 -0
  321. package/src/render/icon-source-resolver.ts +69 -0
  322. package/src/render/index.ts +16 -0
  323. package/src/render/protocol.ts +61 -0
  324. package/src/render/push-raw-image.ts +100 -0
  325. package/src/render/screenshot-cadence.ts +87 -0
  326. package/src/render/state-publisher.ts +110 -0
  327. package/src/render/vite-server.ts +138 -0
  328. package/src/render/ws-bridge.ts +315 -0
  329. package/src/system/device-selection.ts +80 -0
  330. package/src/system/glob-match.ts +74 -0
  331. package/src/system/providers/active-app/darwin.ts +117 -0
  332. package/src/system/providers/active-app/linux.ts +245 -0
  333. package/src/system/providers/active-app/wayland-gnome.ts +303 -0
  334. package/src/system/providers/active-app/windows.ts +94 -0
  335. package/src/system/providers/active-app.ts +97 -0
  336. package/src/system/providers/clipboard/darwin.ts +65 -0
  337. package/src/system/providers/clipboard/linux.ts +163 -0
  338. package/src/system/providers/clipboard/windows.ts +94 -0
  339. package/src/system/providers/clipboard.ts +84 -0
  340. package/src/system/providers/error.ts +16 -0
  341. package/src/system/providers/key-macro/darwin.ts +221 -0
  342. package/src/system/providers/key-macro/linux.ts +651 -0
  343. package/src/system/providers/key-macro/parser.ts +215 -0
  344. package/src/system/providers/key-macro/windows.ts +476 -0
  345. package/src/system/providers/key-macro.ts +79 -0
  346. package/src/system/providers/notification/darwin.ts +36 -0
  347. package/src/system/providers/notification/linux.ts +115 -0
  348. package/src/system/providers/notification/windows.ts +72 -0
  349. package/src/system/providers/notification.ts +75 -0
  350. package/src/system/providers/session/darwin.ts +82 -0
  351. package/src/system/providers/session/linux.ts +175 -0
  352. package/src/system/providers/session/windows.ts +73 -0
  353. package/src/system/providers/session.ts +89 -0
  354. package/src/system/providers/shared.ts +65 -0
  355. package/src/system/requirements.ts +235 -0
  356. package/src/system/setup-wizard/config-seed.ts +30 -0
  357. package/src/system/setup-wizard/format.ts +275 -0
  358. package/src/system/setup-wizard/index.ts +45 -0
  359. package/src/system/setup-wizard/open-settings.ts +26 -0
  360. package/src/system/setup-wizard/plan.ts +230 -0
  361. package/src/system/setup-wizard/probe-cache.ts +34 -0
  362. package/src/system/setup-wizard/probe.ts +486 -0
  363. package/src/system/setup-wizard/runtime-features.ts +85 -0
  364. package/src/system/setup-wizard/sudo.ts +178 -0
  365. package/src/system/setup-wizard/types.ts +133 -0
  366. package/src/system/virtual-stream-deck.ts +67 -0
  367. package/src/themes/css.ts +210 -0
  368. package/src/themes/default/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  369. package/src/themes/default/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  370. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
  371. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf +0 -0
  372. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf +0 -0
  373. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf +0 -0
  374. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Italic.ttf +0 -0
  375. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Light.ttf +0 -0
  376. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf +0 -0
  377. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
  378. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf +0 -0
  379. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
  380. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf +0 -0
  381. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf +0 -0
  382. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Thin.ttf +0 -0
  383. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf +0 -0
  384. package/src/themes/default/assets/IBM_Plex_Mono/OFL.txt +93 -0
  385. package/src/themes/default/assets/OFL.txt +93 -0
  386. package/src/themes/default/assets/README.txt +106 -0
  387. package/src/themes/default/assets/static/IBMPlexSans-Bold.ttf +0 -0
  388. package/src/themes/default/assets/static/IBMPlexSans-BoldItalic.ttf +0 -0
  389. package/src/themes/default/assets/static/IBMPlexSans-ExtraLight.ttf +0 -0
  390. package/src/themes/default/assets/static/IBMPlexSans-ExtraLightItalic.ttf +0 -0
  391. package/src/themes/default/assets/static/IBMPlexSans-Italic.ttf +0 -0
  392. package/src/themes/default/assets/static/IBMPlexSans-Light.ttf +0 -0
  393. package/src/themes/default/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
  394. package/src/themes/default/assets/static/IBMPlexSans-Medium.ttf +0 -0
  395. package/src/themes/default/assets/static/IBMPlexSans-MediumItalic.ttf +0 -0
  396. package/src/themes/default/assets/static/IBMPlexSans-Regular.ttf +0 -0
  397. package/src/themes/default/assets/static/IBMPlexSans-SemiBold.ttf +0 -0
  398. package/src/themes/default/assets/static/IBMPlexSans-SemiBoldItalic.ttf +0 -0
  399. package/src/themes/default/assets/static/IBMPlexSans-Thin.ttf +0 -0
  400. package/src/themes/default/assets/static/IBMPlexSans-ThinItalic.ttf +0 -0
  401. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Bold.ttf +0 -0
  402. package/src/themes/default/assets/static/IBMPlexSans_Condensed-BoldItalic.ttf +0 -0
  403. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLight.ttf +0 -0
  404. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLightItalic.ttf +0 -0
  405. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Italic.ttf +0 -0
  406. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Light.ttf +0 -0
  407. package/src/themes/default/assets/static/IBMPlexSans_Condensed-LightItalic.ttf +0 -0
  408. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Medium.ttf +0 -0
  409. package/src/themes/default/assets/static/IBMPlexSans_Condensed-MediumItalic.ttf +0 -0
  410. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Regular.ttf +0 -0
  411. package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBold.ttf +0 -0
  412. package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBoldItalic.ttf +0 -0
  413. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Thin.ttf +0 -0
  414. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ThinItalic.ttf +0 -0
  415. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Bold.ttf +0 -0
  416. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-BoldItalic.ttf +0 -0
  417. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLight.ttf +0 -0
  418. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLightItalic.ttf +0 -0
  419. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Italic.ttf +0 -0
  420. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Light.ttf +0 -0
  421. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-LightItalic.ttf +0 -0
  422. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Medium.ttf +0 -0
  423. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-MediumItalic.ttf +0 -0
  424. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Regular.ttf +0 -0
  425. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBold.ttf +0 -0
  426. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  427. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Thin.ttf +0 -0
  428. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ThinItalic.ttf +0 -0
  429. package/src/themes/default/components.css +81 -0
  430. package/src/themes/default/sirenodeck.json +170 -0
  431. package/src/themes/index.ts +14 -0
  432. package/src/themes/light/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  433. package/src/themes/light/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  434. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
  435. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
  436. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
  437. package/src/themes/light/assets/IBM_Plex_Mono/OFL.txt +93 -0
  438. package/src/themes/light/assets/OFL.txt +93 -0
  439. package/src/themes/light/assets/README.txt +106 -0
  440. package/src/themes/light/assets/static/IBMPlexSans-Light.ttf +0 -0
  441. package/src/themes/light/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
  442. package/src/themes/light/assets/static/IBMPlexSans-Thin.ttf +0 -0
  443. package/src/themes/light/components.css +70 -0
  444. package/src/themes/light/sirenodeck.json +170 -0
  445. package/src/themes/loader.ts +343 -0
  446. package/src/themes/manifest.ts +164 -0
  447. package/src/themes/use-resolved-theme.tsx +32 -0
  448. package/src/ui/ButtonFrame.tsx +230 -0
  449. package/src/ui/contexts/AssetCacheContext.tsx +43 -0
  450. package/src/ui/index.ts +20 -0
  451. package/src/ui/primitives/Chip.tsx +53 -0
  452. package/src/ui/primitives/Icon.tsx +209 -0
  453. package/src/ui/primitives/Label.tsx +82 -0
  454. package/src/ui/primitives/ProgressBar.tsx +38 -0
  455. package/src/ui/primitives/TapIndicator.tsx +67 -0
  456. package/src/ui/primitives/Text.tsx +810 -0
  457. package/src/ui/primitives/index.ts +6 -0
  458. package/src/ui/surfaces/BarsSurface.tsx +211 -0
  459. package/src/ui/surfaces/IconLabelProgressSurface.tsx +133 -0
  460. package/src/ui/surfaces/IconLabelSurface.tsx +59 -0
  461. package/src/ui/surfaces/LabelValueListSurface.tsx +202 -0
  462. package/src/ui/surfaces/PaginatedSurface.tsx +164 -0
  463. package/src/ui/surfaces/SplitActionSurface.tsx +98 -0
  464. package/src/ui/surfaces/TemporaryErrorSurface.tsx +68 -0
  465. package/src/ui/surfaces/ValueChart.tsx +218 -0
  466. package/src/ui/surfaces/index.ts +32 -0
  467. package/src/ui/surfaces/paginated-schema.ts +22 -0
  468. package/src/ui/theme-presentation-builder.ts +184 -0
  469. package/src/ui/theme-presentation.tsx +146 -0
  470. package/src/ui/utils/cn.ts +12 -0
  471. package/src/util/cache-paths.ts +38 -0
  472. package/src/util/config-diff.ts +16 -0
  473. package/src/util/daemon-control.ts +85 -0
  474. package/src/util/daemon.ts +575 -0
  475. package/src/util/device-config.ts +56 -0
  476. package/src/util/errors.ts +13 -0
  477. package/src/util/log-reader.ts +114 -0
  478. package/src/util/log-tail.ts +103 -0
  479. package/src/util/logger.ts +417 -0
  480. package/src/util/process-title.ts +79 -0
  481. package/src/util/single-instance.ts +202 -0
  482. package/src/version.ts +10 -0
  483. package/src/vite/index.ts +6 -0
  484. package/src/vite/virtual-modules.ts +631 -0
  485. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,516 @@
1
+ import type pino from "pino"
2
+
3
+ import type { ActionExecutor } from "@/action/executor"
4
+ import type {
5
+ AddonServiceContext,
6
+ AddonServiceMethod,
7
+ AddonButtonService,
8
+ AddonButtonServiceContext,
9
+ AddonButtonTypeService,
10
+ AddonGlobalService,
11
+ AddonGlobalPoller,
12
+ } from "@/addon/api"
13
+ import type { ScannedAddon } from "@/cli/commands/addon-registry"
14
+ import { BUILTIN_MANIFESTS } from "@/builtin-addons/register-builtins"
15
+ import type { PubSub } from "@/core/pub-sub"
16
+ import type { Store } from "@/core/store"
17
+ import type { Methods } from "@/deck/methods"
18
+ import type { Runtime, RuntimeDeck } from "@/deck/runtime"
19
+ import type { StatePublisher } from "@/render/state-publisher"
20
+ import type { WsBridge } from "@/render/ws-bridge"
21
+
22
+ export interface BridgeAddonServicesParams {
23
+ readonly runtime: Runtime
24
+ readonly decks: ReadonlyArray<RuntimeDeck>
25
+ readonly scanned: ReadonlyArray<ScannedAddon>
26
+ // ponytail: third-party (registry-loaded) addons that supply buttonTypes
27
+ // and/or globalService. Wired identically to built-ins; only the module
28
+ // path differs.
29
+ readonly externalAddons: ReadonlyArray<ScannedAddon>
30
+ readonly executor: ActionExecutor
31
+ readonly pubSub: PubSub
32
+ readonly store: Store
33
+ readonly signal: AbortSignal
34
+ readonly logger: pino.Logger
35
+ readonly statePublisher: Pick<StatePublisher, "registerChannel">
36
+ readonly bridge: Pick<WsBridge, "broadcast" | "registerCacheablePoller">
37
+ readonly methods: Methods
38
+ /** Optional host callback that re-materializes addon decks (dynamic decks). */
39
+ readonly requestDeckRebuild?: () => void
40
+ }
41
+
42
+ type AddonModule = {
43
+ readonly default?: unknown
44
+ readonly manifest?: unknown
45
+ }
46
+
47
+ const namespacedKey = (addonName: string, methodName: string): string =>
48
+ `${addonName}:${methodName}`
49
+
50
+ /**
51
+ * A builtin's module without going through the filesystem.
52
+ *
53
+ * ponytail: importing a builtin by path is the one thing that cannot work in a
54
+ * published install — the path points at TypeScript source that uses `@/`
55
+ * aliases, so plain node refuses it and every builtin button loses its backend.
56
+ * The manifests are compiled into the bundle already, so prefer the object.
57
+ * A third-party addon is still loaded from disk, which is correct: it is not
58
+ * part of this package and has a real, loadable entry file.
59
+ */
60
+ const staticAddonModule = (addonName: string): AddonModule | null => {
61
+ const manifest = BUILTIN_MANIFESTS.get(addonName)
62
+ return manifest === undefined ? null : ({ manifest } as AddonModule)
63
+ }
64
+
65
+ export interface AddonBridgeHandle {
66
+ dispose(): void
67
+ }
68
+
69
+ export const bridgeAddonServices = async (
70
+ params: BridgeAddonServicesParams,
71
+ ): Promise<AddonBridgeHandle> => {
72
+ const {
73
+ runtime,
74
+ decks,
75
+ scanned,
76
+ externalAddons,
77
+ executor,
78
+ pubSub,
79
+ store,
80
+ signal,
81
+ statePublisher,
82
+ bridge,
83
+ methods,
84
+ requestDeckRebuild,
85
+ } = params
86
+ const logger = params.logger.child({ component: "addon-handler" })
87
+
88
+ // ponytail: built-in and third-party addons wire through the same loop;
89
+ // built-ins come from the static registry, third-parties from the loader.
90
+ const allAddons: ReadonlyArray<ScannedAddon> = [...scanned, ...externalAddons]
91
+
92
+ runtime.setGestureListener((buttonId, event) => {
93
+ bridge.broadcast({
94
+ type: "state",
95
+ channels: { [`runtime:gesture:${buttonId}`]: event },
96
+ })
97
+ })
98
+
99
+ const abortController = new AbortController()
100
+ signal.addEventListener("abort", () => abortController.abort())
101
+
102
+ const registeredHandlerIds = new Set<string>()
103
+ const trackedCleanup: Array<() => void> = []
104
+
105
+ const addonModules = new Map<string, AddonModule>()
106
+ const addonGlobalServices = new Map<string, AddonGlobalService>()
107
+ const addonMethods = new Map<
108
+ string,
109
+ Readonly<Record<string, AddonServiceMethod>>
110
+ >()
111
+
112
+ for (const addon of allAddons) {
113
+ if (addon.globalServiceEntry === null) continue
114
+
115
+ try {
116
+ const mod =
117
+ staticAddonModule(addon.name) ??
118
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
+ ((await import(addon.globalServiceEntry)) as AddonModule)
120
+ let globalService: AddonGlobalService | undefined
121
+ if (
122
+ typeof (mod as unknown as { globalService?: AddonGlobalService })
123
+ .globalService === "object"
124
+ ) {
125
+ globalService = (
126
+ mod as unknown as { globalService: AddonGlobalService }
127
+ ).globalService
128
+ } else {
129
+ const exported =
130
+ mod.manifest ??
131
+ (mod.default && typeof mod.default === "object" ? mod.default : null)
132
+ if (exported === null) continue
133
+ const manifest = exported as {
134
+ readonly name?: string
135
+ readonly globalService?: AddonGlobalService
136
+ }
137
+ globalService = manifest.globalService
138
+ }
139
+ if (globalService === undefined) continue
140
+
141
+ addonModules.set(addon.name, mod)
142
+ addonGlobalServices.set(addon.name, globalService)
143
+ } catch (err) {
144
+ // ponytail: this used to swallow the error silently. When run.ts handed
145
+ // the daemon an addon's BROWSER bundle, every import here threw on an
146
+ // unresolvable host-UI specifier and the addon simply vanished — no
147
+ // global service, no handlers, no log line, and a deck button that did
148
+ // nothing when tapped. Whatever the cause, say so.
149
+ logger.error(
150
+ { addonName: addon.name, entry: addon.globalServiceEntry, err },
151
+ "addon global service failed to load",
152
+ )
153
+ }
154
+ }
155
+
156
+ for (const [addonName, globalService] of addonGlobalServices) {
157
+ const primaryChannel = globalService.pollers?.[0]?.channel
158
+ const pollersById = new Map<string, AddonGlobalPoller>()
159
+ for (const poller of globalService.pollers ?? []) {
160
+ pollersById.set(poller.id, poller)
161
+ }
162
+
163
+ const ctx: AddonServiceContext = {
164
+ publish: (data: unknown) => {
165
+ if (primaryChannel !== undefined) {
166
+ bridge.broadcast({
167
+ type: "state",
168
+ channels: { [primaryChannel]: data },
169
+ })
170
+ pubSub.publish("runtime:invalidate", undefined)
171
+ } else {
172
+ pubSub.publish(`addon:${addonName}`, data)
173
+ }
174
+ },
175
+ poll: async (id: string) => {
176
+ const poller = pollersById.get(id)
177
+ if (poller === undefined) return
178
+ try {
179
+ const value = await poller.poll(ctx)
180
+ bridge.broadcast({
181
+ type: "state",
182
+ channels: { [poller.channel]: value },
183
+ })
184
+ } catch (err) {
185
+ logger.error({ addonName, id, err }, `addon poll failed`)
186
+ }
187
+ },
188
+ signal: abortController.signal,
189
+ executor,
190
+ notify: methods.notify,
191
+ ...(requestDeckRebuild !== undefined ? { requestDeckRebuild } : {}),
192
+ }
193
+
194
+ if (globalService.pollers !== undefined) {
195
+ for (const poller of globalService.pollers) {
196
+ statePublisher.registerChannel({
197
+ channel: poller.channel,
198
+ addonName,
199
+ intervalMs: poller.intervalMs,
200
+ poll: () => poller.poll(ctx),
201
+ })
202
+ bridge.registerCacheablePoller(poller.channel, () => poller.poll(ctx))
203
+ }
204
+ }
205
+
206
+ try {
207
+ const result = globalService.onLoad?.(ctx)
208
+ if (result instanceof Promise) {
209
+ result.catch((err) => {
210
+ logger.error({ addonName, err }, `addon onLoad failed`)
211
+ })
212
+ }
213
+
214
+ if (globalService.methods !== undefined) {
215
+ addonMethods.set(addonName, globalService.methods)
216
+ }
217
+ } catch (err) {
218
+ logger.error({ addonName, err }, `addon onLoad threw`)
219
+ }
220
+
221
+ // ponytail: `subscriptions` has been part of the addon API since it was
222
+ // written (api.ts documents them as "push-based sources — file watchers,
223
+ // sockets") but nothing ever invoked them. coding-agents declares one, and
224
+ // its ClaudeCodeProvider fills its agent map ONLY inside subscribe() — so
225
+ // the provider reported zero Claude Code sessions forever, on a machine
226
+ // with eight of them open. Pollers were wired; subscriptions never were.
227
+ for (const subscription of globalService.subscriptions ?? []) {
228
+ try {
229
+ const handle = subscription.subscribe(ctx)
230
+ trackedCleanup.push(() => {
231
+ try {
232
+ handle.unsubscribe()
233
+ } catch (err) {
234
+ logger.error(
235
+ { addonName, channel: subscription.channel, err },
236
+ `addon subscription unsubscribe failed`,
237
+ )
238
+ }
239
+ })
240
+ } catch (err) {
241
+ logger.error(
242
+ { addonName, channel: subscription.channel, err },
243
+ `addon subscription failed to start`,
244
+ )
245
+ }
246
+ }
247
+ }
248
+
249
+ const deckButtonCleanup = new Map<
250
+ string,
251
+ Array<{
252
+ buttonAbort: AbortController
253
+ buttonService: AddonButtonService
254
+ wrappedCtx: AddonButtonServiceContext<unknown>
255
+ }>
256
+ >()
257
+
258
+ for (const deck of decks) {
259
+ for (const button of deck.buttons) {
260
+ const buttonType = button.type
261
+
262
+ let addonName: string | null = null
263
+ let resolvedButtonType: string | null = null
264
+ for (const addon of allAddons) {
265
+ if (addon.types.includes(buttonType)) {
266
+ addonName = addon.name
267
+ resolvedButtonType = buttonType
268
+ break
269
+ }
270
+ if (
271
+ typeof addon.defaultButton === "string" &&
272
+ addon.name === buttonType
273
+ ) {
274
+ addonName = addon.name
275
+ resolvedButtonType = addon.defaultButton
276
+ break
277
+ }
278
+ }
279
+
280
+ if (addonName === null || resolvedButtonType === null) continue
281
+
282
+ const globalMethods = addonMethods.get(addonName) ?? {}
283
+ const buttonMethods: Record<string, AddonServiceMethod> = {}
284
+ for (const [methodName, method] of Object.entries(globalMethods)) {
285
+ buttonMethods[namespacedKey(addonName, methodName)] = method
286
+ }
287
+
288
+ let addonMod: AddonModule | null = null
289
+ for (const addon of allAddons) {
290
+ if (addon.name !== addonName) continue
291
+ if (addon.frontendEntry === null) continue
292
+ try {
293
+ addonMod =
294
+ staticAddonModule(addon.name) ??
295
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
296
+ ((await import(addon.frontendEntry)) as AddonModule)
297
+ } catch (err) {
298
+ // ponytail: silence here meant a failed import surfaced only as a
299
+ // button that ignored taps. See the matching catch above.
300
+ logger.error(
301
+ { addonName, entry: addon.frontendEntry, err },
302
+ "addon button handlers failed to load",
303
+ )
304
+ }
305
+ break
306
+ }
307
+
308
+ const exported = addonMod
309
+ ? (addonMod.manifest ??
310
+ (addonMod.default && typeof addonMod.default === "object"
311
+ ? addonMod.default
312
+ : null))
313
+ : null
314
+
315
+ if (exported === null) continue
316
+
317
+ const manifest = exported as {
318
+ readonly name?: string
319
+ readonly buttonTypes?: Record<
320
+ string,
321
+ {
322
+ readonly service?: AddonButtonTypeService & AddonButtonService
323
+ }
324
+ >
325
+ }
326
+
327
+ const buttonTypeEntry = manifest.buttonTypes?.[resolvedButtonType]
328
+ if (buttonTypeEntry?.service === undefined) continue
329
+
330
+ const buttonService = buttonTypeEntry.service
331
+ const buttonCtx: AddonButtonServiceContext<unknown> = {
332
+ config: button.config ?? {},
333
+ buttonId: button.id,
334
+ ...(button.position !== undefined ? { position: button.position } : {}),
335
+ addonName,
336
+ methods: Object.freeze(buttonMethods),
337
+ coreMethods: methods,
338
+ publish: (channel: string, data: unknown) =>
339
+ pubSub.publish(channel, data),
340
+ executor,
341
+ signal: abortController.signal,
342
+ store,
343
+ }
344
+
345
+ const buttonAbort = new AbortController()
346
+ abortController.signal.addEventListener("abort", () =>
347
+ buttonAbort.abort(),
348
+ )
349
+
350
+ const wrappedCtx = {
351
+ ...buttonCtx,
352
+ signal: buttonAbort.signal,
353
+ }
354
+
355
+ const existing = deckButtonCleanup.get(deck.id) ?? []
356
+ existing.push({ buttonAbort, buttonService, wrappedCtx })
357
+ deckButtonCleanup.set(deck.id, existing)
358
+
359
+ try {
360
+ buttonService.onMount?.(wrappedCtx)
361
+ } catch (err) {
362
+ logger.error({ addonName, buttonType, err }, `addon onMount threw`)
363
+ }
364
+
365
+ const allowedGestures = buttonService.gestureHandlers
366
+ const handler = {
367
+ async onTap(ctx: {
368
+ buttonId: string
369
+ config: unknown
370
+ gesture: string
371
+ }) {
372
+ if (allowedGestures === undefined || !allowedGestures.includes("tap"))
373
+ return
374
+ try {
375
+ await buttonService.onTap?.(wrappedCtx)
376
+ } catch (err) {
377
+ logger.error(
378
+ { addonName, buttonType: resolvedButtonType, err },
379
+ `addon onTap failed`,
380
+ )
381
+ }
382
+ },
383
+ async onDblTap(ctx: {
384
+ buttonId: string
385
+ config: unknown
386
+ gesture: string
387
+ }) {
388
+ if (
389
+ allowedGestures === undefined ||
390
+ !allowedGestures.includes("dbl-tap")
391
+ )
392
+ return
393
+ try {
394
+ await buttonService.onDblTap?.(wrappedCtx)
395
+ } catch (err) {
396
+ logger.error(
397
+ { addonName, buttonType: resolvedButtonType, err },
398
+ `addon onDblTap failed`,
399
+ )
400
+ }
401
+ },
402
+ async onHold(ctx: {
403
+ buttonId: string
404
+ config: unknown
405
+ gesture: string
406
+ }) {
407
+ if (
408
+ allowedGestures === undefined ||
409
+ !allowedGestures.includes("hold")
410
+ )
411
+ return
412
+ try {
413
+ await buttonService.onHold?.(wrappedCtx)
414
+ } catch (err) {
415
+ logger.error(
416
+ { addonName, buttonType: resolvedButtonType, err },
417
+ `addon onHold failed`,
418
+ )
419
+ }
420
+ },
421
+ dispose() {
422
+ buttonAbort.abort()
423
+ try {
424
+ buttonService.dispose?.(wrappedCtx)
425
+ } catch (err) {
426
+ logger.error({ addonName, buttonType, err }, `addon dispose failed`)
427
+ }
428
+ },
429
+ }
430
+
431
+ runtime.registerButtonHandler(`${deck.id}:${button.id}`, handler)
432
+ registeredHandlerIds.add(`${deck.id}:${button.id}`)
433
+ }
434
+ }
435
+
436
+ const unsubscribeDeckInactive = pubSub.subscribe(
437
+ "runtime:deck-inactive",
438
+ (payload: unknown) => {
439
+ if (
440
+ typeof payload !== "object" ||
441
+ payload === null ||
442
+ !("deckId" in payload)
443
+ ) {
444
+ return
445
+ }
446
+ const deckId = String((payload as { deckId: unknown }).deckId)
447
+ const tracked = deckButtonCleanup.get(deckId)
448
+ if (tracked === undefined) return
449
+ for (const { buttonAbort, buttonService, wrappedCtx } of tracked) {
450
+ try {
451
+ buttonService.onUnmount?.(wrappedCtx)
452
+ } catch (err) {
453
+ logger.error({ deckId, err }, `bridge onUnmount failed`)
454
+ }
455
+ buttonAbort.abort()
456
+ }
457
+ },
458
+ )
459
+ trackedCleanup.push(unsubscribeDeckInactive)
460
+
461
+ abortController.signal.addEventListener("abort", () => {
462
+ for (const [addonName, globalService] of addonGlobalServices) {
463
+ try {
464
+ const ctx: AddonServiceContext = {
465
+ publish: () => {},
466
+ poll: async () => {},
467
+ signal: abortController.signal,
468
+ executor,
469
+ notify: methods.notify,
470
+ }
471
+ globalService.onUnload?.(ctx)
472
+ } catch (err) {
473
+ logger.error({ addonName, err }, `addon onUnload failed`)
474
+ }
475
+ }
476
+ })
477
+
478
+ let disposed = false
479
+ const dispose = (): void => {
480
+ if (disposed) return
481
+ disposed = true
482
+ for (const cleanup of trackedCleanup) cleanup()
483
+ for (const handlerId of registeredHandlerIds) {
484
+ runtime.unregisterButtonHandler(handlerId)
485
+ }
486
+ registeredHandlerIds.clear()
487
+ for (const tracked of deckButtonCleanup.values()) {
488
+ for (const { buttonAbort, buttonService, wrappedCtx } of tracked) {
489
+ try {
490
+ buttonService.onUnmount?.(wrappedCtx)
491
+ } catch (err) {
492
+ logger.error({ err }, `bridge onUnmount failed`)
493
+ }
494
+ buttonAbort.abort()
495
+ }
496
+ }
497
+ deckButtonCleanup.clear()
498
+ for (const [addonName, globalService] of addonGlobalServices) {
499
+ try {
500
+ const ctx: AddonServiceContext = {
501
+ publish: () => {},
502
+ poll: async () => {},
503
+ signal: abortController.signal,
504
+ executor,
505
+ notify: methods.notify,
506
+ }
507
+ globalService.onUnload?.(ctx)
508
+ } catch (err) {
509
+ logger.error({ addonName, err }, `addon onUnload failed`)
510
+ }
511
+ }
512
+ abortController.abort()
513
+ }
514
+
515
+ return { dispose }
516
+ }