@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,91 @@
1
+ import type { Unsubscribe } from "@/api/addon"
2
+
3
+ export type ChannelPayload = unknown
4
+
5
+ type Listener = (payload: ChannelPayload) => void
6
+ type AnnounceSubscribe = (channels: string[]) => void
7
+
8
+ export class ChannelRegistry {
9
+ private static _instance: ChannelRegistry | null = null
10
+ private static _announceSubscribe: AnnounceSubscribe | null = null
11
+ private readonly lastPayloads = new Map<string, ChannelPayload>()
12
+ private readonly listeners = new Map<string, Set<Listener>>()
13
+ private readonly listenerCounts = new Map<string, number>()
14
+ private pendingChannels: string[] = []
15
+ private flushScheduled = false
16
+
17
+ static instance(): ChannelRegistry {
18
+ if (ChannelRegistry._instance === null) {
19
+ ChannelRegistry._instance = new ChannelRegistry()
20
+ }
21
+ return ChannelRegistry._instance
22
+ }
23
+
24
+ static resetForTests(): void {
25
+ ChannelRegistry._instance = null
26
+ ChannelRegistry._announceSubscribe = null
27
+ }
28
+
29
+ static setAnnounceSubscribe(cb: AnnounceSubscribe | null): void {
30
+ ChannelRegistry._announceSubscribe = cb
31
+ }
32
+
33
+ publish<T extends ChannelPayload>(channel: string, payload: T): void {
34
+ this.lastPayloads.set(channel, payload)
35
+ const set = this.listeners.get(channel)
36
+ if (set !== undefined) for (const l of set) l(payload)
37
+ }
38
+
39
+ subscribe<T extends ChannelPayload>(
40
+ channel: string,
41
+ cb: (payload: T) => void,
42
+ ): Unsubscribe {
43
+ let set = this.listeners.get(channel)
44
+ if (set === undefined) {
45
+ set = new Set()
46
+ this.listeners.set(channel, set)
47
+ }
48
+ const listener = cb as Listener
49
+ set.add(listener)
50
+
51
+ const prevCount = this.listenerCounts.get(channel) ?? 0
52
+ this.listenerCounts.set(channel, prevCount + 1)
53
+ if (prevCount === 0) {
54
+ this.pendingChannels.push(channel)
55
+ if (!this.flushScheduled) {
56
+ this.flushScheduled = true
57
+ queueMicrotask(() => this.flushPendingSubscriptions())
58
+ }
59
+ }
60
+
61
+ return () => {
62
+ set?.delete(listener)
63
+ const count = this.listenerCounts.get(channel) ?? 0
64
+ if (count <= 1) {
65
+ this.listenerCounts.delete(channel)
66
+ } else {
67
+ this.listenerCounts.set(channel, count - 1)
68
+ }
69
+ }
70
+ }
71
+
72
+ private flushPendingSubscriptions(): void {
73
+ this.flushScheduled = false
74
+ if (this.pendingChannels.length === 0) return
75
+ const channels = [...new Set(this.pendingChannels)]
76
+ this.pendingChannels = []
77
+ ChannelRegistry._announceSubscribe?.(channels)
78
+ }
79
+
80
+ last<T extends ChannelPayload>(channel: string): T | undefined {
81
+ return this.lastPayloads.get(channel) as T | undefined
82
+ }
83
+
84
+ clear(): void {
85
+ this.lastPayloads.clear()
86
+ this.listeners.clear()
87
+ this.listenerCounts.clear()
88
+ this.pendingChannels = []
89
+ this.flushScheduled = false
90
+ }
91
+ }
@@ -0,0 +1,26 @@
1
+ import { useEffect, useState } from "react"
2
+
3
+ import { ChannelRegistry } from "./registry"
4
+ import type { ChannelPayload } from "@/api/addon"
5
+
6
+ export interface UseAddonChannelReturn<T = unknown> {
7
+ data: T | undefined
8
+ }
9
+
10
+ export const useAddonChannel = <T = unknown>(
11
+ channel: string,
12
+ ): UseAddonChannelReturn<T> => {
13
+ const [data, setData] = useState<T | undefined>(() =>
14
+ ChannelRegistry.instance().last<T>(channel),
15
+ )
16
+
17
+ useEffect(() => {
18
+ return ChannelRegistry.instance().subscribe<T>(channel, (payload) => {
19
+ setData(payload)
20
+ })
21
+ }, [channel])
22
+
23
+ return { data }
24
+ }
25
+
26
+ export type { ChannelRegistry, ChannelPayload }
@@ -0,0 +1,23 @@
1
+ import {
2
+ useAddonChannel,
3
+ type ChannelRegistry,
4
+ type ChannelPayload,
5
+ } from "./use-addon-channel"
6
+
7
+ export { useAddonChannel, type ChannelRegistry, type ChannelPayload }
8
+
9
+ export interface UseButtonStateReturn<T = unknown> {
10
+ readonly state: T | undefined
11
+ }
12
+
13
+ export const useButtonState = <T = unknown,>(
14
+ addonName: string,
15
+ buttonType: string,
16
+ buttonId: string,
17
+ ): UseButtonStateReturn<T> => {
18
+ const channel = `${addonName}:${buttonType}:${buttonId}`
19
+ const { data } = useAddonChannel<T>(channel)
20
+ return { state: data }
21
+ }
22
+
23
+ export const NullButton = (): null => null
@@ -0,0 +1,24 @@
1
+ import { useEffect, useState } from "react"
2
+
3
+ import { ChannelRegistry } from "./registry"
4
+
5
+ export interface UseDeckReturn {
6
+ activeDeckId: string | null
7
+ }
8
+
9
+ export const useDeck = (): UseDeckReturn => {
10
+ const [activeDeckId, setActiveDeckId] = useState<string | null>(
11
+ () => ChannelRegistry.instance().last<string>("runtime:activeDeck") ?? null,
12
+ )
13
+
14
+ useEffect(() => {
15
+ return ChannelRegistry.instance().subscribe<{ deckId: string }>(
16
+ "runtime:activeDeck",
17
+ (payload) => {
18
+ setActiveDeckId(payload.deckId)
19
+ },
20
+ )
21
+ }, [])
22
+
23
+ return { activeDeckId }
24
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,51 @@
1
+ # brightness
2
+
3
+ Single button that adjusts screen brightness. Tap = action (up/down/set). The button shows the current brightness as a bar.
4
+
5
+ Uses the OS provider from `packages/cli/src/system/providers/`:
6
+
7
+ - Linux: `xrandr` / `brightnessctl` (whichever is available)
8
+ - macOS: `osascript`
9
+ - Windows: PowerShell + WMI
10
+
11
+ ## Buttons
12
+
13
+ | Type | Description |
14
+ | ----------------------- | ------------------------------------------------ |
15
+ | `brightness:brightness` | Single button: shows + adjusts screen brightness |
16
+
17
+ ## Config
18
+
19
+ ```yaml
20
+ - position: 5
21
+ type: brightness:brightness
22
+ config:
23
+ action: up # "up" (default), "down", or "set"
24
+ value: 75 # required for "set" (0-100); ignored for up/down
25
+ ```
26
+
27
+ ### Gesture semantics
28
+
29
+ - **Tap** on a `brightness:brightness` button → executes the configured `action`.
30
+ - **Hold** on a `brightness:brightness` button → toggles between `up` and `down` for fine-grained adjustment.
31
+
32
+ ## Example
33
+
34
+ In your `settings` deck (auto-generated by `internal-settings`):
35
+
36
+ ```yaml
37
+ decks:
38
+ settings:
39
+ name: Settings
40
+ buttons:
41
+ - position: 0
42
+ type: brightness:brightness
43
+ config: { action: up }
44
+ ```
45
+
46
+ Run `xrandr --output eDP-1 --brightness 0.75` (Linux) to verify the action works on your system.
47
+
48
+ ## See also
49
+
50
+ - [system-status](../system-status/README.md) — same polling pattern; brightness is just a one-button metric
51
+ - [OS providers](../../../../system/providers/README.md) — brightness provider per OS
@@ -0,0 +1,13 @@
1
+ import type { AddonButtonTypeService } from "@/addon/api"
2
+
3
+ import { buildMacOSCommand, formatCommand, isMacOS } from "../../domain/macos"
4
+ import { configSchema, type ConfigSchema } from "./config"
5
+
6
+ export default {
7
+ configSchema,
8
+ defaultRenderIntervalMs: 2000,
9
+ onTap: ({ config, executor }) => {
10
+ if (!isMacOS(process.platform)) return
11
+ void executor.run(formatCommand(buildMacOSCommand(config))).catch(() => {})
12
+ },
13
+ } satisfies AddonButtonTypeService<ConfigSchema>
@@ -0,0 +1,10 @@
1
+ import { z } from "zod"
2
+
3
+ export const configSchema = z
4
+ .object({
5
+ action: z.enum(["up", "down", "set"]).optional().default("up"),
6
+ value: z.number().int().min(0).max(100).optional(),
7
+ })
8
+ .strict()
9
+
10
+ export type ConfigSchema = z.infer<typeof configSchema>
@@ -0,0 +1,50 @@
1
+ import { Text } from "@/ui/index"
2
+ import { useAddonChannel } from "@/api/react"
3
+ import type { AddonFrontendButton } from "@/addon/api"
4
+
5
+ import type { ConfigSchema } from "./config"
6
+
7
+ interface BrightnessState {
8
+ readonly value: number
9
+ readonly max: number
10
+ }
11
+
12
+ const BrightnessButtonFrontend: AddonFrontendButton<ConfigSchema> = ({
13
+ config,
14
+ }) => {
15
+ const { action } = config ?? {}
16
+ const { data } = useAddonChannel<BrightnessState>("brightness:current")
17
+ if (data === undefined) {
18
+ return (
19
+ <Text
20
+ size="xs"
21
+ tone="muted"
22
+ typography="mono"
23
+ className="flex h-full w-full items-center justify-center"
24
+ text="—"
25
+ />
26
+ )
27
+ }
28
+ const pct = Math.max(0, Math.min(100, (data.value / (data.max || 100)) * 100))
29
+ return (
30
+ <span className="flex h-full w-full flex-col items-stretch justify-center gap-1 p-2">
31
+ <span className="flex items-baseline justify-between">
32
+ <Text size="xs" tone="muted" fit="ellipsis" text="Brightness" />
33
+ <Text size="xs" tone="fg" text={`${pct.toFixed(0)}%`} />
34
+ </span>
35
+ <span className="block h-1.5 w-full overflow-hidden rounded bg-bar">
36
+ <span className="block h-full bg-accent" style={{ width: `${pct}%` }} />
37
+ </span>
38
+ <Text
39
+ size="xs"
40
+ tone="muted"
41
+ className="text-center"
42
+ typography="aux"
43
+ text={`Tap: ${action ?? "up"}`}
44
+ />
45
+ </span>
46
+ )
47
+ }
48
+
49
+ export default BrightnessButtonFrontend
50
+ export { BrightnessButtonFrontend }
@@ -0,0 +1,46 @@
1
+ import type { ConfigSchema as BrightnessButtonConfig } from "../buttons/brightness/config"
2
+
3
+ const escape = (s: string): string => `'${s.replaceAll("'", "'\\''")}'`
4
+
5
+ export const isMacOS = (platform: NodeJS.Platform): boolean =>
6
+ platform === "darwin"
7
+
8
+ export const setBrightnessMacOS = async (
9
+ config: BrightnessButtonConfig,
10
+ exec: (
11
+ cmd: string,
12
+ args: string[],
13
+ ) => Promise<{ exitCode: number; stderr: string }>,
14
+ ): Promise<void> => {
15
+ if (config.action === "set" && config.value !== undefined) {
16
+ const script = `tell application "System Events" to set brightness of (first item of (get displays)) to ${config.value / 100}`
17
+ await exec("osascript", ["-e", script])
18
+ return
19
+ }
20
+ if (config.action === "down") {
21
+ await exec("osascript", [
22
+ "-e",
23
+ `tell application "System Events" to key code 145`,
24
+ ])
25
+ return
26
+ }
27
+ await exec("osascript", [
28
+ "-e",
29
+ `tell application "System Events" to key code 144`,
30
+ ])
31
+ }
32
+
33
+ export const buildMacOSCommand = (
34
+ config: BrightnessButtonConfig,
35
+ ): readonly string[] => {
36
+ if (config.action === "set" && config.value !== undefined) {
37
+ return ["osascript", "-e", `set brightness to ${config.value / 100}`]
38
+ }
39
+ if (config.action === "down") {
40
+ return ["osascript", "-e", `key code 145`]
41
+ }
42
+ return ["osascript", "-e", `key code 144`]
43
+ }
44
+
45
+ export const formatCommand = (cmd: readonly string[]): string =>
46
+ cmd.map(escape).join(" ")
@@ -0,0 +1,5 @@
1
+ import type { NewAddonManifest } from "@/addon/api"
2
+
3
+ declare const manifest: NewAddonManifest
4
+ export default manifest
5
+ export { manifest }
@@ -0,0 +1,20 @@
1
+ import type { AddonManifestV1 } from "@/addon/api"
2
+
3
+ import brightnessBackend from "./buttons/brightness/backend"
4
+ import brightnessFrontend from "./buttons/brightness/frontend"
5
+
6
+ export const manifest: AddonManifestV1 = {
7
+ apiVersion: 1,
8
+ name: "brightness",
9
+ buttonTypes: {
10
+ "brightness:brightness": {
11
+ frontend: brightnessFrontend,
12
+ service: { ...brightnessBackend, gestureHandlers: ["tap"] as const },
13
+ },
14
+ },
15
+ publishIntervalMs: 2000,
16
+ }
17
+
18
+ export const brightnessAddon = manifest
19
+ export default manifest
20
+ export { brightnessBackend, brightnessFrontend }
@@ -0,0 +1,27 @@
1
+ import type { AddonPoller } from "@/addon/api-types"
2
+
3
+ import type { BrightnessProvider } from "./providers"
4
+
5
+ export interface BrightnessPollerDeps {
6
+ readonly brightnessProvider: BrightnessProvider | null
7
+ }
8
+
9
+ export const createPoller = (deps: BrightnessPollerDeps): AddonPoller => ({
10
+ channels: [
11
+ {
12
+ channel: "brightness:current",
13
+ intervalMs: 2_000,
14
+ poll: async () => {
15
+ if (deps.brightnessProvider === null) {
16
+ return { value: 0, max: 100 }
17
+ }
18
+ try {
19
+ const reading = await deps.brightnessProvider.getCurrent()
20
+ return { value: reading.value, max: reading.max }
21
+ } catch {
22
+ return { value: 0, max: 100 }
23
+ }
24
+ },
25
+ },
26
+ ],
27
+ })
@@ -0,0 +1,107 @@
1
+ import type pino from "pino"
2
+
3
+ import { type CommandExecutor } from "@/system/providers/shared"
4
+
5
+ import {
6
+ createNullBrightnessProvider,
7
+ type BrightnessProvider,
8
+ type BrightnessReading,
9
+ } from "./index"
10
+
11
+ export interface CreateDarwinBrightnessProviderOptions {
12
+ readonly executor: CommandExecutor
13
+ readonly logger: pino.Logger
14
+ }
15
+
16
+ // ponytail: this provider used to shell out to
17
+ // `tell application "System Events" to get brightness of (every item of
18
+ // displays)`. System Events has no `displays` element, so every call failed
19
+ // with "The variable displays is not defined (-2753)" and brightness has never
20
+ // worked on macOS. There is no public CLI or ioreg node for display brightness
21
+ // on Apple Silicon (it moved into the private DisplayServices framework), so
22
+ // the honest options are the `brightness` Homebrew binary or nothing.
23
+ //
24
+ // `brightness -l` prints one "display N: brightness 0.550000" line per display;
25
+ // `brightness <0..1>` sets every display. Values are 0..1 on the wire and
26
+ // 0..100 across the BrightnessProvider interface.
27
+ const BRIGHTNESS_BIN = "brightness"
28
+ const INSTALL_HINT = "install it with `brew install brightness`"
29
+
30
+ const BRIGHTNESS_LINE = /display\s+\d+:\s+brightness\s+([0-9]*\.?[0-9]+)/i
31
+
32
+ export const parseBrightnessList = (
33
+ stdout: string,
34
+ ): BrightnessReading | null => {
35
+ const match = BRIGHTNESS_LINE.exec(stdout)
36
+ if (match?.[1] === undefined) return null
37
+ const fraction = Number.parseFloat(match[1])
38
+ if (!Number.isFinite(fraction)) return null
39
+ const clamped = Math.max(0, Math.min(1, fraction))
40
+ return { value: Math.round(clamped * 100), max: 100 }
41
+ }
42
+
43
+ export const createDarwinBrightnessProvider = (
44
+ options: CreateDarwinBrightnessProviderOptions,
45
+ ): BrightnessProvider => {
46
+ const { executor, logger } = options
47
+ const nullProvider = createNullBrightnessProvider(logger)
48
+ let disposed = false
49
+ // Warn once, not on every poll tick.
50
+ let warnedMissing = false
51
+
52
+ const warnMissing = (detail: string): void => {
53
+ if (warnedMissing) return
54
+ warnedMissing = true
55
+ logger.warn(
56
+ { bin: BRIGHTNESS_BIN, detail },
57
+ `brightness: '${BRIGHTNESS_BIN}' unavailable — ${INSTALL_HINT}`,
58
+ )
59
+ }
60
+
61
+ const stop = async (): Promise<void> => {
62
+ disposed = true
63
+ }
64
+
65
+ const getCurrent = async (): Promise<BrightnessReading> => {
66
+ if (disposed) throw new Error("Brightness provider is disposed")
67
+ let result
68
+ try {
69
+ result = await executor.run(BRIGHTNESS_BIN, ["-l"], { timeoutMs: 2_000 })
70
+ } catch (err) {
71
+ warnMissing((err as Error).message)
72
+ return nullProvider.getCurrent()
73
+ }
74
+ if (result.exitCode !== 0) {
75
+ warnMissing(result.stderr.trim() || `exit ${result.exitCode}`)
76
+ return nullProvider.getCurrent()
77
+ }
78
+ const parsed = parseBrightnessList(result.stdout)
79
+ if (parsed === null) {
80
+ logger.warn(
81
+ { stdout: result.stdout.slice(0, 200) },
82
+ "brightness: could not parse `brightness -l` output",
83
+ )
84
+ return nullProvider.getCurrent()
85
+ }
86
+ return parsed
87
+ }
88
+
89
+ const setBrightness = async (value: number): Promise<void> => {
90
+ if (disposed) throw new Error("Brightness provider is disposed")
91
+ const fraction = Math.max(0, Math.min(100, Math.round(value))) / 100
92
+ let result
93
+ try {
94
+ result = await executor.run(BRIGHTNESS_BIN, [fraction.toFixed(3)], {
95
+ timeoutMs: 2_000,
96
+ })
97
+ } catch (err) {
98
+ warnMissing((err as Error).message)
99
+ return
100
+ }
101
+ if (result.exitCode !== 0) {
102
+ warnMissing(result.stderr.trim() || `exit ${result.exitCode}`)
103
+ }
104
+ }
105
+
106
+ return { getCurrent, setBrightness, stop }
107
+ }
@@ -0,0 +1,81 @@
1
+ import type pino from "pino"
2
+
3
+ import { ProviderError } from "@/system/providers/error"
4
+
5
+ import { createDarwinBrightnessProvider } from "./darwin"
6
+ import { createLinuxBrightnessProvider } from "./linux"
7
+ import { type CommandExecutor } from "@/system/providers/shared"
8
+ import { createWindowsBrightnessProvider } from "./windows"
9
+
10
+ export interface BrightnessReading {
11
+ readonly value: number
12
+ readonly max: number
13
+ }
14
+
15
+ export interface BrightnessProvider {
16
+ getCurrent(): Promise<BrightnessReading>
17
+ setBrightness(value: number): Promise<void>
18
+ stop(): Promise<void>
19
+ }
20
+
21
+ export const createNullBrightnessProvider = (
22
+ logger?: pino.Logger,
23
+ ): BrightnessProvider => {
24
+ if (logger) {
25
+ logger.warn(
26
+ { provider: "brightness" },
27
+ "OS brightness provider unavailable, using null provider",
28
+ )
29
+ }
30
+ return {
31
+ async getCurrent(): Promise<BrightnessReading> {
32
+ return { value: 0, max: 100 }
33
+ },
34
+ async setBrightness(_value: number): Promise<void> {
35
+ throw new ProviderError(
36
+ "NOT_AVAILABLE",
37
+ "Brightness provider not available on this platform",
38
+ )
39
+ },
40
+ async stop() {
41
+ return
42
+ },
43
+ }
44
+ }
45
+
46
+ export interface CreateBrightnessProviderOptions {
47
+ readonly executor: CommandExecutor
48
+ readonly platform?: NodeJS.Platform
49
+ readonly env?: Readonly<Record<string, string>>
50
+ readonly logger: pino.Logger
51
+ }
52
+
53
+ export const createBrightnessProvider = (
54
+ options: CreateBrightnessProviderOptions,
55
+ ): BrightnessProvider => {
56
+ const platform = options.platform ?? process.platform
57
+ if (platform === "linux") {
58
+ return createLinuxBrightnessProvider({
59
+ executor: options.executor,
60
+ env: options.env,
61
+ logger: options.logger,
62
+ })
63
+ }
64
+ if (platform === "darwin") {
65
+ return createDarwinBrightnessProvider({
66
+ executor: options.executor,
67
+ logger: options.logger,
68
+ })
69
+ }
70
+ if (platform === "win32") {
71
+ return createWindowsBrightnessProvider({
72
+ executor: options.executor,
73
+ logger: options.logger,
74
+ })
75
+ }
76
+ void createNullBrightnessProvider(options.logger)
77
+ throw new ProviderError(
78
+ "UNSUPPORTED_PLATFORM",
79
+ `Brightness provider not implemented for platform '${platform}' in this build`,
80
+ )
81
+ }