@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,348 @@
1
+ import type pino from "pino"
2
+
3
+ import type { PubSub } from "@/core/pub-sub"
4
+ import type { Store } from "@/core/store"
5
+ import type { KeyMacroProvider } from "@/system/providers/key-macro"
6
+ import type {
7
+ NotificationArgs,
8
+ NotificationProvider,
9
+ } from "@/system/providers/notification"
10
+ import {
11
+ type RequirementsCheckResult,
12
+ type SystemCapability,
13
+ } from "@/system/requirements"
14
+ import {
15
+ isValidKey,
16
+ knownKeys,
17
+ parseCombo,
18
+ type ParsedCombo,
19
+ } from "@/system/providers/key-macro/parser"
20
+ import { NotImplementedError } from "@/util/errors"
21
+
22
+ import type { ActionExecutor, ActionExecutorOptions } from "@/action/executor"
23
+ import type { Runtime, RuntimeDeck } from "./runtime"
24
+ import { dispatchMacro, resolvePlatformMacro } from "./macro-parse"
25
+
26
+ const DEFAULT_BUTTON_ERROR_DURATION_MS = 5000
27
+
28
+ export interface KeyMacroAction {
29
+ kind: "key" | "combo" | "text"
30
+ value: string
31
+ modifiers?: ReadonlyArray<"shift" | "ctrl" | "alt" | "meta" | "cmd">
32
+ }
33
+
34
+ export interface MethodsContext {
35
+ runtime: Runtime
36
+ pubSub: PubSub
37
+ store: Store
38
+ executor: ActionExecutor
39
+ logger: pino.Logger
40
+ keyMacroProvider?: KeyMacroProvider
41
+ notificationProvider?: NotificationProvider
42
+ }
43
+
44
+ export interface Methods {
45
+ runCommand(
46
+ command: string,
47
+ options?: ActionExecutorOptions,
48
+ ): Promise<{
49
+ stdout: string
50
+ stderr: string
51
+ exitCode: number
52
+ durationMs: number
53
+ }>
54
+ /**
55
+ * Send a key or combo via the key-macro provider. Equivalent to the OS-level
56
+ * keystroke — Linux uses `wtype`, macOS uses `osascript keystroke`, Windows
57
+ * uses Win32 `SendInput`. Accepts both combos (`ctrl+shift+t`) and plain
58
+ * UTF-8 text (including emoji).
59
+ */
60
+ keyMacro(action: KeyMacroAction): Promise<void>
61
+ /**
62
+ * Convenience wrapper for addons that want to type literal text without
63
+ * constructing a `KeyMacroAction`. Passes straight through to the
64
+ * key-macro provider; the provider is responsible for the actual keystroke
65
+ * injection, so UTF-8 / emoji work everywhere a key-macro tool is
66
+ * available.
67
+ */
68
+ typeText(text: string): Promise<void>
69
+ navigateToDeck(args: { id: string; addToHistory?: boolean }): void
70
+ goBack(): void
71
+ getActiveDeckId(): string
72
+ invalidate(): void
73
+ publish<T>(channel: string, payload: T): void
74
+ subscribe<T>(channel: string, cb: (payload: T) => void): () => void
75
+ setKeyMacroProvider(provider: KeyMacroProvider): void
76
+ /**
77
+ * Fire an OS notification. Falls back to a no-op (logged at warn) when no
78
+ * notification provider is configured for this platform. Used by addons
79
+ * via `coreMethods.notify`.
80
+ */
81
+ notify(args: NotificationArgs): Promise<void>
82
+ setNotificationProvider(provider: NotificationProvider): void
83
+ setRequirements(requirements: RequirementsCheckResult): void
84
+ checkRequirement(capability: SystemCapability): boolean
85
+ showTemporaryError(
86
+ deckId: string,
87
+ position: number,
88
+ durationMs?: number,
89
+ buttonId?: string,
90
+ details?: string,
91
+ ): void
92
+ adjustBrightness(args: { direction: "up" | "down" }): void
93
+ dispatch(value: string): Promise<void>
94
+ /**
95
+ * Ask the host to re-materialize the addon decks (used when a dynamic deck's
96
+ * shape depends on live state, e.g. session counts). The host replaces the
97
+ * runtime deck set and rebroadcasts deck-config. Set via `setDeckRebuilder`.
98
+ */
99
+ requestDeckRebuild(): void
100
+ setDeckRebuilder(rebuild: () => void): void
101
+ }
102
+
103
+ export const createMethods = (ctx: MethodsContext): Methods => {
104
+ let keyMacroProvider: KeyMacroProvider | undefined = ctx.keyMacroProvider
105
+ let notificationProvider: NotificationProvider | undefined =
106
+ ctx.notificationProvider
107
+ const logger = ctx.logger.child({ component: "methods" })
108
+ const setKeyMacroProvider: Methods["setKeyMacroProvider"] = (provider) => {
109
+ keyMacroProvider = provider
110
+ }
111
+ const setNotificationProvider: Methods["setNotificationProvider"] = (
112
+ provider,
113
+ ) => {
114
+ notificationProvider = provider
115
+ }
116
+ const notify: Methods["notify"] = async (args) => {
117
+ if (notificationProvider === undefined) {
118
+ logger.warn(
119
+ { title: args.title },
120
+ "methods.notify: no notification provider configured; skipping",
121
+ )
122
+ return
123
+ }
124
+ await notificationProvider.notify(args)
125
+ }
126
+
127
+ let requirements: RequirementsCheckResult | undefined = undefined
128
+ let deckRebuilder: (() => void) | undefined
129
+ const setDeckRebuilder: Methods["setDeckRebuilder"] = (rebuild) => {
130
+ deckRebuilder = rebuild
131
+ }
132
+ const requestDeckRebuild: Methods["requestDeckRebuild"] = () => {
133
+ if (deckRebuilder === undefined) {
134
+ logger.warn(
135
+ "methods.requestDeckRebuild: no deck rebuilder wired; skipping",
136
+ )
137
+ return
138
+ }
139
+ deckRebuilder()
140
+ }
141
+ const setRequirements: Methods["setRequirements"] = (value) => {
142
+ requirements = value
143
+ }
144
+ const checkRequirement: Methods["checkRequirement"] = (capability) => {
145
+ if (requirements === undefined) return true
146
+ return requirements[capability]?.available ?? true
147
+ }
148
+ const showTemporaryError: Methods["showTemporaryError"] = (
149
+ deckId,
150
+ position,
151
+ durationMs = DEFAULT_BUTTON_ERROR_DURATION_MS,
152
+ buttonId,
153
+ details,
154
+ ) => {
155
+ ctx.pubSub.publish("runtime:buttonError", {
156
+ deckId,
157
+ position,
158
+ durationMs,
159
+ ...(buttonId !== undefined ? { buttonId } : {}),
160
+ ...(details !== undefined ? { details } : {}),
161
+ })
162
+ }
163
+
164
+ const adjustBrightness: Methods["adjustBrightness"] = ({ direction }) => {
165
+ const step = 10
166
+ const current = ctx.runtime.getBrightness()
167
+ const raw = direction === "up" ? current + step : current - step
168
+ const next = Math.max(10, Math.min(100, Math.round(raw)))
169
+ if (next === current) return
170
+ ctx.runtime.setBrightness(next)
171
+ ctx.pubSub.publish("methods:adjustBrightness", { direction, value: next })
172
+ }
173
+
174
+ const navigateToDeck: Methods["navigateToDeck"] = (args) => {
175
+ ctx.runtime.navigateToDeck(args.id, { addToHistory: args.addToHistory })
176
+ }
177
+
178
+ const goBack: Methods["goBack"] = () => {
179
+ ctx.runtime.goBack()
180
+ }
181
+
182
+ const getActiveDeckId: Methods["getActiveDeckId"] = () => {
183
+ return ctx.runtime.getActiveDeckId()
184
+ }
185
+
186
+ const invalidate: Methods["invalidate"] = () => {
187
+ ctx.runtime.invalidate()
188
+ }
189
+
190
+ const publish: Methods["publish"] = <T>(channel: string, payload: T) => {
191
+ ctx.pubSub.publish<T>(channel, payload)
192
+ }
193
+
194
+ const subscribe: Methods["subscribe"] = <T>(
195
+ channel: string,
196
+ cb: (payload: T) => void,
197
+ ) => {
198
+ return ctx.pubSub.subscribe<T>(channel, cb)
199
+ }
200
+
201
+ const runCommand: Methods["runCommand"] = async (command, options) => {
202
+ return await ctx.executor.run(command, options)
203
+ }
204
+
205
+ const keyMacro: Methods["keyMacro"] = async (action) => {
206
+ if (keyMacroProvider === undefined) {
207
+ throw new NotImplementedError(
208
+ "methods.keyMacro requires a keyMacroProvider (set via methods.setKeyMacroProvider)",
209
+ )
210
+ }
211
+ if (action.kind === "text") {
212
+ await keyMacroProvider.sendKey(action.value)
213
+ return
214
+ }
215
+ if (action.kind === "key") {
216
+ if (!isValidKey(action.value)) {
217
+ throw new NotImplementedError(
218
+ `methods.keyMacro: unknown key '${action.value}'. Valid keys: ${[...knownKeys].join(", ")}`,
219
+ )
220
+ }
221
+ await keyMacroProvider.sendKey(action.value)
222
+ return
223
+ }
224
+ const parsed: ParsedCombo | null = parseCombo(action.value)
225
+ if (parsed === null) {
226
+ throw new NotImplementedError(
227
+ `methods.keyMacro: invalid combo '${action.value}'`,
228
+ )
229
+ }
230
+ const combo =
231
+ parsed.mods.length > 0
232
+ ? `${parsed.mods.join("+")}+${parsed.key}`
233
+ : parsed.key
234
+ await keyMacroProvider.sendKey(combo)
235
+ }
236
+
237
+ const typeText: Methods["typeText"] = async (text) => {
238
+ if (keyMacroProvider === undefined) {
239
+ throw new NotImplementedError(
240
+ "methods.typeText requires a keyMacroProvider (set via methods.setKeyMacroProvider)",
241
+ )
242
+ }
243
+ await keyMacroProvider.sendKey(text)
244
+ }
245
+
246
+ const dispatch: Methods["dispatch"] = async (value) => {
247
+ if (value.startsWith("type://")) {
248
+ const text = value.slice("type://".length)
249
+ if (text.length === 0) {
250
+ throw new NotImplementedError(
251
+ "dispatch: type:// requires a value, e.g. type://hello",
252
+ )
253
+ }
254
+ await keyMacro({ kind: "text", value: text })
255
+ return
256
+ }
257
+ if (value.startsWith("macro://")) {
258
+ const inner = value.slice("macro://".length)
259
+ if (inner.length === 0) {
260
+ throw new NotImplementedError(
261
+ "dispatch: macro:// requires a value, e.g. macro://ctrl+c",
262
+ )
263
+ }
264
+ let macro: string
265
+ if (inner.startsWith("{")) {
266
+ let parsed: unknown
267
+ try {
268
+ parsed = JSON.parse(inner)
269
+ } catch {
270
+ throw new NotImplementedError(
271
+ "dispatch: macro://{...} payload is not valid JSON",
272
+ )
273
+ }
274
+ if (typeof parsed !== "object" || parsed === null) {
275
+ throw new NotImplementedError(
276
+ "dispatch: macro://{...} payload must be a JSON object",
277
+ )
278
+ }
279
+ const variants = parsed as Record<string, unknown>
280
+ const platKey =
281
+ process.platform === "darwin"
282
+ ? "osx"
283
+ : process.platform === "win32"
284
+ ? "windows"
285
+ : "linux"
286
+ const pick =
287
+ typeof variants[platKey] === "string"
288
+ ? (variants[platKey] as string)
289
+ : typeof variants.all === "string"
290
+ ? (variants.all as string)
291
+ : undefined
292
+ if (pick === undefined || pick.length === 0) {
293
+ throw new NotImplementedError(
294
+ `dispatch: macro://{...} has no value for platform '${process.platform}' and no 'all' fallback`,
295
+ )
296
+ }
297
+ macro = pick
298
+ } else {
299
+ // `[macos:cmd+c]ctrl+c` — per-OS overrides with a mandatory default.
300
+ // Plain macros contain no leading bracket and fall straight through.
301
+ try {
302
+ macro = resolvePlatformMacro(inner)
303
+ } catch (error) {
304
+ throw new NotImplementedError(
305
+ `dispatch: ${error instanceof Error ? error.message : String(error)}`,
306
+ )
307
+ }
308
+ }
309
+ await dispatchMacro(macro, { runCommand, keyMacro })
310
+ return
311
+ }
312
+ if (value.startsWith("brightness://")) {
313
+ const inner = value.slice("brightness://".length)
314
+ if (inner === "up" || inner === "down") {
315
+ adjustBrightness({ direction: inner })
316
+ return
317
+ }
318
+ throw new NotImplementedError(
319
+ `dispatch: brightness:// requires up/down, got '${inner}'`,
320
+ )
321
+ }
322
+ await runCommand(value)
323
+ }
324
+
325
+ return {
326
+ runCommand,
327
+ keyMacro,
328
+ typeText,
329
+ dispatch,
330
+ navigateToDeck,
331
+ goBack,
332
+ getActiveDeckId,
333
+ invalidate,
334
+ publish,
335
+ subscribe,
336
+ setKeyMacroProvider,
337
+ setNotificationProvider,
338
+ notify,
339
+ setRequirements,
340
+ checkRequirement,
341
+ showTemporaryError,
342
+ adjustBrightness,
343
+ requestDeckRebuild,
344
+ setDeckRebuilder,
345
+ }
346
+ }
347
+
348
+ export const deckFromRuntimeDeck = (deck: RuntimeDeck) => deck
@@ -0,0 +1,98 @@
1
+ import type { AddonGeneratedDeck } from "@/addon/api"
2
+ import {
3
+ NEXT_PAGE_MARKER,
4
+ type NextPageMarker,
5
+ paginate,
6
+ } from "@/core/pagination"
7
+
8
+ export interface PageDeckResult {
9
+ readonly deckId: string
10
+ readonly pageIndex: number
11
+ readonly totalPages: number
12
+ readonly deck: AddonGeneratedDeck
13
+ }
14
+
15
+ export interface PaginateDeckOptions {
16
+ readonly baseDeckId: string
17
+ readonly buttons: readonly unknown[]
18
+ readonly keyCount: number
19
+ }
20
+
21
+ const PAGE_NAV_BUTTON_TYPE = "core:page-nav"
22
+
23
+ const isNextPageMarker = (
24
+ item: { id: string; value: unknown } | null,
25
+ ): item is { id: NextPageMarker; value: unknown } =>
26
+ item !== null && item.value === NEXT_PAGE_MARKER
27
+
28
+ export const paginateDeck = (opts: PaginateDeckOptions): PageDeckResult[] => {
29
+ const { baseDeckId, buttons, keyCount } = opts
30
+
31
+ const result = paginate(buttons, { keyCount })
32
+ if (result.pages.length === 0) {
33
+ return []
34
+ }
35
+
36
+ const pageNavPosition = keyCount - 2
37
+
38
+ return result.pages.map((page, pageIndex) => {
39
+ const isFirstPage = pageIndex === 0
40
+ const isLastPage = pageIndex === result.pages.length - 1
41
+ const totalPages = result.pages.length
42
+ const deckId =
43
+ totalPages === 1 ? baseDeckId : `${baseDeckId}-p${pageIndex + 1}`
44
+
45
+ const deckButtons: unknown[] = []
46
+
47
+ for (const item of page.items) {
48
+ if (isNextPageMarker(item)) {
49
+ // ponytail: keep page-nav targets inside the page set. The base deck
50
+ // id (e.g. chrome-overlay:shortcuts) is NOT a runtime deck (the pages
51
+ // are -p1, -p2, -p3); navigating to it would no-op. First page's
52
+ // prev and last page's next target the current page (no-op), so the
53
+ // entry/exit to the overlay is via core:overlay-toggle only.
54
+ const prevDeckId = isFirstPage ? deckId : `${baseDeckId}-p${pageIndex}`
55
+ const nextDeckId = isLastPage
56
+ ? deckId
57
+ : `${baseDeckId}-p${pageIndex + 2}`
58
+
59
+ for (let i = deckButtons.length - 1; i >= 0; i--) {
60
+ const existing = deckButtons[i] as Record<string, unknown>
61
+ if (existing?.position === pageNavPosition) {
62
+ deckButtons.splice(i, 1)
63
+ }
64
+ }
65
+ deckButtons.push({
66
+ id: `${pageNavPosition}-${baseDeckId}-${pageIndex}`,
67
+ type: PAGE_NAV_BUTTON_TYPE,
68
+ position: pageNavPosition,
69
+ config: {
70
+ currentPage: pageIndex + 1,
71
+ totalPages,
72
+ prevDeckId,
73
+ nextDeckId,
74
+ },
75
+ })
76
+ } else if (item !== null) {
77
+ // ponytail: emit each page-deck with LOCAL slot positions 0..N-1,
78
+ // not the global page * (K-2) + slot encoding. The frontend renders
79
+ // a fixed K-key grid per page; n-1 (sysBack) and n-2 (pageNav) are
80
+ // reserved on every page independently.
81
+ const slot = Number(item.id.split("-").pop() ?? "0")
82
+ deckButtons.push({
83
+ ...(item.value as Record<string, unknown>),
84
+ position: slot,
85
+ })
86
+ }
87
+ }
88
+
89
+ return {
90
+ deckId,
91
+ pageIndex,
92
+ totalPages,
93
+ deck: {
94
+ buttons: deckButtons,
95
+ },
96
+ }
97
+ })
98
+ }
@@ -0,0 +1,57 @@
1
+ import type pino from "pino"
2
+
3
+ /**
4
+ * Assigns a position to every button — no drops.
5
+ *
6
+ * - Buttons with a valid explicit `position` keep it (first-come wins
7
+ * for duplicates).
8
+ * - Duplicate positions and buttons without a position fall through to
9
+ * a "gap" queue, filled sequentially starting at 0 in array order.
10
+ * - Positions ≥ keyCount are preserved: the caller is responsible for
11
+ * pagination (group by `floor(pos / (K-2))`). The deck-config and
12
+ * addon-deck pipelines both go through `paginateDeck` for that.
13
+ * - The internal contract: every emitted button has `position` set.
14
+ *
15
+ * ponytail: O(n²) scan is fine for deck sizes (<30).
16
+ */
17
+ export const positionButtons = <T extends { position?: number }>(
18
+ buttons: readonly T[],
19
+ keyCount: number,
20
+ logger?: pino.Logger,
21
+ ): T[] => {
22
+ const occupied = new Set<number>()
23
+ const result: T[] = []
24
+ const unfixed: T[] = []
25
+
26
+ for (const btn of buttons) {
27
+ const pos = btn.position
28
+ if (pos === undefined || !Number.isFinite(pos) || pos < 0) {
29
+ unfixed.push(btn)
30
+ continue
31
+ }
32
+ // ponytail: positions ≥ keyCount are preserved as deck-global ordering
33
+ // hints. Pagination groups them via floor(pos / (K-2)); the deck-config
34
+ // pipeline runs paginateDeck before the frontend serializes per-page
35
+ // slot positions.
36
+ if (occupied.has(pos)) {
37
+ logger?.debug(
38
+ { position: pos, reason: "duplicate" },
39
+ "duplicate position, treating as unfixed",
40
+ )
41
+ unfixed.push(btn)
42
+ continue
43
+ }
44
+ occupied.add(pos)
45
+ result.push(btn)
46
+ }
47
+
48
+ let gap = 0
49
+ for (const btn of unfixed) {
50
+ while (occupied.has(gap)) gap++
51
+ occupied.add(gap)
52
+ result.push({ ...btn, position: gap } as T)
53
+ gap++
54
+ }
55
+
56
+ return result
57
+ }
@@ -0,0 +1,5 @@
1
+ // ponytail: re-export so `import ... from "./runtime"` resolves to
2
+ // this directory via the standard `./runtime/index.{ts,tsx}` lookup.
3
+ // Without this, `moduleResolution: bundler` falls back to
4
+ // `./runtime/index.json` and crashes the daemon with ERR_MODULE_NOT_FOUND.
5
+ export * from "./runtime"