@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,343 @@
1
+ import { fileURLToPath } from "node:url"
2
+ import { createRequire } from "node:module"
3
+ import { dirname, resolve as resolvePath, join } from "node:path"
4
+ import { cpSync, existsSync, readFileSync, readdirSync } from "node:fs"
5
+
6
+ import type { AddonRegistry } from "@/addon/registry"
7
+ import type { LoadedTheme } from "@/addon/api"
8
+ import { ThemeJsonManifestSchema, type ThemeJsonManifest } from "./manifest"
9
+ import { buildThemeCss } from "./css"
10
+ import type { ThemeEntry } from "@/config/schemas"
11
+
12
+ const here = dirname(fileURLToPath(import.meta.url))
13
+
14
+ // ponytail: `here` is the themes dir when running from source
15
+ // (`packages/cli/src/themes`) but the BUNDLE dir when running from dist
16
+ // (`packages/cli/dist`), where the source tree isn't shipped. The dist build
17
+ // sits one level below src/, so try the sibling src/themes fallback first —
18
+ // this is what let the systemd unit (dist) crash on `theme: default` with
19
+ // "not a registered theme". Each candidate is validated by the presence of
20
+ // the `default` theme manifest so a stale candidate can never win.
21
+ const resolveThemesRoot = (): string => {
22
+ const candidates = [
23
+ here,
24
+ resolvePath(here, "..", "src", "themes"),
25
+ resolvePath(here, "..", "..", "src", "themes"),
26
+ ]
27
+ for (const candidate of candidates) {
28
+ if (existsSync(join(candidate, "default", "sirenodeck.json"))) {
29
+ return candidate
30
+ }
31
+ }
32
+ return here
33
+ }
34
+
35
+ const themesRoot = resolveThemesRoot()
36
+
37
+ // ponytail: sibling scan — first-party themes shipped outside the CLI package
38
+ // (e.g. packages/themes/<name>) get registered as "sibling" so authors can
39
+ // iterate on a theme in a separate workspace. From source `here` is
40
+ // `<repo>/packages/cli/src/themes`, so `../../../themes` lands in
41
+ // `<repo>/packages/themes`; from dist it's `<repo>/packages/cli/dist`, so
42
+ // `../../themes` lands there instead.
43
+ const siblingCandidates = [
44
+ resolvePath(themesRoot, "..", "..", "..", "themes"),
45
+ resolvePath(themesRoot, "..", "..", "themes"),
46
+ ]
47
+ const siblingThemesRoot =
48
+ siblingCandidates.find((root) => existsSync(root)) ?? siblingCandidates[0]!
49
+
50
+ export interface BuiltInThemeSpec {
51
+ name: string
52
+ dir: string
53
+ }
54
+
55
+ function _discoverThemesInRoot(
56
+ root: string,
57
+ ): ReadonlyArray<{ dir: string; name: string }> {
58
+ if (!existsSync(root)) return []
59
+ const entries = readdirSync(root, { withFileTypes: true })
60
+ const themes: { dir: string; name: string }[] = []
61
+ for (const entry of entries) {
62
+ if (!entry.isDirectory()) continue
63
+ const manifestPath = join(root, entry.name, "sirenodeck.json")
64
+ if (!existsSync(manifestPath)) continue
65
+ themes.push({ dir: join(root, entry.name), name: entry.name })
66
+ }
67
+ return themes
68
+ }
69
+
70
+ function _discoverThemesDir(): ReadonlyArray<{ dir: string; name: string }> {
71
+ return _discoverThemesInRoot(themesRoot)
72
+ }
73
+
74
+ function _discoverSiblingThemesDir(): ReadonlyArray<{
75
+ dir: string
76
+ name: string
77
+ }> {
78
+ return _discoverThemesInRoot(siblingThemesRoot)
79
+ }
80
+
81
+ export const BUILT_IN_THEMES: ReadonlyArray<BuiltInThemeSpec> =
82
+ _discoverThemesDir().map(({ dir, name }) => ({ name, dir }))
83
+
84
+ function discoverThemeManifests(): ReadonlyArray<{
85
+ dir: string
86
+ name: string
87
+ }> {
88
+ return _discoverThemesDir()
89
+ }
90
+
91
+ export function readAndValidateManifest(
92
+ manifestPath: string,
93
+ expectedName?: string,
94
+ ): ThemeJsonManifest {
95
+ let raw: unknown
96
+ try {
97
+ raw = JSON.parse(readFileSync(manifestPath, "utf8"))
98
+ } catch {
99
+ throw new Error(`Failed to parse theme manifest: ${manifestPath}`)
100
+ }
101
+ const result = ThemeJsonManifestSchema.safeParse(raw)
102
+ if (!result.success) {
103
+ throw new Error(
104
+ `Invalid theme manifest in ${manifestPath}: ${result.error.message}`,
105
+ )
106
+ }
107
+ if (expectedName !== undefined && result.data.name !== expectedName) {
108
+ throw new Error(
109
+ `Theme name '${result.data.name}' in ${manifestPath} does not match expected '${expectedName}'`,
110
+ )
111
+ }
112
+ return result.data
113
+ }
114
+
115
+ export function buildThemeCssFromManifest(
116
+ manifest: ThemeJsonManifest,
117
+ themeDir: string,
118
+ ): string {
119
+ const assetsStyles = (manifest.assets?.styles ?? ["./components.css"]).map(
120
+ (s) => resolvePath(themeDir, s),
121
+ )
122
+ const stylesheetContents = assetsStyles.map((p) => {
123
+ try {
124
+ return readFileSync(p, "utf8")
125
+ } catch {
126
+ return `/* stylesheet not found: ${p} */`
127
+ }
128
+ })
129
+ return buildThemeCss(manifest, stylesheetContents)
130
+ }
131
+
132
+ // ponytail: theme.css is emitted into the vite frontend's `.sirenodeck/`
133
+ // dir, but its `url('./assets/...')` font/asset paths are relative to THAT
134
+ // file — so the theme's assets/ must be copied next to it or every font 404s.
135
+ export function copyThemeAssets(themeDir: string, cssDir: string): void {
136
+ const assetsDir = join(themeDir, "assets")
137
+ if (!existsSync(assetsDir)) return
138
+ cpSync(assetsDir, join(cssDir, "assets"), { recursive: true })
139
+ }
140
+
141
+ function buildLoadedTheme(
142
+ manifest: ThemeJsonManifest,
143
+ themeDir: string,
144
+ source:
145
+ | { kind: "builtin"; resolvedPath: string }
146
+ | { kind: "sibling"; resolvedPath: string }
147
+ | { kind: "local"; resolvedPath: string },
148
+ ): { theme: LoadedTheme; getCss: () => string } {
149
+ const uiOverridesPath = manifest["ui-overrides"]
150
+ ? resolvePath(themeDir, manifest["ui-overrides"])
151
+ : null
152
+
153
+ const theme: LoadedTheme = {
154
+ name: manifest.name,
155
+ apiVersion: manifest.apiVersion,
156
+ source,
157
+ manifestPath: join(themeDir, "sirenodeck.json"),
158
+ uiOverridesPath,
159
+ cssPath: "",
160
+ }
161
+
162
+ const getCss = (): string => buildThemeCssFromManifest(manifest, themeDir)
163
+
164
+ return { theme, getCss }
165
+ }
166
+
167
+ export function loadBuiltInThemes(): Array<{
168
+ theme: LoadedTheme
169
+ getCss: () => string
170
+ }> {
171
+ const discovered = discoverThemeManifests()
172
+ return discovered.map(({ dir, name }) => {
173
+ const manifest = readAndValidateManifest(join(dir, "sirenodeck.json"), name)
174
+ return buildLoadedTheme(manifest, dir, {
175
+ kind: "builtin",
176
+ resolvedPath: dir,
177
+ })
178
+ })
179
+ }
180
+
181
+ export const registerBuiltInThemes = (registry: AddonRegistry): void => {
182
+ const builtIns = loadBuiltInThemes()
183
+ for (const { theme } of builtIns) {
184
+ registry.loadTheme(theme)
185
+ }
186
+ }
187
+
188
+ export function loadSiblingThemes(): Array<{
189
+ theme: LoadedTheme
190
+ getCss: () => string
191
+ }> {
192
+ const discovered = _discoverSiblingThemesDir()
193
+ return discovered.map(({ dir, name }) => {
194
+ const manifest = readAndValidateManifest(join(dir, "sirenodeck.json"), name)
195
+ return buildLoadedTheme(manifest, dir, {
196
+ kind: "sibling",
197
+ resolvedPath: dir,
198
+ })
199
+ })
200
+ }
201
+
202
+ export const registerSiblingThemes = (registry: AddonRegistry): void => {
203
+ const siblings = loadSiblingThemes()
204
+ for (const { theme } of siblings) {
205
+ registry.loadTheme(theme)
206
+ }
207
+ }
208
+
209
+ export function loadThemeFromPath(
210
+ registry: AddonRegistry,
211
+ themePath: string,
212
+ aliasName?: string,
213
+ ): { theme: LoadedTheme; getCss: () => string } {
214
+ const resolvedPath = resolvePath(themePath)
215
+ const manifestPath = join(resolvedPath, "sirenodeck.json")
216
+ const manifest = readAndValidateManifest(manifestPath)
217
+ const { theme, getCss } = buildLoadedTheme(manifest, resolvedPath, {
218
+ kind: "local",
219
+ resolvedPath,
220
+ })
221
+ // ponytail: theme may already be registered by `registerSiblingThemes`
222
+ // (sibling scan at boot) or a previous load. Skip the duplicate-registration
223
+ // throw — the manifest-driven getCss callback is still produced either way.
224
+ if (registry.getTheme(theme.name) === undefined) {
225
+ registry.loadTheme(theme)
226
+ }
227
+ if (aliasName !== undefined && aliasName !== manifest.name) {
228
+ if (registry.getTheme(aliasName) === undefined) {
229
+ registry.loadTheme({ ...theme, name: aliasName })
230
+ }
231
+ }
232
+ return { theme, getCss }
233
+ }
234
+
235
+ export type ResolveThemeOptions = {
236
+ theme: ThemeEntry | undefined
237
+ packageRoots?: ReadonlyArray<string>
238
+ }
239
+
240
+ export interface ResolveThemeResult {
241
+ theme: LoadedTheme
242
+ getCss: () => string
243
+ }
244
+
245
+ /**
246
+ * Detect whether a string looks like a filesystem path. Strict prefix
247
+ * detection — only `./`, `../`, `/`, or a Windows drive letter. Anything
248
+ * else is treated as a registered theme name or an npm package.
249
+ */
250
+ function isPathLike(s: string): boolean {
251
+ return (
252
+ s.startsWith("./") ||
253
+ s.startsWith("../") ||
254
+ s.startsWith("/") ||
255
+ /^[a-zA-Z]:[\\/]/.test(s)
256
+ )
257
+ }
258
+
259
+ /**
260
+ * Resolve an npm package name to its installed directory via Node's
261
+ * resolver. Returns null when the package isn't found.
262
+ */
263
+ function resolvePackagePath(
264
+ name: string,
265
+ packageRoots: ReadonlyArray<string> = [],
266
+ ): string | null {
267
+ try {
268
+ const require = createRequire(import.meta.url)
269
+ const pkgJsonPath = require.resolve(`${name}/package.json`)
270
+ return dirname(pkgJsonPath)
271
+ } catch {
272
+ for (const root of packageRoots) {
273
+ const candidate = join(root, name)
274
+ if (existsSync(join(candidate, "package.json"))) return candidate
275
+ }
276
+ return null
277
+ }
278
+ }
279
+
280
+ const resolveBuiltinTheme = (
281
+ registry: AddonRegistry,
282
+ theme: LoadedTheme,
283
+ ): ResolveThemeResult => {
284
+ if (theme.source.kind === "builtin" || theme.source.kind === "sibling") {
285
+ const themeDir = dirname(theme.manifestPath)
286
+ const manifest = readAndValidateManifest(theme.manifestPath, theme.name)
287
+ return {
288
+ theme,
289
+ getCss: () => buildThemeCssFromManifest(manifest, themeDir),
290
+ }
291
+ }
292
+ return { theme, getCss: () => "" }
293
+ }
294
+
295
+ const resolveStringTheme = (
296
+ entry: string,
297
+ registry: AddonRegistry,
298
+ packageRoots: ReadonlyArray<string>,
299
+ ): ResolveThemeResult => {
300
+ // 1. Registered theme name (built-in auto-discovery or a previously
301
+ // loaded theme). Use getTheme (returns undefined when missing);
302
+ // resolveActiveTheme throws, which short-circuits the dispatcher.
303
+ const registered = registry.getTheme(entry)
304
+ if (registered) {
305
+ return resolveBuiltinTheme(registry, registered)
306
+ }
307
+
308
+ // 2. Path — `./`, `../`, `/`, or a Windows drive letter.
309
+ if (isPathLike(entry)) {
310
+ return loadThemeFromPath(registry, entry)
311
+ }
312
+
313
+ // 3. npm package — resolve to the installed package directory and load
314
+ // the theme files inside it.
315
+ const packagePath = resolvePackagePath(entry, packageRoots)
316
+ if (packagePath !== null) {
317
+ return loadThemeFromPath(registry, packagePath)
318
+ }
319
+
320
+ throw new Error(
321
+ `Theme '${entry}' is not a registered theme, not a path, and not a known npm package.`,
322
+ )
323
+ }
324
+
325
+ export const resolveActiveTheme = (
326
+ registry: AddonRegistry,
327
+ options: ResolveThemeOptions,
328
+ ): ResolveThemeResult => {
329
+ const { theme: themeEntry } = options
330
+ if (themeEntry === undefined) {
331
+ const theme = registry.resolveActiveTheme(undefined)
332
+ return resolveBuiltinTheme(registry, theme)
333
+ }
334
+ if (typeof themeEntry === "object") {
335
+ const resolved = resolvePackagePath(themeEntry.src, options.packageRoots)
336
+ if (resolved === null)
337
+ throw new Error(
338
+ `Theme '${themeEntry.src}' is not installed. Run 'sirenodeck install ${themeEntry.src}'.`,
339
+ )
340
+ return loadThemeFromPath(registry, resolved)
341
+ }
342
+ return resolveStringTheme(themeEntry, registry, options.packageRoots ?? [])
343
+ }
@@ -0,0 +1,164 @@
1
+ import { z } from "zod"
2
+
3
+ export const ThemeColorTokenSchema = z
4
+ .object({
5
+ background: z.string().min(1),
6
+ frame: z.string().min(1),
7
+ foreground: z.string().min(1),
8
+ "foreground-contrast": z.string().min(1),
9
+ primary: z.string().min(1),
10
+ accent: z.string().min(1),
11
+ muted: z.string().min(1).optional(),
12
+ success: z.string().min(1),
13
+ danger: z.string().min(1),
14
+ })
15
+ .strict()
16
+
17
+ /**
18
+ * Required variant keys every theme must declare. Addons can rely on these
19
+ * five for highlights, warnings, success and error states without falling
20
+ * back to a hardcoded palette.
21
+ *
22
+ * Themes may declare EXTRA variants on top — those surface as `--sireno-variant-<name>-*`
23
+ * CSS vars and become available to user config.
24
+ */
25
+ export const REQUIRED_VARIANT_KEYS = ["default", "error"] as const
26
+
27
+ export type RequiredVariantKey = (typeof REQUIRED_VARIANT_KEYS)[number]
28
+
29
+ const ThemeVariantTokenOverridesSchema = z
30
+ .object({
31
+ primary: z.string().min(1).optional(),
32
+ accent: z.string().min(1).optional(),
33
+ foreground: z.string().min(1).optional(),
34
+ "foreground-contrast": z.string().min(1).optional(),
35
+ success: z.string().min(1).optional(),
36
+ danger: z.string().min(1).optional(),
37
+ muted: z.string().min(1).optional(),
38
+ })
39
+ .strict()
40
+
41
+ export const ThemeVariantStylesSchema = z
42
+ .object({
43
+ background: z.string().min(1),
44
+ border: z.string().min(1),
45
+ foreground: z.string().min(1),
46
+ glow: z.string().optional(),
47
+ tokens: ThemeVariantTokenOverridesSchema.optional(),
48
+ })
49
+ .strict()
50
+
51
+ export const ThemeVariantsSchema = z
52
+ .record(z.string().min(1), ThemeVariantStylesSchema)
53
+ .superRefine((variants, ctx) => {
54
+ const missing = REQUIRED_VARIANT_KEYS.filter((k) => !(k in variants))
55
+ if (missing.length === 0) return
56
+ for (const key of missing) {
57
+ ctx.addIssue({
58
+ code: z.ZodIssueCode.custom,
59
+ path: [key],
60
+ message: `theme manifest is missing required variant "${key}" — every theme must declare variants: { ${REQUIRED_VARIANT_KEYS.map((k) => `"${k}"`).join(", ")} } at minimum`,
61
+ })
62
+ }
63
+ })
64
+
65
+ export const ThemeTypographyRoleSchema = z
66
+ .object({
67
+ fontFamily: z.string().min(1),
68
+ fontSize: z.number().positive(),
69
+ fontWeight: z.number().int().positive(),
70
+ letterSpacing: z.number().optional(),
71
+ })
72
+ .strict()
73
+
74
+ export const ThemeTypographySchema = z
75
+ .object({
76
+ main_text: ThemeTypographyRoleSchema,
77
+ auxiliary_text: ThemeTypographyRoleSchema,
78
+ monospace: ThemeTypographyRoleSchema,
79
+ })
80
+ .strict()
81
+
82
+ export const ThemeFontFaceSchema = z
83
+ .object({
84
+ fontFamily: z.string().min(1),
85
+ fontWeight: z.number().int().positive(),
86
+ fontStyle: z.enum(["normal", "italic", "oblique"]).default("normal"),
87
+ src: z.string().min(1),
88
+ /**
89
+ * Optional variable-font axes — accepts a [min, max] tuple per axis.
90
+ * When set, css.ts emits a single @font-face with `font-weight: from..to`
91
+ * shorthand so one entry can serve a range without duplicating files.
92
+ */
93
+ axes: z
94
+ .object({
95
+ weight: z.tuple([z.number().int(), z.number().int()]).optional(),
96
+ slant: z.tuple([z.number(), z.number()]).optional(),
97
+ width: z.tuple([z.number(), z.number()]).optional(),
98
+ })
99
+ .optional(),
100
+ })
101
+ .strict()
102
+
103
+ export const ThemeEffectsSchema = z
104
+ .object({
105
+ glow: z
106
+ .object({
107
+ sm: z.string().min(1),
108
+ md: z.string().min(1),
109
+ lg: z.string().min(1),
110
+ })
111
+ .partial()
112
+ .optional(),
113
+ shadow: z
114
+ .object({
115
+ soft: z.string().min(1),
116
+ hard: z.string().min(1),
117
+ })
118
+ .partial()
119
+ .optional(),
120
+ blur: z
121
+ .object({
122
+ sm: z.string().min(1),
123
+ md: z.string().min(1),
124
+ })
125
+ .partial()
126
+ .optional(),
127
+ })
128
+ .strict()
129
+ .optional()
130
+
131
+ export const ThemeAssetsSchema = z
132
+ .object({
133
+ styles: z.array(z.string().min(1)).default([]),
134
+ })
135
+ .strict()
136
+
137
+ export const ThemeJsonManifestSchema = z
138
+ .object({
139
+ kind: z.literal("theme"),
140
+ apiVersion: z.number().int().positive(),
141
+ name: z.string().min(1),
142
+ version: z.string().min(1).optional(),
143
+ description: z.string().min(1).optional(),
144
+ colorTokens: ThemeColorTokenSchema,
145
+ typography: ThemeTypographySchema,
146
+ fonts: z.array(ThemeFontFaceSchema).default([]),
147
+ effects: ThemeEffectsSchema,
148
+ variants: ThemeVariantsSchema,
149
+ assets: ThemeAssetsSchema.optional(),
150
+ "ui-overrides": z.string().min(1).optional(),
151
+ })
152
+ .strict()
153
+
154
+ export type ThemeJsonManifest = z.infer<typeof ThemeJsonManifestSchema>
155
+
156
+ export type ThemeColorToken = keyof z.infer<typeof ThemeColorTokenSchema>
157
+
158
+ export type ThemeTypographyRole = keyof z.infer<typeof ThemeTypographySchema>
159
+
160
+ export type ThemeVariantStyles = z.infer<typeof ThemeVariantStylesSchema>
161
+
162
+ export type ThemeVariantName = string
163
+
164
+ export type ThemeVariants = z.infer<typeof ThemeVariantsSchema>
@@ -0,0 +1,32 @@
1
+ import { createContext, useContext, type ReactNode } from "react"
2
+
3
+ import type { LoadedTheme } from "@/addon/api"
4
+
5
+ export interface ThemeContextValue {
6
+ name: string
7
+ cssPath: string
8
+ theme: LoadedTheme
9
+ colorTokens: Record<string, string> | null
10
+ typography: Record<string, unknown> | null
11
+ }
12
+
13
+ export const ThemeContext = createContext<ThemeContextValue | null>(null)
14
+
15
+ export interface ThemeProviderProps {
16
+ value: ThemeContextValue
17
+ children: ReactNode
18
+ }
19
+
20
+ export const ThemeProvider = ({ value, children }: ThemeProviderProps) => (
21
+ <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
22
+ )
23
+
24
+ export const useTheme = (): ThemeContextValue => {
25
+ const ctx = useContext(ThemeContext)
26
+ if (!ctx) {
27
+ throw new Error(
28
+ "useTheme must be used inside <ThemeProvider>. Ensure your app is wrapped in ThemeProvider with a resolved theme.",
29
+ )
30
+ }
31
+ return ctx
32
+ }