@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,66 @@
1
+ import {
2
+ agentKey,
3
+ type Agent,
4
+ type AgentStatus,
5
+ type AgentsSnapshot,
6
+ type ProviderId,
7
+ } from "./state.js"
8
+
9
+ export const mergeSnapshot = (
10
+ prev: AgentsSnapshot,
11
+ providers: Readonly<Record<ProviderId, readonly Agent[]>>,
12
+ ): AgentsSnapshot => {
13
+ const merged: Record<ProviderId, Agent[]> = {
14
+ opencode: [...prev.byProvider["opencode"]],
15
+ "claude-code": [...prev.byProvider["claude-code"]],
16
+ }
17
+ const seen = new Map<string, Agent>()
18
+ const attention: string[] = []
19
+
20
+ for (const id of Object.keys(providers) as ProviderId[]) {
21
+ const list: Agent[] = []
22
+ for (const a of providers[id]) {
23
+ const k = agentKey(a)
24
+ seen.set(k, a)
25
+ list.push(a)
26
+ if (a.status === "waiting_for_human" || a.status === "error") {
27
+ attention.push(k)
28
+ }
29
+ }
30
+ list.sort((x, y) => y.updatedAt - x.updatedAt)
31
+ merged[id] = list
32
+ }
33
+
34
+ return {
35
+ byProvider: merged,
36
+ attention,
37
+ generatedAt: Date.now(),
38
+ }
39
+ }
40
+
41
+ export const diffStatus = (
42
+ before: AgentStatus | undefined,
43
+ after: AgentStatus,
44
+ ): boolean => before !== after
45
+
46
+ const byCreatedDesc = (a: Agent, b: Agent): number => {
47
+ const aCreated = a.createdAt ?? 0
48
+ const bCreated = b.createdAt ?? 0
49
+ if (aCreated !== bCreated) return bCreated - aCreated
50
+ return b.updatedAt - a.updatedAt
51
+ }
52
+
53
+ // ponytail: the agents deck is materialized once at startup with a fixed
54
+ // number of slot tiles, so which agent fills each tile is resolved per
55
+ // broadcast from the live snapshot. Ordered by creation date (newest first);
56
+ // state is conveyed by the tile's color instead of ordering.
57
+ export const listAgents = (snapshot: AgentsSnapshot): readonly Agent[] =>
58
+ [
59
+ ...snapshot.byProvider["opencode"],
60
+ ...snapshot.byProvider["claude-code"],
61
+ ].sort(byCreatedDesc)
62
+
63
+ export const agentAtSlot = (
64
+ snapshot: AgentsSnapshot,
65
+ slot: number,
66
+ ): Agent | undefined => listAgents(snapshot)[slot]
@@ -0,0 +1,72 @@
1
+ export type AgentStatus =
2
+ | "idle"
3
+ | "running"
4
+ | "waiting"
5
+ | "waiting_for_human"
6
+ | "error"
7
+ | "compacting"
8
+
9
+ export type ProviderId = "opencode" | "claude-code"
10
+
11
+ export interface Agent {
12
+ readonly sessionId: string
13
+ readonly instanceId?: string
14
+ readonly pid?: number
15
+ readonly providerId: ProviderId
16
+ readonly title: string
17
+ readonly status: AgentStatus
18
+ readonly directory?: string
19
+ readonly cost?: number
20
+ readonly contextTokens?: number
21
+ readonly contextPercent?: number
22
+ readonly createdAt?: number
23
+ readonly updatedAt: number
24
+ readonly lastMessagePreview?: string
25
+ }
26
+
27
+ export interface AgentsSnapshot {
28
+ readonly byProvider: Readonly<Record<ProviderId, readonly Agent[]>>
29
+ readonly attention: readonly string[]
30
+ readonly generatedAt: number
31
+ // ponytail: provider logo asset ids (asset://...) registered daemon-side so
32
+ // the frontend can render small `<Icon>` logos on tiles in dev AND real mode.
33
+ readonly icons?: Readonly<Partial<Record<ProviderId, string>>>
34
+ }
35
+
36
+ export const CHANNEL = "coding-agents:agents" as const
37
+
38
+ export const KNOWN_PROVIDERS: readonly ProviderId[] = [
39
+ "opencode",
40
+ "claude-code",
41
+ ] as const
42
+
43
+ export const notifiableStatus = (s: AgentStatus): boolean =>
44
+ s === "waiting_for_human" || s === "error"
45
+
46
+ export const agentKey = (
47
+ a: Pick<Agent, "providerId" | "sessionId" | "instanceId">,
48
+ ): string => `${a.providerId}:${a.instanceId ?? a.sessionId}`
49
+
50
+ export const EMPTY_SNAPSHOT: AgentsSnapshot = {
51
+ byProvider: { opencode: [], "claude-code": [] },
52
+ attention: [],
53
+ generatedAt: 0,
54
+ }
55
+
56
+ export interface CoreMethods {
57
+ notify(args: { title: string; body: string; sound?: boolean }): Promise<void>
58
+ runCommand(
59
+ command: string,
60
+ options?: { timeoutMs?: number },
61
+ ): Promise<unknown>
62
+ navigateToDeck(args: { id: string; addToHistory?: boolean }): void
63
+ goBack(): void
64
+ }
65
+
66
+ export interface AgentProvider {
67
+ readonly id: ProviderId
68
+ readonly displayName: string
69
+ readonly logoPath: string
70
+ fetchSnapshot(signal: AbortSignal): Promise<readonly Agent[]>
71
+ subscribe(signal: AbortSignal, onChange: () => void): () => void
72
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "kind": "addon",
3
+ "apiVersion": 1,
4
+ "name": "coding-agents",
5
+ "entry": "manifest.ts"
6
+ }
@@ -0,0 +1,33 @@
1
+ // ponytail: as a builtin of the cli, this addon re-exports the runtime's
2
+ // type surface directly. The previous third-party addon shipped a
3
+ // minimal local mirror because third-party packages must not reach into
4
+ // the cli's @/ path aliases — builtins don't have that constraint.
5
+ export type {
6
+ AddonButtonService,
7
+ AddonButtonServiceContext,
8
+ AddonButtonTypeDef,
9
+ AddonButtonTypeDefAny,
10
+ AddonButtonTypeService,
11
+ AddonCheck,
12
+ AddonCheckResult,
13
+ AddonDeckDefinition,
14
+ AddonDeckEntry,
15
+ AddonDeckEntryCtx,
16
+ AddonDeckFactory,
17
+ AddonFrontend,
18
+ AddonFrontendButton,
19
+ AddonFrontendButtonProps,
20
+ AddonGeneratedDeck,
21
+ AddonGlobalPoller,
22
+ AddonGlobalService,
23
+ AddonGlobalSubscription,
24
+ AddonGestureEvent,
25
+ AddonLoadIssue,
26
+ AddonManifest,
27
+ AddonManifestKind,
28
+ AddonManifestV1,
29
+ AddonServiceContext,
30
+ AddonServiceMethod,
31
+ AddonKind,
32
+ GestureKind,
33
+ } from "@/addon/api"
@@ -0,0 +1,89 @@
1
+ # core
2
+
3
+ **Internal-only addon.** Provides the foundational button types every deck depends on. The runtime injects `core:change-deck` automatically (the reserved `n-1` slot); `core:action` and `core:toggle` are user-facing but the runtime also uses them internally for things like the settings back button.
4
+
5
+ If you're writing user-facing config, you only need `core:action` and `core:change-deck`. The other two are runtime-managed.
6
+
7
+ ## Buttons
8
+
9
+ | Type | Description |
10
+ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `core:change-deck` | Navigate to another deck. Internal: the reserved `n-1` slot. |
12
+ | `core:action` | Run a shell command (xdg-open, google-chrome, etc.) |
13
+ | `core:toggle` | Toggle a stateful button. Two modes: legacy `{key,default}` boolean flip, or `statusCommand`/`states` polled status display with per-state tap action |
14
+ | `core:page-nav` | Internal: next/previous deck navigation |
15
+ | `core:media-sample` | Internal: a sample media-player button used in tests |
16
+
17
+ ## Config
18
+
19
+ ### `core:action`
20
+
21
+ Requires at least one of `icon` or `label`. Command goes in `actions.tap`
22
+ (also accepts `type://<combo-or-text>` URIs for keystroke shortcuts).
23
+
24
+ ```yaml
25
+ - position: 1
26
+ type: core:action
27
+ actions:
28
+ tap: "xdg-open https://example.com"
29
+ config:
30
+ label: Open
31
+ icon: icon://globe
32
+ ```
33
+
34
+ ### `core:change-deck`
35
+
36
+ ```yaml
37
+ - position: 9
38
+ type: core:change-deck
39
+ config:
40
+ deck: emoji
41
+ ```
42
+
43
+ `deck` must reference a deck id defined in the same `config.yml` under `decks:`.
44
+
45
+ ### `core:toggle`
46
+
47
+ Two shapes are supported.
48
+
49
+ **Status mode (recommended):** poll a command, match its trimmed stdout against
50
+ `states`, render the matched state's label/icon, and run the matched state's
51
+ `onTap` on user tap. Per-button `intervalMs` (default 2000) bounds how often
52
+ `statusCommand` runs.
53
+
54
+ ```yaml
55
+ - position: 1
56
+ type: core:toggle
57
+ config:
58
+ statusCommand: "playerctl status"
59
+ intervalMs: 2000
60
+ timeoutMs: 5000
61
+ states:
62
+ Playing:
63
+ label: "<xl>$(playerctl title)</xl>"
64
+ icon: "icon://play"
65
+ onTap: "playerctl pause"
66
+ Paused:
67
+ label: "Paused"
68
+ icon: "icon://pause"
69
+ onTap: "playerctl play"
70
+ ```
71
+
72
+ Each state entry must declare at least one of `label` or `icon`. `onTap` is
73
+ optional — if omitted, tapping still triggers a republish so the deck reflects
74
+ the latest status without waiting for the next tick.
75
+
76
+ **Legacy mode:** keep the runtime boolean flip.
77
+
78
+ ```yaml
79
+ - position: 1
80
+ type: core:toggle
81
+ config:
82
+ key: my-flag
83
+ default: false
84
+ ```
85
+
86
+ ## See also
87
+
88
+ - [Internal settings](../internal-settings/README.md) — overlay deck
89
+ - [Session](../session/README.md) — `session:locked` deck
@@ -0,0 +1,7 @@
1
+ import type { AddonButtonTypeService } from "@/addon/api"
2
+
3
+ import { configSchema } from "./config"
4
+
5
+ export default {
6
+ configSchema,
7
+ } satisfies AddonButtonTypeService
@@ -0,0 +1,13 @@
1
+ import { z } from "zod"
2
+
3
+ import { IconSourceSchema } from "@/config/schemas"
4
+
5
+ export const configSchema = z
6
+ .object({
7
+ icon: IconSourceSchema.optional(),
8
+ label: z.string().optional(),
9
+ })
10
+ .refine((c) => Boolean(c.icon) || Boolean(c.label), {
11
+ message: "core:action requires at least one of 'icon' or 'label'",
12
+ })
13
+ export type ConfigSchema = z.infer<typeof configSchema>
@@ -0,0 +1,16 @@
1
+ import type { AddonFrontendButton } from "@/addon/api"
2
+ import { IconLabelSurface } from "@/ui/index"
3
+
4
+ type Config = { icon?: string; label?: string }
5
+
6
+ const CoreActionButtonFrontend: AddonFrontendButton<Config> = ({ config }) => {
7
+ const { icon, label } = config ?? {}
8
+ return (
9
+ <IconLabelSurface
10
+ {...(icon !== undefined ? { source: icon } : {})}
11
+ label={label ?? ""}
12
+ />
13
+ )
14
+ }
15
+
16
+ export default CoreActionButtonFrontend
@@ -0,0 +1,18 @@
1
+ import { z } from "zod"
2
+ import type { AddonButtonTypeService } from "@/addon/api"
3
+
4
+ import { configSchema } from "./config"
5
+
6
+ type Config = z.infer<typeof configSchema>
7
+
8
+ export default {
9
+ configSchema,
10
+ onTap: ({ config, publish, buttonId, position }) => {
11
+ publish("runtime:navigate-deck", {
12
+ deckId: config.deck,
13
+ addToHistory: config.addToHistory ?? true,
14
+ buttonId,
15
+ position,
16
+ })
17
+ },
18
+ } satisfies AddonButtonTypeService<Config>
@@ -0,0 +1,15 @@
1
+ import { z } from "zod"
2
+
3
+ import { IconSourceSchema } from "@/config/schemas"
4
+
5
+ export const configSchema = z
6
+ .object({
7
+ deck: z.string().min(1),
8
+ addToHistory: z.boolean().default(true).meta({ internal: true }),
9
+ icon: IconSourceSchema.optional(),
10
+ label: z.string().optional(),
11
+ })
12
+ .refine((c) => Boolean(c.icon) || Boolean(c.label), {
13
+ message: "core:change-deck requires at least one of 'icon' or 'label'",
14
+ })
15
+ export type ConfigSchema = z.infer<typeof configSchema>
@@ -0,0 +1,18 @@
1
+ import type { AddonFrontendButton } from "@/addon/api"
2
+ import { IconLabelSurface } from "@/ui/index"
3
+
4
+ type Config = { icon?: string; label?: string }
5
+
6
+ const CoreChangeDeckButtonFrontend: AddonFrontendButton<Config> = ({
7
+ config,
8
+ }) => {
9
+ const { icon, label } = config ?? {}
10
+ return (
11
+ <IconLabelSurface
12
+ {...(icon !== undefined ? { source: icon } : {})}
13
+ label={label ?? ""}
14
+ />
15
+ )
16
+ }
17
+
18
+ export default CoreChangeDeckButtonFrontend
@@ -0,0 +1,8 @@
1
+ import type { AddonButtonTypeService } from "@/addon/api"
2
+
3
+ import { configSchema } from "./config"
4
+
5
+ export default {
6
+ configSchema,
7
+ onTap: async () => {},
8
+ } satisfies AddonButtonTypeService
@@ -0,0 +1,7 @@
1
+ import { z } from "zod"
2
+
3
+ export const configSchema = z.object({
4
+ channel: z.string().min(1),
5
+ fallback: z.unknown().optional(),
6
+ })
7
+ export type ConfigSchema = z.infer<typeof configSchema>
@@ -0,0 +1,2 @@
1
+ import { NullButton } from "@/api/react"
2
+ export default NullButton
@@ -0,0 +1,28 @@
1
+ import { z } from "zod"
2
+ import type { AddonButtonTypeService } from "@/addon/api"
3
+
4
+ import { configSchema } from "./config"
5
+
6
+ type Config = z.infer<typeof configSchema>
7
+
8
+ export default {
9
+ configSchema,
10
+ onTap: ({ config, publish, buttonId, position }) => {
11
+ const { nextDeckId } = config as Config
12
+ publish("runtime:navigate-deck", {
13
+ deckId: nextDeckId,
14
+ addToHistory: false,
15
+ buttonId,
16
+ position,
17
+ })
18
+ },
19
+ onHold: ({ config, publish, buttonId, position }) => {
20
+ const { prevDeckId } = config as Config
21
+ publish("runtime:navigate-deck", {
22
+ deckId: prevDeckId,
23
+ addToHistory: false,
24
+ buttonId,
25
+ position,
26
+ })
27
+ },
28
+ } satisfies AddonButtonTypeService<Config>
@@ -0,0 +1,9 @@
1
+ import { z } from "zod"
2
+
3
+ export const configSchema = z.object({
4
+ currentPage: z.number().min(1),
5
+ totalPages: z.number().min(1),
6
+ prevDeckId: z.string().min(1),
7
+ nextDeckId: z.string().min(1),
8
+ })
9
+ export type ConfigSchema = z.infer<typeof configSchema>
@@ -0,0 +1,68 @@
1
+ import type { AddonFrontendButton } from "@/addon/api"
2
+ import { cn, Icon, TapIndicator, TapIndicatorType, Text } from "@/ui"
3
+
4
+ type Config = {
5
+ currentPage: number
6
+ totalPages: number
7
+ prevDeckId: string
8
+ nextDeckId: string
9
+ }
10
+
11
+ type NavActionProps = {
12
+ dir: "next" | "prev"
13
+ }
14
+
15
+ const icons: Record<NavActionProps["dir"], string> = {
16
+ next: "chevron-right",
17
+ prev: "chevron-left",
18
+ }
19
+
20
+ const tapTypes: Record<NavActionProps["dir"], TapIndicatorType> = {
21
+ next: "tap",
22
+ prev: "hold",
23
+ }
24
+
25
+ const NavAction = ({ dir = "prev" }: NavActionProps) => {
26
+ const icon = icons[dir]
27
+ const tapType = tapTypes[dir]
28
+
29
+ return (
30
+ <div
31
+ className={cn(
32
+ "flex items-center gap-0",
33
+ dir === "prev" ? "flex-row" : "flex-row-reverse",
34
+ )}
35
+ >
36
+ <Icon source={`icon://${icon}`} size={30} />
37
+ <TapIndicator type={tapType} size="xs" />
38
+ </div>
39
+ )
40
+ }
41
+
42
+ const PageIndicator = ({
43
+ current,
44
+ total,
45
+ }: {
46
+ current: number
47
+ total: number
48
+ }) => <Text size="md" tone="muted" text={`*${current}* / ${total}`} />
49
+
50
+ const PageNavButtonFrontend: AddonFrontendButton<Config> = ({ config }) => {
51
+ const { currentPage, totalPages } = config
52
+ const isFirstPage = currentPage === 1
53
+ const isLastPage = currentPage === totalPages
54
+
55
+ return (
56
+ <div className="size-full flex flex-col gap-0.5 justify-center items-center">
57
+ <div className={cn(isFirstPage ? "invisible" : "visible", "-ml-7")}>
58
+ <NavAction dir="prev" />
59
+ </div>
60
+ <PageIndicator current={currentPage} total={totalPages} />
61
+ <div className={cn(isLastPage ? "invisible" : "visible", "ml-7")}>
62
+ <NavAction dir="next" />
63
+ </div>
64
+ </div>
65
+ )
66
+ }
67
+
68
+ export default PageNavButtonFrontend
@@ -0,0 +1,119 @@
1
+ import type { Methods } from "@/deck/methods"
2
+ import type { Store } from "@/core/store"
3
+ import type {
4
+ AddonButtonServiceContext,
5
+ AddonButtonTypeService,
6
+ } from "@/addon/api"
7
+
8
+ import {
9
+ configSchema,
10
+ isStatusToggleConfig,
11
+ type ConfigSchema,
12
+ type LegacyToggleConfig,
13
+ type StatusToggleConfig,
14
+ } from "./config"
15
+
16
+ // ponytail: bridge namespaces addon-global methods as `${addonName}:${methodName}`.
17
+ // core addon's method names are `register` / `unregister` / `republish` / `lookup`,
18
+ // so the namespaced keys are `core:register` etc.
19
+ const REG_METHOD = "core:register"
20
+ const UNREG_METHOD = "core:unregister"
21
+ const REPUBLISH_METHOD = "core:republish"
22
+ const LOOKUP_METHOD = "core:lookup"
23
+
24
+ interface ToggleStateLike {
25
+ readonly raw: string
26
+ readonly state: string | undefined
27
+ readonly error?: string
28
+ readonly at: number
29
+ }
30
+
31
+ export default {
32
+ configSchema,
33
+ onMount: (ctx: AddonButtonServiceContext<ConfigSchema>) => {
34
+ if (!isStatusToggleConfig(ctx.config)) return
35
+ const register = (
36
+ ctx.methods as unknown as Record<
37
+ string,
38
+ ((...a: unknown[]) => unknown) | undefined
39
+ >
40
+ )[REG_METHOD]
41
+ register?.(ctx.buttonId, ctx.config)
42
+ },
43
+ dispose: (ctx: AddonButtonServiceContext<ConfigSchema>) => {
44
+ if (!isStatusToggleConfig(ctx.config)) return
45
+ const unregister = (
46
+ ctx.methods as unknown as Record<
47
+ string,
48
+ ((...a: unknown[]) => unknown) | undefined
49
+ >
50
+ )[UNREG_METHOD]
51
+ unregister?.(ctx.buttonId)
52
+ },
53
+ onTap: async (ctx: AddonButtonServiceContext<ConfigSchema>) => {
54
+ const { config, methods, store, coreMethods, buttonId } =
55
+ ctx as unknown as {
56
+ config: ConfigSchema
57
+ methods: Methods
58
+ store: Store
59
+ coreMethods: Methods
60
+ buttonId: string
61
+ }
62
+ if (!isStatusToggleConfig(config)) {
63
+ await legacyTap({ config: config as LegacyToggleConfig, methods, store })
64
+ return
65
+ }
66
+ await statusTap({ config, methods, coreMethods, buttonId }, config)
67
+ },
68
+ } satisfies AddonButtonTypeService<ConfigSchema>
69
+
70
+ const legacyTap = async (ctx: {
71
+ config: LegacyToggleConfig
72
+ methods: Methods
73
+ store: Store
74
+ }): Promise<void> => {
75
+ const scope = ctx.store.buttonScope<boolean>("core", ctx.config.key)
76
+ const current = scope.get("value") ?? ctx.config.default
77
+ scope.set("value", !current)
78
+ ctx.methods.invalidate()
79
+ }
80
+
81
+ const statusTap = async (
82
+ ctx: {
83
+ config: StatusToggleConfig
84
+ methods: Methods
85
+ coreMethods: Methods
86
+ buttonId: string
87
+ },
88
+ config: StatusToggleConfig,
89
+ ): Promise<void> => {
90
+ const lookup = (
91
+ ctx.methods as unknown as Record<
92
+ string,
93
+ ((...a: unknown[]) => unknown) | undefined
94
+ >
95
+ )[LOOKUP_METHOD] as
96
+ | ((id: unknown) => Promise<ToggleStateLike | null>)
97
+ | undefined
98
+ let stateKey: string | undefined
99
+ if (lookup !== undefined) {
100
+ const current = await lookup(ctx.buttonId)
101
+ stateKey = current?.state
102
+ }
103
+ const entry = stateKey !== undefined ? config.states[stateKey] : undefined
104
+ const command = entry?.onTap
105
+ const republish = (
106
+ ctx.methods as unknown as Record<
107
+ string,
108
+ ((...a: unknown[]) => unknown) | undefined
109
+ >
110
+ )[REPUBLISH_METHOD] as ((buttonId?: unknown) => void) | undefined
111
+ if (command === undefined || command.length === 0) {
112
+ republish?.(ctx.buttonId)
113
+ return
114
+ }
115
+ await ctx.coreMethods.runCommand(command, {
116
+ timeoutMs: config.timeoutMs,
117
+ } as Parameters<typeof ctx.coreMethods.runCommand>[1])
118
+ republish?.(ctx.buttonId)
119
+ }
@@ -0,0 +1,57 @@
1
+ import { z } from "zod"
2
+
3
+ export const TOGGLE_DEFAULT_INTERVAL_MS = 2000
4
+ export const TOGGLE_DEFAULT_TIMEOUT_MS = 5000
5
+
6
+ export const StatusToggleStateSchema = z
7
+ .object({
8
+ label: z.string().min(1).optional(),
9
+ icon: z.string().min(1).optional(),
10
+ onTap: z.string().min(1).optional(),
11
+ })
12
+ .strict()
13
+ .refine((s) => s.label !== undefined || s.icon !== undefined, {
14
+ message: "each state requires at least one of `label` or `icon`",
15
+ })
16
+
17
+ export type StatusToggleState = z.infer<typeof StatusToggleStateSchema>
18
+
19
+ export const StatusToggleConfigSchema = z
20
+ .object({
21
+ statusCommand: z.string().min(1),
22
+ intervalMs: z.number().int().positive().optional(),
23
+ timeoutMs: z.number().int().positive().optional(),
24
+ states: z.record(z.string().min(1), StatusToggleStateSchema),
25
+ })
26
+ .strict()
27
+ .refine((c) => Object.keys(c.states).length > 0, {
28
+ message: "`states` must declare at least one entry",
29
+ })
30
+
31
+ export type StatusToggleConfig = z.infer<typeof StatusToggleConfigSchema>
32
+
33
+ export const LegacyToggleConfigSchema = z
34
+ .object({
35
+ key: z.string().min(1),
36
+ default: z.boolean().default(false),
37
+ })
38
+ .strict()
39
+
40
+ export type LegacyToggleConfig = z.infer<typeof LegacyToggleConfigSchema>
41
+
42
+ export const configSchema = z.union([
43
+ StatusToggleConfigSchema,
44
+ LegacyToggleConfigSchema,
45
+ ])
46
+
47
+ export type ConfigSchema = z.infer<typeof configSchema>
48
+
49
+ export const isStatusToggleConfig = (
50
+ value: unknown,
51
+ ): value is StatusToggleConfig =>
52
+ StatusToggleConfigSchema.safeParse(value).success
53
+
54
+ export const isLegacyToggleConfig = (
55
+ value: unknown,
56
+ ): value is LegacyToggleConfig =>
57
+ LegacyToggleConfigSchema.safeParse(value).success