@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,651 @@
1
+ import type pino from "pino"
2
+
3
+ import { ProviderError } from "../error"
4
+ import { createClipboardProvider, type ClipboardProvider } from "../clipboard"
5
+ import { type CommandExecutor, withTimeout } from "../shared"
6
+ import { type KeyMacroProvider } from "../key-macro"
7
+ import { parseCombo } from "./parser"
8
+
9
+ export interface LinuxKeyMacroDeps {
10
+ readonly executor: CommandExecutor
11
+ readonly env: Readonly<Record<string, string>>
12
+ readonly logger: pino.Logger
13
+ readonly timeoutMs?: number
14
+ readonly extraFsProbe?: (tool: string) => boolean
15
+ }
16
+
17
+ const YDOTOOL_TOOL = "ydotool"
18
+ const WTYPE_TOOL = "wtype"
19
+
20
+ type Tool = "ydotool" | "wtype"
21
+
22
+ const YDOTOOL_CLIPBOARD_PRE_DELAY_MS = 50
23
+
24
+ // Linux input-event-codes.h — only the codes we need.
25
+ const SC_LEFTCTRL = 29
26
+ const SC_LEFTALT = 56
27
+ const SC_LEFTSHIFT = 42
28
+ const SC_LEFTMETA = 125 // also used for Super/Hyper
29
+
30
+ const SC_ESC = 1
31
+ const SC_1 = 2
32
+ const SC_2 = 3
33
+ const SC_3 = 4
34
+ const SC_4 = 5
35
+ const SC_5 = 6
36
+ const SC_6 = 7
37
+ const SC_7 = 8
38
+ const SC_8 = 9
39
+ const SC_9 = 10
40
+ const SC_0 = 11
41
+ const SC_MINUS = 12
42
+ const SC_EQUAL = 13
43
+ const SC_BACKSPACE = 14
44
+ const SC_TAB = 15
45
+ const SC_Q = 16
46
+ const SC_W = 17
47
+ const SC_E = 18
48
+ const SC_R = 19
49
+ const SC_T = 20
50
+ const SC_Y = 21
51
+ const SC_U = 22
52
+ const SC_I = 23
53
+ const SC_O = 24
54
+ const SC_P = 25
55
+ const SC_LEFTBRACE = 26
56
+ const SC_RIGHTBRACE = 27
57
+ const SC_ENTER = 28
58
+ const SC_A = 30
59
+ const SC_S = 31
60
+ const SC_D = 32
61
+ const SC_F = 33
62
+ const SC_G = 34
63
+ const SC_H = 35
64
+ const SC_J = 36
65
+ const SC_K = 37
66
+ const SC_L = 38
67
+ const SC_SEMICOLON = 39
68
+ const SC_APOSTROPHE = 40
69
+ const SC_GRAVE = 41
70
+ const SC_BACKSLASH = 43
71
+ const SC_Z = 44
72
+ const SC_X = 45
73
+ const SC_C = 46
74
+ const SC_V = 47
75
+ const SC_B = 48
76
+ const SC_N = 49
77
+ const SC_M = 50
78
+ const SC_COMMA = 51
79
+ const SC_DOT = 52
80
+ const SC_SLASH = 53
81
+ const SC_KPDOT = 83
82
+ const SC_KP0 = 82
83
+ const SC_KP1 = 79
84
+ const SC_KP2 = 80
85
+ const SC_KP3 = 81
86
+ const SC_KP4 = 75
87
+ const SC_KP5 = 76
88
+ const SC_KP6 = 77
89
+ const SC_KP7 = 71
90
+ const SC_KP8 = 72
91
+ const SC_KP9 = 73
92
+ const SC_CAPSLOCK = 58
93
+ const SC_SPACE = 57
94
+ const SC_F1 = 59
95
+ const SC_F2 = 60
96
+ const SC_F3 = 61
97
+ const SC_F4 = 62
98
+ const SC_F5 = 63
99
+ const SC_F6 = 64
100
+ const SC_F7 = 65
101
+ const SC_F8 = 66
102
+ const SC_F9 = 67
103
+ const SC_F10 = 68
104
+ const SC_F11 = 87
105
+ const SC_F12 = 88
106
+ const SC_NUMLOCK = 69
107
+ const SC_SCROLLLOCK = 70
108
+ const SC_KPENTER = 96
109
+ const SC_KPPLUS = 78
110
+ const SC_KPMINUS = 74
111
+ const SC_KPASTERISK = 55
112
+ const SC_KPSLASH = 98
113
+ const SC_HOME = 102
114
+ const SC_UP = 103
115
+ const SC_PAGEUP = 104
116
+ const SC_LEFT = 105
117
+ const SC_RIGHT = 106
118
+ const SC_END = 107
119
+ const SC_DOWN = 108
120
+ const SC_PAGEDOWN = 109
121
+ const SC_INSERT = 110
122
+ const SC_DELETE = 111
123
+ const SC_PAUSE = 119
124
+ const SC_PRINT = 99
125
+ const SC_MENU = 139
126
+ const SC_F13 = 183
127
+ const SC_F14 = 184
128
+ const SC_F15 = 185
129
+ const SC_F16 = 186
130
+ const SC_F17 = 187
131
+ const SC_F18 = 188
132
+ const SC_F19 = 189
133
+ const SC_F20 = 190
134
+ const SC_F21 = 191
135
+ const SC_F22 = 192
136
+ const SC_F23 = 193
137
+ const SC_F24 = 194
138
+
139
+ const MOD_SCANCODES: ReadonlyMap<string, number> = new Map<string, number>([
140
+ ["ctrl", SC_LEFTCTRL],
141
+ ["alt", SC_LEFTALT],
142
+ ["shift", SC_LEFTSHIFT],
143
+ ["meta", SC_LEFTMETA],
144
+ ["super", SC_LEFTMETA],
145
+ ["hyper", SC_LEFTMETA],
146
+ ])
147
+
148
+ const MOD_ORDER: ReadonlyArray<string> = ["ctrl", "alt", "shift", "super"]
149
+
150
+ const modToScancode = (mod: string): number | null =>
151
+ MOD_SCANCODES.get(mod) ?? null
152
+
153
+ const namedKeyToScancode = (name: string): number | null => {
154
+ switch (name) {
155
+ case "Escape":
156
+ return SC_ESC
157
+ case "Tab":
158
+ return SC_TAB
159
+ case "Return":
160
+ return SC_ENTER
161
+ case "BackSpace":
162
+ return SC_BACKSPACE
163
+ case "Caps_Lock":
164
+ return SC_CAPSLOCK
165
+ case "Num_Lock":
166
+ return SC_NUMLOCK
167
+ case "Scroll_Lock":
168
+ return SC_SCROLLLOCK
169
+ case "space":
170
+ return SC_SPACE
171
+ case "Print":
172
+ return SC_PRINT
173
+ case "Pause":
174
+ return SC_PAUSE
175
+ case "Menu":
176
+ return SC_MENU
177
+ case "Insert":
178
+ return SC_INSERT
179
+ case "Delete":
180
+ return SC_DELETE
181
+ case "Home":
182
+ return SC_HOME
183
+ case "End":
184
+ return SC_END
185
+ case "Page_Up":
186
+ return SC_PAGEUP
187
+ case "Page_Down":
188
+ return SC_PAGEDOWN
189
+ case "Up":
190
+ return SC_UP
191
+ case "Down":
192
+ return SC_DOWN
193
+ case "Left":
194
+ return SC_LEFT
195
+ case "Right":
196
+ return SC_RIGHT
197
+ case "equal":
198
+ return SC_EQUAL
199
+ case "minus":
200
+ return SC_MINUS
201
+ case "comma":
202
+ return SC_COMMA
203
+ case "period":
204
+ return SC_DOT
205
+ case "F1":
206
+ return SC_F1
207
+ case "F2":
208
+ return SC_F2
209
+ case "F3":
210
+ return SC_F3
211
+ case "F4":
212
+ return SC_F4
213
+ case "F5":
214
+ return SC_F5
215
+ case "F6":
216
+ return SC_F6
217
+ case "F7":
218
+ return SC_F7
219
+ case "F8":
220
+ return SC_F8
221
+ case "F9":
222
+ return SC_F9
223
+ case "F10":
224
+ return SC_F10
225
+ case "F11":
226
+ return SC_F11
227
+ case "F12":
228
+ return SC_F12
229
+ case "F13":
230
+ return SC_F13
231
+ case "F14":
232
+ return SC_F14
233
+ case "F15":
234
+ return SC_F15
235
+ case "F16":
236
+ return SC_F16
237
+ case "F17":
238
+ return SC_F17
239
+ case "F18":
240
+ return SC_F18
241
+ case "F19":
242
+ return SC_F19
243
+ case "F20":
244
+ return SC_F20
245
+ case "F21":
246
+ return SC_F21
247
+ case "F22":
248
+ return SC_F22
249
+ case "F23":
250
+ return SC_F23
251
+ case "F24":
252
+ return SC_F24
253
+ default:
254
+ return null
255
+ }
256
+ }
257
+
258
+ const LETTER_SC: ReadonlyMap<string, number> = new Map<string, number>([
259
+ ["a", SC_A],
260
+ ["b", SC_B],
261
+ ["c", SC_C],
262
+ ["d", SC_D],
263
+ ["e", SC_E],
264
+ ["f", SC_F],
265
+ ["g", SC_G],
266
+ ["h", SC_H],
267
+ ["i", SC_I],
268
+ ["j", SC_J],
269
+ ["k", SC_K],
270
+ ["l", SC_L],
271
+ ["m", SC_M],
272
+ ["n", SC_N],
273
+ ["o", SC_O],
274
+ ["p", SC_P],
275
+ ["q", SC_Q],
276
+ ["r", SC_R],
277
+ ["s", SC_S],
278
+ ["t", SC_T],
279
+ ["u", SC_U],
280
+ ["v", SC_V],
281
+ ["w", SC_W],
282
+ ["x", SC_X],
283
+ ["y", SC_Y],
284
+ ["z", SC_Z],
285
+ ])
286
+
287
+ const DIGIT_SC: ReadonlyMap<string, number> = new Map<string, number>([
288
+ ["0", SC_0],
289
+ ["1", SC_1],
290
+ ["2", SC_2],
291
+ ["3", SC_3],
292
+ ["4", SC_4],
293
+ ["5", SC_5],
294
+ ["6", SC_6],
295
+ ["7", SC_7],
296
+ ["8", SC_8],
297
+ ["9", SC_9],
298
+ ])
299
+
300
+ const KEY_ALIASES: ReadonlyMap<string, string> = new Map([
301
+ ["plus", "equal"],
302
+ ["minus", "minus"],
303
+ ["equal", "equal"],
304
+ ["comma", "comma"],
305
+ ["period", "period"],
306
+ ])
307
+
308
+ const keyToScancode = (key: string): number | null => {
309
+ const normalized = KEY_ALIASES.get(key) ?? key
310
+ const fromNamed = namedKeyToScancode(normalized)
311
+ if (fromNamed !== null) return fromNamed
312
+ const fromLetter = LETTER_SC.get(normalized)
313
+ if (fromLetter !== undefined) return fromLetter
314
+ const fromDigit = DIGIT_SC.get(normalized)
315
+ if (fromDigit !== undefined) return fromDigit
316
+ return null
317
+ }
318
+
319
+ const probeTool = async (
320
+ executor: CommandExecutor,
321
+ tool: Tool,
322
+ extraFsProbe?: (tool: string) => boolean,
323
+ ): Promise<boolean> => {
324
+ const result = await executor.run("which", [tool])
325
+ if (result.exitCode === 0 && result.stdout.trim().length > 0) return true
326
+ // ponytail: fallback when CLI is launched with a stripped PATH (systemd,
327
+ // launchd, IDE runners) and `which` returns nothing even though ydotool is
328
+ // installed at e.g. /usr/local/bin. Mirror of the requirements.ts probe.
329
+ return extraFsProbe?.(tool) === true
330
+ }
331
+
332
+ const shellQuote = (value: string): string =>
333
+ `'${value.replace(/'/g, "'\\''")}'`
334
+
335
+ const YDOTOOL_CTRL_V_ARGS: string[] = [
336
+ "key",
337
+ `${SC_LEFTCTRL}:1`,
338
+ `${SC_V}:1`,
339
+ `${SC_V}:0`,
340
+ `${SC_LEFTCTRL}:0`,
341
+ ]
342
+
343
+ const SHIFT_NEEDED_KEYS = new Set(["plus", "minus"])
344
+
345
+ const buildYdotoolComboArgs = (input: string): string[] | null => {
346
+ const parsed = parseCombo(input)
347
+ if (parsed === null) return null
348
+
349
+ const seenSc = new Set<number>()
350
+ const orderedMods: number[] = []
351
+ for (const mod of MOD_ORDER) {
352
+ if (!parsed.mods.includes(mod)) continue
353
+ const sc = modToScancode(mod)
354
+ if (sc === null) {
355
+ throw new ProviderError(
356
+ "EXEC_FAILED",
357
+ `ydotool: cannot map modifier '${mod}' to scancode`,
358
+ )
359
+ }
360
+ if (seenSc.has(sc)) continue
361
+ seenSc.add(sc)
362
+ orderedMods.push(sc)
363
+ }
364
+ for (const mod of parsed.mods) {
365
+ const sc = modToScancode(mod)
366
+ if (sc === null) {
367
+ throw new ProviderError(
368
+ "EXEC_FAILED",
369
+ `ydotool: cannot map modifier '${mod}' to scancode`,
370
+ )
371
+ }
372
+ if (seenSc.has(sc)) continue
373
+ seenSc.add(sc)
374
+ orderedMods.push(sc)
375
+ }
376
+
377
+ const keySc = keyToScancode(parsed.key)
378
+ if (keySc === null) {
379
+ throw new ProviderError(
380
+ "EXEC_FAILED",
381
+ `ydotool: cannot map key '${parsed.key}' to scancode (combo keys must be alphanumeric or a named key)`,
382
+ )
383
+ }
384
+
385
+ const needsShift =
386
+ SHIFT_NEEDED_KEYS.has(parsed.key) && !parsed.mods.includes("shift")
387
+ if (needsShift && !seenSc.has(SC_LEFTSHIFT)) {
388
+ seenSc.add(SC_LEFTSHIFT)
389
+ orderedMods.push(SC_LEFTSHIFT)
390
+ }
391
+
392
+ const args: string[] = ["key"]
393
+ for (const sc of orderedMods) args.push(`${sc}:1`)
394
+ args.push(`${keySc}:1`)
395
+ args.push(`${keySc}:0`)
396
+ for (let i = orderedMods.length - 1; i >= 0; i -= 1) {
397
+ args.push(`${orderedMods[i]}:0`)
398
+ }
399
+ return args
400
+ }
401
+
402
+ const buildYdotoolLiteralArgs = (text: string): string[] => {
403
+ return ["type", "--", text]
404
+ }
405
+
406
+ const buildYdotoolArgs = (input: string): string[] => {
407
+ const combo = buildYdotoolComboArgs(input)
408
+ if (combo !== null) return combo
409
+ return buildYdotoolLiteralArgs(input)
410
+ }
411
+
412
+ const buildWtypeComboArgs = (input: string): string[] | null => {
413
+ const parsed = parseCombo(input)
414
+ if (parsed === null) return null
415
+
416
+ const seen = new Set<string>()
417
+ const orderedMods: string[] = []
418
+ for (const mod of MOD_ORDER) {
419
+ if (parsed.mods.includes(mod) && !seen.has(mod)) {
420
+ let wtype = mod
421
+ if (mod === "meta" || mod === "hyper") wtype = "super"
422
+ seen.add(mod)
423
+ orderedMods.push(wtype)
424
+ }
425
+ }
426
+ for (const mod of parsed.mods) {
427
+ if (seen.has(mod)) continue
428
+ let wtype = mod
429
+ if (mod === "meta" || mod === "hyper") wtype = "super"
430
+ seen.add(mod)
431
+ orderedMods.push(wtype)
432
+ }
433
+
434
+ const args: string[] = []
435
+ for (const m of orderedMods) args.push("-M", m)
436
+ args.push("-k", parsed.key)
437
+ for (let i = orderedMods.length - 1; i >= 0; i -= 1) {
438
+ args.push("-m", orderedMods[i]!)
439
+ }
440
+ return args
441
+ }
442
+
443
+ const buildWtypeLiteralArgs = (text: string): string[] => {
444
+ return [shellQuote(text)]
445
+ }
446
+
447
+ const buildWtypeArgs = (input: string): string[] => {
448
+ const combo = buildWtypeComboArgs(input)
449
+ if (combo !== null) return combo
450
+ return buildWtypeLiteralArgs(input)
451
+ }
452
+
453
+ const isPureAscii = (text: string): boolean => /^[\x20-\x7E]*$/.test(text)
454
+
455
+ const sleep = (ms: number): Promise<void> =>
456
+ new Promise((resolve) => setTimeout(resolve, ms))
457
+
458
+ const runYdotool = async (
459
+ args: string[],
460
+ deps: LinuxKeyMacroDeps,
461
+ ): Promise<void> => {
462
+ const baseTimeoutMs = deps.timeoutMs ?? 500
463
+ try {
464
+ const result = await withTimeout(
465
+ deps.executor.run(YDOTOOL_TOOL, args),
466
+ baseTimeoutMs + 2500,
467
+ )
468
+ if (result.exitCode !== 0) {
469
+ throw new ProviderError(
470
+ "EXEC_FAILED",
471
+ `${YDOTOOL_TOOL} ${args.join(" ")} exited with code ${result.exitCode}: ${result.stderr.trim()}`,
472
+ )
473
+ }
474
+ } catch (err) {
475
+ if (err instanceof ProviderError) throw err
476
+ throw new ProviderError(
477
+ "EXEC_FAILED",
478
+ `${YDOTOOL_TOOL} failed: ${err instanceof Error ? err.message : String(err)}`,
479
+ )
480
+ }
481
+ }
482
+
483
+ const runWtype = async (
484
+ args: string[],
485
+ deps: LinuxKeyMacroDeps,
486
+ ): Promise<void> => {
487
+ const baseTimeoutMs = deps.timeoutMs ?? 500
488
+ try {
489
+ const result = await withTimeout(
490
+ deps.executor.run(WTYPE_TOOL, args),
491
+ baseTimeoutMs + 2500,
492
+ )
493
+ if (result.exitCode !== 0) {
494
+ throw new ProviderError(
495
+ "EXEC_FAILED",
496
+ `${WTYPE_TOOL} ${args.join(" ")} exited with code ${result.exitCode}: ${result.stderr.trim()}`,
497
+ )
498
+ }
499
+ } catch (err) {
500
+ if (err instanceof ProviderError) throw err
501
+ throw new ProviderError(
502
+ "EXEC_FAILED",
503
+ `${WTYPE_TOOL} failed: ${err instanceof Error ? err.message : String(err)}`,
504
+ )
505
+ }
506
+ }
507
+
508
+ const sendKeyWtype = async (
509
+ input: string,
510
+ deps: LinuxKeyMacroDeps,
511
+ ): Promise<void> => {
512
+ await runWtype(buildWtypeArgs(input), deps)
513
+ }
514
+
515
+ const sendKeyYdotool = async (
516
+ input: string,
517
+ deps: LinuxKeyMacroDeps,
518
+ clipboardProvider: ClipboardProvider | undefined,
519
+ ): Promise<void> => {
520
+ const parsed = parseCombo(input)
521
+ if (parsed !== null) {
522
+ await runYdotool(buildYdotoolComboArgs(input)!, deps)
523
+ return
524
+ }
525
+
526
+ if (isPureAscii(input)) {
527
+ await runYdotool(buildYdotoolLiteralArgs(input), deps)
528
+ return
529
+ }
530
+
531
+ if (clipboardProvider === undefined) {
532
+ throw new ProviderError(
533
+ "NOT_AVAILABLE",
534
+ "non-ASCII text (e.g. emoji) on Linux needs wl-copy on PATH — install the wl-clipboard package",
535
+ )
536
+ }
537
+
538
+ const totalStart = Date.now()
539
+ deps.logger.info(
540
+ {
541
+ step: "keyMacro.nonAscii.start",
542
+ textPreview: input.slice(0, 24),
543
+ },
544
+ "keyMacro: typing non-ASCII via clipboard+ctrl+v",
545
+ )
546
+ const writeStart = Date.now()
547
+ await clipboardProvider.writeText(input)
548
+ deps.logger.info(
549
+ {
550
+ step: "keyMacro.nonAscii.afterWrite",
551
+ writeElapsedMs: Date.now() - writeStart,
552
+ },
553
+ "keyMacro: clipboard writeText done",
554
+ )
555
+
556
+ deps.logger.info(
557
+ { step: "keyMacro.nonAscii.preDelay", ms: YDOTOOL_CLIPBOARD_PRE_DELAY_MS },
558
+ "keyMacro: pre-keystroke delay",
559
+ )
560
+ await sleep(YDOTOOL_CLIPBOARD_PRE_DELAY_MS)
561
+
562
+ const keystrokeStart = Date.now()
563
+ await runYdotool(YDOTOOL_CTRL_V_ARGS, deps)
564
+ deps.logger.info(
565
+ {
566
+ step: "keyMacro.nonAscii.afterKeystroke",
567
+ keystrokeElapsedMs: Date.now() - keystrokeStart,
568
+ totalElapsedMs: Date.now() - totalStart,
569
+ },
570
+ "keyMacro: ctrl+v keystroke done",
571
+ )
572
+ }
573
+
574
+ export const createLinuxKeyMacroProvider = async (
575
+ deps: LinuxKeyMacroDeps,
576
+ ): Promise<KeyMacroProvider> => {
577
+ const ydotoolOk = await probeTool(
578
+ deps.executor,
579
+ YDOTOOL_TOOL,
580
+ deps.extraFsProbe,
581
+ )
582
+ const wtypeOk =
583
+ !ydotoolOk &&
584
+ (await probeTool(deps.executor, WTYPE_TOOL, deps.extraFsProbe))
585
+
586
+ if (!ydotoolOk && !wtypeOk) {
587
+ deps.logger.warn(
588
+ {},
589
+ "no key-macro tool found (need ydotool or wtype on PATH); key-macro will throw ProviderError",
590
+ )
591
+ return {
592
+ async sendKey(_input: string) {
593
+ throw new ProviderError(
594
+ "NOT_AVAILABLE",
595
+ "no key-macro tool found on PATH (need ydotool or wtype)",
596
+ )
597
+ },
598
+ async stop() {
599
+ return
600
+ },
601
+ }
602
+ }
603
+
604
+ const tool: Tool = ydotoolOk ? YDOTOOL_TOOL : WTYPE_TOOL
605
+ deps.logger.info({ tool }, "Linux key-macro tool selected")
606
+
607
+ let clipboardProvider: ClipboardProvider | undefined
608
+ if (tool === YDOTOOL_TOOL) {
609
+ try {
610
+ clipboardProvider = createClipboardProvider({
611
+ // ponytail: createClipboardProvider defaults to process.platform, so
612
+ // the LINUX key-macro provider was handing itself a darwin clipboard
613
+ // (pbcopy) whenever it ran on a Mac. The per-OS providers are chosen
614
+ // by the caller everywhere else; pin it here too rather than letting
615
+ // the host OS decide what a linux provider does.
616
+ platform: "linux",
617
+ executor: deps.executor,
618
+ env: deps.env,
619
+ logger: deps.logger,
620
+ ...(deps.extraFsProbe !== undefined
621
+ ? { extraFsProbe: deps.extraFsProbe }
622
+ : {}),
623
+ })
624
+ } catch (err) {
625
+ deps.logger.warn(
626
+ { err },
627
+ "linux clipboard provider unavailable; non-ASCII typing will throw",
628
+ )
629
+ }
630
+ }
631
+
632
+ if (tool === YDOTOOL_TOOL) {
633
+ return {
634
+ async sendKey(input: string) {
635
+ await sendKeyYdotool(input, deps, clipboardProvider)
636
+ },
637
+ async stop() {
638
+ return
639
+ },
640
+ }
641
+ }
642
+
643
+ return {
644
+ async sendKey(input: string) {
645
+ await sendKeyWtype(input, deps)
646
+ },
647
+ async stop() {
648
+ return
649
+ },
650
+ }
651
+ }