@overcrow/mcp 0.0.0 → 0.1.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 (461) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -1
  3. package/dist/audit/index.js +103 -0
  4. package/dist/audit/lexer.js +417 -0
  5. package/dist/audit/project.js +200 -0
  6. package/dist/audit/rules.js +661 -0
  7. package/dist/bootstrap/download.js +229 -0
  8. package/dist/bootstrap/install.js +280 -0
  9. package/dist/bootstrap/pin.js +85 -0
  10. package/dist/bootstrap/pin.json +119 -0
  11. package/dist/bootstrap/zip.js +258 -0
  12. package/dist/cli.js +200 -0
  13. package/dist/confine.js +244 -0
  14. package/dist/content/docs/en/cli.md +353 -0
  15. package/dist/content/docs/en/dev-channel.md +130 -0
  16. package/dist/content/docs/en/elements.md +469 -0
  17. package/dist/content/docs/en/forms.md +328 -0
  18. package/dist/content/docs/en/guide.md +253 -0
  19. package/dist/content/docs/en/index.md +85 -0
  20. package/dist/content/docs/en/limits.md +241 -0
  21. package/dist/content/docs/en/logic.md +446 -0
  22. package/dist/content/docs/en/manifest.md +381 -0
  23. package/dist/content/docs/en/mcp.md +165 -0
  24. package/dist/content/docs/en/package.md +131 -0
  25. package/dist/content/docs/en/publishing.md +162 -0
  26. package/dist/content/docs/en/reference.md +55 -0
  27. package/dist/content/docs/en/result-shapes.md +332 -0
  28. package/dist/content/docs/en/sdk.md +295 -0
  29. package/dist/content/docs/en/security.md +115 -0
  30. package/dist/content/docs/en/service-reference.md +344 -0
  31. package/dist/content/docs/en/services.md +517 -0
  32. package/dist/content/docs/en/style-properties.md +270 -0
  33. package/dist/content/docs/en/style.md +266 -0
  34. package/dist/content/docs/en/testing.md +400 -0
  35. package/dist/content/docs/en/view.md +338 -0
  36. package/dist/content/docs/en/widgets.md +76 -0
  37. package/dist/content/docs/fr/cli.md +367 -0
  38. package/dist/content/docs/fr/dev-channel.md +140 -0
  39. package/dist/content/docs/fr/elements.md +475 -0
  40. package/dist/content/docs/fr/forms.md +341 -0
  41. package/dist/content/docs/fr/guide.md +264 -0
  42. package/dist/content/docs/fr/index.md +90 -0
  43. package/dist/content/docs/fr/limits.md +244 -0
  44. package/dist/content/docs/fr/logic.md +461 -0
  45. package/dist/content/docs/fr/manifest.md +392 -0
  46. package/dist/content/docs/fr/mcp.md +173 -0
  47. package/dist/content/docs/fr/package.md +139 -0
  48. package/dist/content/docs/fr/publishing.md +172 -0
  49. package/dist/content/docs/fr/reference.md +59 -0
  50. package/dist/content/docs/fr/result-shapes.md +333 -0
  51. package/dist/content/docs/fr/sdk.md +301 -0
  52. package/dist/content/docs/fr/security.md +129 -0
  53. package/dist/content/docs/fr/service-reference.md +346 -0
  54. package/dist/content/docs/fr/services.md +540 -0
  55. package/dist/content/docs/fr/style-properties.md +272 -0
  56. package/dist/content/docs/fr/style.md +281 -0
  57. package/dist/content/docs/fr/testing.md +417 -0
  58. package/dist/content/docs/fr/view.md +352 -0
  59. package/dist/content/docs/fr/widgets.md +81 -0
  60. package/dist/content/examples/clock/LICENSE +21 -0
  61. package/dist/content/examples/clock/README.md +75 -0
  62. package/dist/content/examples/clock/listing.json +19 -0
  63. package/dist/content/examples/clock/locales/en.json +4 -0
  64. package/dist/content/examples/clock/locales/fr.json +4 -0
  65. package/dist/content/examples/clock/logic.ts +85 -0
  66. package/dist/content/examples/clock/manifest.json +44 -0
  67. package/dist/content/examples/clock/package.json +16 -0
  68. package/dist/content/examples/clock/style.ocss +26 -0
  69. package/dist/content/examples/clock/tests/hidden.scenario.json +68 -0
  70. package/dist/content/examples/clock/tests/load-logic.mjs +22 -0
  71. package/dist/content/examples/clock/tests/logic.test.mjs +89 -0
  72. package/dist/content/examples/clock/tests/options.scenario.json +108 -0
  73. package/dist/content/examples/clock/tests/preview.scenario.json +25 -0
  74. package/dist/content/examples/clock/tests/render.scenario.json +94 -0
  75. package/dist/content/examples/clock/tests/summer-time.scenario.json +74 -0
  76. package/dist/content/examples/clock/tests/ticks.scenario.json +120 -0
  77. package/dist/content/examples/clock/tsconfig.json +16 -0
  78. package/dist/content/examples/clock/view.ocml +8 -0
  79. package/dist/content/examples/countdown/LICENSE +20 -0
  80. package/dist/content/examples/countdown/listing.json +10 -0
  81. package/dist/content/examples/countdown/locales/en.json +15 -0
  82. package/dist/content/examples/countdown/locales/fr.json +15 -0
  83. package/dist/content/examples/countdown/logic.ts +179 -0
  84. package/dist/content/examples/countdown/manifest.json +34 -0
  85. package/dist/content/examples/countdown/package.json +15 -0
  86. package/dist/content/examples/countdown/style.ocss +94 -0
  87. package/dist/content/examples/countdown/tsconfig.json +16 -0
  88. package/dist/content/examples/countdown/view.ocml +42 -0
  89. package/dist/content/examples/fps/LICENSE +21 -0
  90. package/dist/content/examples/fps/README.md +64 -0
  91. package/dist/content/examples/fps/listing.json +19 -0
  92. package/dist/content/examples/fps/locales/en.json +11 -0
  93. package/dist/content/examples/fps/locales/fr.json +11 -0
  94. package/dist/content/examples/fps/logic.ts +64 -0
  95. package/dist/content/examples/fps/manifest.json +30 -0
  96. package/dist/content/examples/fps/package.json +16 -0
  97. package/dist/content/examples/fps/style.ocss +31 -0
  98. package/dist/content/examples/fps/tests/label.scenario.json +63 -0
  99. package/dist/content/examples/fps/tests/load-logic.mjs +22 -0
  100. package/dist/content/examples/fps/tests/logic.test.mjs +60 -0
  101. package/dist/content/examples/fps/tests/preview.scenario.json +29 -0
  102. package/dist/content/examples/fps/tests/refused.scenario.json +20 -0
  103. package/dist/content/examples/fps/tests/render.scenario.json +96 -0
  104. package/dist/content/examples/fps/tests/stale.scenario.json +91 -0
  105. package/dist/content/examples/fps/tests/unavailable.scenario.json +111 -0
  106. package/dist/content/examples/fps/tsconfig.json +16 -0
  107. package/dist/content/examples/fps/view.ocml +11 -0
  108. package/dist/content/examples/media/LICENSE +21 -0
  109. package/dist/content/examples/media/README.md +147 -0
  110. package/dist/content/examples/media/listing.json +19 -0
  111. package/dist/content/examples/media/locales/en.json +11 -0
  112. package/dist/content/examples/media/locales/fr.json +11 -0
  113. package/dist/content/examples/media/logic.ts +191 -0
  114. package/dist/content/examples/media/manifest.json +35 -0
  115. package/dist/content/examples/media/package.json +16 -0
  116. package/dist/content/examples/media/style.ocss +148 -0
  117. package/dist/content/examples/media/tests/covers.scenario.json +144 -0
  118. package/dist/content/examples/media/tests/gesture.scenario.json +173 -0
  119. package/dist/content/examples/media/tests/load-logic.mjs +22 -0
  120. package/dist/content/examples/media/tests/logic.test.mjs +176 -0
  121. package/dist/content/examples/media/tests/marquee.scenario.json +127 -0
  122. package/dist/content/examples/media/tests/preview.scenario.json +52 -0
  123. package/dist/content/examples/media/tests/read-only.scenario.json +53 -0
  124. package/dist/content/examples/media/tests/refused.scenario.json +22 -0
  125. package/dist/content/examples/media/tests/refused.test.mjs +31 -0
  126. package/dist/content/examples/media/tests/render.scenario.json +136 -0
  127. package/dist/content/examples/media/tests/states.scenario.json +257 -0
  128. package/dist/content/examples/media/tests/unavailable.scenario.json +120 -0
  129. package/dist/content/examples/media/tsconfig.json +16 -0
  130. package/dist/content/examples/media/view.ocml +41 -0
  131. package/dist/content/examples/notes/LICENSE +21 -0
  132. package/dist/content/examples/notes/README.md +219 -0
  133. package/dist/content/examples/notes/listing.json +19 -0
  134. package/dist/content/examples/notes/locales/en.json +25 -0
  135. package/dist/content/examples/notes/locales/fr.json +25 -0
  136. package/dist/content/examples/notes/logic.ts +799 -0
  137. package/dist/content/examples/notes/manifest.json +40 -0
  138. package/dist/content/examples/notes/package.json +16 -0
  139. package/dist/content/examples/notes/style.ocss +469 -0
  140. package/dist/content/examples/notes/tests/checks.scenario.json +237 -0
  141. package/dist/content/examples/notes/tests/create.scenario.json +260 -0
  142. package/dist/content/examples/notes/tests/delete.scenario.json +342 -0
  143. package/dist/content/examples/notes/tests/drafts.scenario.json +714 -0
  144. package/dist/content/examples/notes/tests/editor.scenario.json +232 -0
  145. package/dist/content/examples/notes/tests/frame.scenario.json +93 -0
  146. package/dist/content/examples/notes/tests/interactive.scenario.json +344 -0
  147. package/dist/content/examples/notes/tests/limits.scenario.json +1351 -0
  148. package/dist/content/examples/notes/tests/load-logic.mjs +22 -0
  149. package/dist/content/examples/notes/tests/logic.test.mjs +514 -0
  150. package/dist/content/examples/notes/tests/preview.scenario.json +99 -0
  151. package/dist/content/examples/notes/tests/read-only.scenario.json +105 -0
  152. package/dist/content/examples/notes/tests/refused.scenario.json +33 -0
  153. package/dist/content/examples/notes/tests/save.scenario.json +378 -0
  154. package/dist/content/examples/notes/tests/states.scenario.json +640 -0
  155. package/dist/content/examples/notes/tests/typing.scenario.json +562 -0
  156. package/dist/content/examples/notes/tests/unavailable.scenario.json +246 -0
  157. package/dist/content/examples/notes/tsconfig.json +16 -0
  158. package/dist/content/examples/notes/view.ocml +154 -0
  159. package/dist/content/examples/performance/LICENSE +21 -0
  160. package/dist/content/examples/performance/README.md +134 -0
  161. package/dist/content/examples/performance/listing.json +19 -0
  162. package/dist/content/examples/performance/locales/en.json +20 -0
  163. package/dist/content/examples/performance/locales/fr.json +20 -0
  164. package/dist/content/examples/performance/logic.ts +210 -0
  165. package/dist/content/examples/performance/manifest.json +83 -0
  166. package/dist/content/examples/performance/package.json +16 -0
  167. package/dist/content/examples/performance/style.ocss +75 -0
  168. package/dist/content/examples/performance/tests/fahrenheit.scenario.json +110 -0
  169. package/dist/content/examples/performance/tests/horizontal.scenario.json +96 -0
  170. package/dist/content/examples/performance/tests/load-logic.mjs +22 -0
  171. package/dist/content/examples/performance/tests/logic.test.mjs +226 -0
  172. package/dist/content/examples/performance/tests/menu.scenario.json +170 -0
  173. package/dist/content/examples/performance/tests/no-fps-source.scenario.json +47 -0
  174. package/dist/content/examples/performance/tests/preview.scenario.json +45 -0
  175. package/dist/content/examples/performance/tests/refused.scenario.json +20 -0
  176. package/dist/content/examples/performance/tests/refused.test.mjs +19 -0
  177. package/dist/content/examples/performance/tests/region.scenario.json +89 -0
  178. package/dist/content/examples/performance/tests/render.scenario.json +112 -0
  179. package/dist/content/examples/performance/tests/sources.scenario.json +151 -0
  180. package/dist/content/examples/performance/tests/stale.scenario.json +111 -0
  181. package/dist/content/examples/performance/tests/telemetry-only.scenario.json +44 -0
  182. package/dist/content/examples/performance/tests/thresholds.scenario.json +147 -0
  183. package/dist/content/examples/performance/tests/waiting.scenario.json +105 -0
  184. package/dist/content/examples/performance/tsconfig.json +16 -0
  185. package/dist/content/examples/performance/view.ocml +18 -0
  186. package/dist/content/examples/playervox-journal/LICENSE +21 -0
  187. package/dist/content/examples/playervox-journal/README.md +150 -0
  188. package/dist/content/examples/playervox-journal/listing.json +19 -0
  189. package/dist/content/examples/playervox-journal/locales/en.json +25 -0
  190. package/dist/content/examples/playervox-journal/locales/fr.json +25 -0
  191. package/dist/content/examples/playervox-journal/logic.ts +556 -0
  192. package/dist/content/examples/playervox-journal/manifest.json +19 -0
  193. package/dist/content/examples/playervox-journal/package.json +16 -0
  194. package/dist/content/examples/playervox-journal/style.ocss +142 -0
  195. package/dist/content/examples/playervox-journal/tests/changes.scenario.json +482 -0
  196. package/dist/content/examples/playervox-journal/tests/delete.scenario.json +432 -0
  197. package/dist/content/examples/playervox-journal/tests/frame.scenario.json +169 -0
  198. package/dist/content/examples/playervox-journal/tests/load-logic.mjs +22 -0
  199. package/dist/content/examples/playervox-journal/tests/logic.test.mjs +349 -0
  200. package/dist/content/examples/playervox-journal/tests/paging.scenario.json +428 -0
  201. package/dist/content/examples/playervox-journal/tests/preview.scenario.json +184 -0
  202. package/dist/content/examples/playervox-journal/tests/read-only.scenario.json +157 -0
  203. package/dist/content/examples/playervox-journal/tests/refused.scenario.json +39 -0
  204. package/dist/content/examples/playervox-journal/tests/scale.scenario.json +286 -0
  205. package/dist/content/examples/playervox-journal/tests/signed-out.scenario.json +134 -0
  206. package/dist/content/examples/playervox-journal/tests/states.scenario.json +741 -0
  207. package/dist/content/examples/playervox-journal/tests/unavailable.scenario.json +183 -0
  208. package/dist/content/examples/playervox-journal/tsconfig.json +16 -0
  209. package/dist/content/examples/playervox-journal/view.ocml +52 -0
  210. package/dist/content/examples/playervox-rating/LICENSE +21 -0
  211. package/dist/content/examples/playervox-rating/README.md +166 -0
  212. package/dist/content/examples/playervox-rating/listing.json +19 -0
  213. package/dist/content/examples/playervox-rating/locales/en.json +35 -0
  214. package/dist/content/examples/playervox-rating/locales/fr.json +35 -0
  215. package/dist/content/examples/playervox-rating/logic.ts +581 -0
  216. package/dist/content/examples/playervox-rating/manifest.json +19 -0
  217. package/dist/content/examples/playervox-rating/package.json +16 -0
  218. package/dist/content/examples/playervox-rating/style.ocss +333 -0
  219. package/dist/content/examples/playervox-rating/tests/account.scenario.json +125 -0
  220. package/dist/content/examples/playervox-rating/tests/bounds.scenario.json +73 -0
  221. package/dist/content/examples/playervox-rating/tests/draft.scenario.json +199 -0
  222. package/dist/content/examples/playervox-rating/tests/form.scenario.json +142 -0
  223. package/dist/content/examples/playervox-rating/tests/load-logic.mjs +22 -0
  224. package/dist/content/examples/playervox-rating/tests/logic.test.mjs +346 -0
  225. package/dist/content/examples/playervox-rating/tests/offline.scenario.json +142 -0
  226. package/dist/content/examples/playervox-rating/tests/preview.scenario.json +44 -0
  227. package/dist/content/examples/playervox-rating/tests/publish-keyboard.scenario.json +221 -0
  228. package/dist/content/examples/playervox-rating/tests/publish-pointer.scenario.json +314 -0
  229. package/dist/content/examples/playervox-rating/tests/read-only.scenario.json +70 -0
  230. package/dist/content/examples/playervox-rating/tests/refused.scenario.json +45 -0
  231. package/dist/content/examples/playervox-rating/tests/states.scenario.json +362 -0
  232. package/dist/content/examples/playervox-rating/tsconfig.json +16 -0
  233. package/dist/content/examples/playervox-rating/view.ocml +62 -0
  234. package/dist/content/examples/playervox-reviews/LICENSE +21 -0
  235. package/dist/content/examples/playervox-reviews/README.md +194 -0
  236. package/dist/content/examples/playervox-reviews/listing.json +19 -0
  237. package/dist/content/examples/playervox-reviews/locales/en.json +32 -0
  238. package/dist/content/examples/playervox-reviews/locales/fr.json +32 -0
  239. package/dist/content/examples/playervox-reviews/logic.ts +822 -0
  240. package/dist/content/examples/playervox-reviews/manifest.json +30 -0
  241. package/dist/content/examples/playervox-reviews/package.json +16 -0
  242. package/dist/content/examples/playervox-reviews/style.ocss +260 -0
  243. package/dist/content/examples/playervox-reviews/tests/account.scenario.json +249 -0
  244. package/dist/content/examples/playervox-reviews/tests/bounds.scenario.json +336 -0
  245. package/dist/content/examples/playervox-reviews/tests/changes.scenario.json +515 -0
  246. package/dist/content/examples/playervox-reviews/tests/filter.scenario.json +400 -0
  247. package/dist/content/examples/playervox-reviews/tests/frame.scenario.json +185 -0
  248. package/dist/content/examples/playervox-reviews/tests/keyboard.scenario.json +188 -0
  249. package/dist/content/examples/playervox-reviews/tests/load-logic.mjs +22 -0
  250. package/dist/content/examples/playervox-reviews/tests/logic.test.mjs +559 -0
  251. package/dist/content/examples/playervox-reviews/tests/offline-start.scenario.json +152 -0
  252. package/dist/content/examples/playervox-reviews/tests/offline.scenario.json +311 -0
  253. package/dist/content/examples/playervox-reviews/tests/paging.scenario.json +497 -0
  254. package/dist/content/examples/playervox-reviews/tests/preview.scenario.json +286 -0
  255. package/dist/content/examples/playervox-reviews/tests/reading.scenario.json +591 -0
  256. package/dist/content/examples/playervox-reviews/tests/refused.scenario.json +54 -0
  257. package/dist/content/examples/playervox-reviews/tests/scale.scenario.json +303 -0
  258. package/dist/content/examples/playervox-reviews/tests/states.scenario.json +700 -0
  259. package/dist/content/examples/playervox-reviews/tests/unavailable.scenario.json +192 -0
  260. package/dist/content/examples/playervox-reviews/tsconfig.json +16 -0
  261. package/dist/content/examples/playervox-reviews/view.ocml +110 -0
  262. package/dist/content/examples/playervox-score/LICENSE +21 -0
  263. package/dist/content/examples/playervox-score/README.md +131 -0
  264. package/dist/content/examples/playervox-score/listing.json +19 -0
  265. package/dist/content/examples/playervox-score/locales/en.json +23 -0
  266. package/dist/content/examples/playervox-score/locales/fr.json +23 -0
  267. package/dist/content/examples/playervox-score/logic.ts +430 -0
  268. package/dist/content/examples/playervox-score/manifest.json +47 -0
  269. package/dist/content/examples/playervox-score/package.json +16 -0
  270. package/dist/content/examples/playervox-score/style.ocss +247 -0
  271. package/dist/content/examples/playervox-score/tests/grades.scenario.json +279 -0
  272. package/dist/content/examples/playervox-score/tests/load-logic.mjs +22 -0
  273. package/dist/content/examples/playervox-score/tests/logic.test.mjs +229 -0
  274. package/dist/content/examples/playervox-score/tests/options.scenario.json +302 -0
  275. package/dist/content/examples/playervox-score/tests/preview.scenario.json +68 -0
  276. package/dist/content/examples/playervox-score/tests/pulse.scenario.json +157 -0
  277. package/dist/content/examples/playervox-score/tests/refused.scenario.json +21 -0
  278. package/dist/content/examples/playervox-score/tests/signed-out.scenario.json +46 -0
  279. package/dist/content/examples/playervox-score/tests/states.scenario.json +450 -0
  280. package/dist/content/examples/playervox-score/tests/unavailable.scenario.json +84 -0
  281. package/dist/content/examples/playervox-score/tsconfig.json +16 -0
  282. package/dist/content/examples/playervox-score/view.ocml +51 -0
  283. package/dist/content/examples/session/LICENSE +21 -0
  284. package/dist/content/examples/session/README.md +57 -0
  285. package/dist/content/examples/session/listing.json +19 -0
  286. package/dist/content/examples/session/locales/en.json +3 -0
  287. package/dist/content/examples/session/locales/fr.json +3 -0
  288. package/dist/content/examples/session/logic.ts +26 -0
  289. package/dist/content/examples/session/manifest.json +19 -0
  290. package/dist/content/examples/session/package.json +16 -0
  291. package/dist/content/examples/session/style.ocss +16 -0
  292. package/dist/content/examples/session/tests/hidden.scenario.json +45 -0
  293. package/dist/content/examples/session/tests/load-logic.mjs +22 -0
  294. package/dist/content/examples/session/tests/logic.test.mjs +37 -0
  295. package/dist/content/examples/session/tests/preview.scenario.json +28 -0
  296. package/dist/content/examples/session/tests/refused.scenario.json +20 -0
  297. package/dist/content/examples/session/tests/refused.test.mjs +16 -0
  298. package/dist/content/examples/session/tests/render.scenario.json +95 -0
  299. package/dist/content/examples/session/tests/ticks.scenario.json +139 -0
  300. package/dist/content/examples/session/tests/unknown.scenario.json +37 -0
  301. package/dist/content/examples/session/tsconfig.json +16 -0
  302. package/dist/content/examples/session/view.ocml +11 -0
  303. package/dist/content/examples/stopwatch/LICENSE +21 -0
  304. package/dist/content/examples/stopwatch/README.md +99 -0
  305. package/dist/content/examples/stopwatch/listing.json +19 -0
  306. package/dist/content/examples/stopwatch/locales/en.json +8 -0
  307. package/dist/content/examples/stopwatch/locales/fr.json +8 -0
  308. package/dist/content/examples/stopwatch/logic.ts +93 -0
  309. package/dist/content/examples/stopwatch/manifest.json +19 -0
  310. package/dist/content/examples/stopwatch/package.json +16 -0
  311. package/dist/content/examples/stopwatch/style.ocss +54 -0
  312. package/dist/content/examples/stopwatch/tests/errors.scenario.json +121 -0
  313. package/dist/content/examples/stopwatch/tests/gesture.scenario.json +154 -0
  314. package/dist/content/examples/stopwatch/tests/hidden.scenario.json +57 -0
  315. package/dist/content/examples/stopwatch/tests/hundredths.scenario.json +173 -0
  316. package/dist/content/examples/stopwatch/tests/load-logic.mjs +22 -0
  317. package/dist/content/examples/stopwatch/tests/logic.test.mjs +99 -0
  318. package/dist/content/examples/stopwatch/tests/preview.scenario.json +36 -0
  319. package/dist/content/examples/stopwatch/tests/read-only.scenario.json +42 -0
  320. package/dist/content/examples/stopwatch/tests/read-only.test.mjs +18 -0
  321. package/dist/content/examples/stopwatch/tests/refused.scenario.json +22 -0
  322. package/dist/content/examples/stopwatch/tests/refused.test.mjs +18 -0
  323. package/dist/content/examples/stopwatch/tests/render.scenario.json +105 -0
  324. package/dist/content/examples/stopwatch/tests/states.scenario.json +246 -0
  325. package/dist/content/examples/stopwatch/tests/unknown.scenario.json +40 -0
  326. package/dist/content/examples/stopwatch/tsconfig.json +16 -0
  327. package/dist/content/examples/stopwatch/view.ocml +29 -0
  328. package/dist/content/examples/twitch-chat/LICENSE +21 -0
  329. package/dist/content/examples/twitch-chat/README.md +283 -0
  330. package/dist/content/examples/twitch-chat/listing.json +19 -0
  331. package/dist/content/examples/twitch-chat/locales/en.json +52 -0
  332. package/dist/content/examples/twitch-chat/locales/fr.json +52 -0
  333. package/dist/content/examples/twitch-chat/logic.ts +887 -0
  334. package/dist/content/examples/twitch-chat/manifest.json +33 -0
  335. package/dist/content/examples/twitch-chat/package.json +16 -0
  336. package/dist/content/examples/twitch-chat/style.ocss +544 -0
  337. package/dist/content/examples/twitch-chat/tests/account.scenario.json +325 -0
  338. package/dist/content/examples/twitch-chat/tests/burst.scenario.json +1866 -0
  339. package/dist/content/examples/twitch-chat/tests/channel-refused.scenario.json +372 -0
  340. package/dist/content/examples/twitch-chat/tests/channel.scenario.json +480 -0
  341. package/dist/content/examples/twitch-chat/tests/draft.scenario.json +279 -0
  342. package/dist/content/examples/twitch-chat/tests/fade.scenario.json +269 -0
  343. package/dist/content/examples/twitch-chat/tests/failures.scenario.json +420 -0
  344. package/dist/content/examples/twitch-chat/tests/favorites.scenario.json +376 -0
  345. package/dist/content/examples/twitch-chat/tests/frame.scenario.json +152 -0
  346. package/dist/content/examples/twitch-chat/tests/lifetime.scenario.json +186 -0
  347. package/dist/content/examples/twitch-chat/tests/load-logic.mjs +22 -0
  348. package/dist/content/examples/twitch-chat/tests/logic.test.mjs +1053 -0
  349. package/dist/content/examples/twitch-chat/tests/passive.scenario.json +536 -0
  350. package/dist/content/examples/twitch-chat/tests/preview.scenario.json +147 -0
  351. package/dist/content/examples/twitch-chat/tests/read-only.scenario.json +202 -0
  352. package/dist/content/examples/twitch-chat/tests/read-only.test.mjs +63 -0
  353. package/dist/content/examples/twitch-chat/tests/refused.scenario.json +65 -0
  354. package/dist/content/examples/twitch-chat/tests/refused.test.mjs +43 -0
  355. package/dist/content/examples/twitch-chat/tests/render.scenario.json +347 -0
  356. package/dist/content/examples/twitch-chat/tests/reply.scenario.json +384 -0
  357. package/dist/content/examples/twitch-chat/tests/send-refused.scenario.json +383 -0
  358. package/dist/content/examples/twitch-chat/tests/send.scenario.json +300 -0
  359. package/dist/content/examples/twitch-chat/tests/states.scenario.json +455 -0
  360. package/dist/content/examples/twitch-chat/tests/unavailable.scenario.json +256 -0
  361. package/dist/content/examples/twitch-chat/tests/unread.scenario.json +997 -0
  362. package/dist/content/examples/twitch-chat/tsconfig.json +16 -0
  363. package/dist/content/examples/twitch-chat/view.ocml +141 -0
  364. package/dist/content/examples/warframe-market/LICENSE +21 -0
  365. package/dist/content/examples/warframe-market/README.md +176 -0
  366. package/dist/content/examples/warframe-market/listing.json +19 -0
  367. package/dist/content/examples/warframe-market/locales/en.json +44 -0
  368. package/dist/content/examples/warframe-market/locales/fr.json +44 -0
  369. package/dist/content/examples/warframe-market/logic.ts +1265 -0
  370. package/dist/content/examples/warframe-market/manifest.json +92 -0
  371. package/dist/content/examples/warframe-market/package.json +16 -0
  372. package/dist/content/examples/warframe-market/style.ocss +422 -0
  373. package/dist/content/examples/warframe-market/tests/bounds.scenario.json +60 -0
  374. package/dist/content/examples/warframe-market/tests/cached.scenario.json +70 -0
  375. package/dist/content/examples/warframe-market/tests/detail.scenario.json +251 -0
  376. package/dist/content/examples/warframe-market/tests/errors.scenario.json +105 -0
  377. package/dist/content/examples/warframe-market/tests/fixtures.json +295 -0
  378. package/dist/content/examples/warframe-market/tests/load-logic.mjs +22 -0
  379. package/dist/content/examples/warframe-market/tests/logic.test.mjs +505 -0
  380. package/dist/content/examples/warframe-market/tests/no-clipboard.scenario.json +104 -0
  381. package/dist/content/examples/warframe-market/tests/passive.scenario.json +87 -0
  382. package/dist/content/examples/warframe-market/tests/preview.scenario.json +146 -0
  383. package/dist/content/examples/warframe-market/tests/refresh.scenario.json +56 -0
  384. package/dist/content/examples/warframe-market/tests/refused.scenario.json +74 -0
  385. package/dist/content/examples/warframe-market/tests/render.scenario.json +210 -0
  386. package/dist/content/examples/warframe-market/tests/search.scenario.json +189 -0
  387. package/dist/content/examples/warframe-market/tests/unavailable.scenario.json +54 -0
  388. package/dist/content/examples/warframe-market/tsconfig.json +16 -0
  389. package/dist/content/examples/warframe-market/view.ocml +116 -0
  390. package/dist/content/examples/weather/LICENSE +20 -0
  391. package/dist/content/examples/weather/listing.json +10 -0
  392. package/dist/content/examples/weather/locales/en.json +9 -0
  393. package/dist/content/examples/weather/locales/fr.json +9 -0
  394. package/dist/content/examples/weather/logic.ts +62 -0
  395. package/dist/content/examples/weather/manifest.json +51 -0
  396. package/dist/content/examples/weather/package.json +15 -0
  397. package/dist/content/examples/weather/style.ocss +29 -0
  398. package/dist/content/examples/weather/tsconfig.json +16 -0
  399. package/dist/content/examples/weather/view.ocml +12 -0
  400. package/dist/content/index.json +3601 -0
  401. package/dist/content/reference/errors.json +1712 -0
  402. package/dist/content/reference/limits.json +1154 -0
  403. package/dist/content/reference/manifest.md +223 -0
  404. package/dist/content/reference/permissions.json +413 -0
  405. package/dist/content/templates/blank/locales/en.json +3 -0
  406. package/dist/content/templates/blank/locales/fr.json +3 -0
  407. package/dist/content/templates/blank/logic.ts +7 -0
  408. package/dist/content/templates/blank/manifest.json +16 -0
  409. package/dist/content/templates/blank/style.ocss +10 -0
  410. package/dist/content/templates/blank/tests/example.scenario.json +10 -0
  411. package/dist/content/templates/blank/view.ocml +6 -0
  412. package/dist/content/templates/chart/locales/en.json +3 -0
  413. package/dist/content/templates/chart/locales/fr.json +3 -0
  414. package/dist/content/templates/chart/logic.ts +27 -0
  415. package/dist/content/templates/chart/manifest.json +16 -0
  416. package/dist/content/templates/chart/style.ocss +27 -0
  417. package/dist/content/templates/chart/tests/example.scenario.json +10 -0
  418. package/dist/content/templates/chart/view.ocml +10 -0
  419. package/dist/content/templates/counter/locales/en.json +4 -0
  420. package/dist/content/templates/counter/locales/fr.json +4 -0
  421. package/dist/content/templates/counter/logic.ts +20 -0
  422. package/dist/content/templates/counter/manifest.json +16 -0
  423. package/dist/content/templates/counter/style.ocss +26 -0
  424. package/dist/content/templates/counter/tests/example.scenario.json +11 -0
  425. package/dist/content/templates/counter/view.ocml +13 -0
  426. package/dist/content/templates/list/locales/en.json +8 -0
  427. package/dist/content/templates/list/locales/fr.json +8 -0
  428. package/dist/content/templates/list/logic.ts +35 -0
  429. package/dist/content/templates/list/manifest.json +16 -0
  430. package/dist/content/templates/list/style.ocss +42 -0
  431. package/dist/content/templates/list/tests/example.scenario.json +10 -0
  432. package/dist/content/templates/list/view.ocml +17 -0
  433. package/dist/content/templates/shared/LICENSE +20 -0
  434. package/dist/content/templates/shared/package.json +14 -0
  435. package/dist/content/templates/shared/tsconfig.json +16 -0
  436. package/dist/content.js +68 -0
  437. package/dist/env.js +156 -0
  438. package/dist/fingerprint.js +32 -0
  439. package/dist/ids.js +27 -0
  440. package/dist/index.js +82 -0
  441. package/dist/knowledge.js +222 -0
  442. package/dist/npm.js +149 -0
  443. package/dist/prompts.js +47 -0
  444. package/dist/resources.js +82 -0
  445. package/dist/run.js +120 -0
  446. package/dist/server.js +43 -0
  447. package/dist/session.js +205 -0
  448. package/dist/sources.js +218 -0
  449. package/dist/text.js +80 -0
  450. package/dist/texts.js +44 -0
  451. package/dist/tools/audit.js +105 -0
  452. package/dist/tools/build.js +453 -0
  453. package/dist/tools/common.js +122 -0
  454. package/dist/tools/docs.js +196 -0
  455. package/dist/tools/preview.js +87 -0
  456. package/dist/tools/project.js +178 -0
  457. package/dist/tools/setup.js +146 -0
  458. package/dist/tools/submission.js +369 -0
  459. package/dist/version.js +3 -0
  460. package/npm-shrinkwrap.json +402 -0
  461. package/package.json +50 -4
@@ -0,0 +1,147 @@
1
+ # Media
2
+
3
+ `com.playervox.overcrow.media` — the track a music or video player of this
4
+ computer is playing: its cover, its title in bold and its artists, and in
5
+ Interactive mode the player's previous, play/pause and next buttons. A
6
+ built-in of OverCrow, written against the public `@overcrow/sdk` and
7
+ packaged with `overcrow-widget` like any creator's widget. MIT.
8
+
9
+ ## What it shows
10
+
11
+ | State | Shows |
12
+ | --- | --- |
13
+ | A track | the square cover, the title in bold, the artists below it |
14
+ | Playing / paused or stopped | the middle button shows pause ("Pause") / play ("Play", FR "Lire"); nothing else differs |
15
+ | No cover, or it is loading | a raised square with a music note in its place |
16
+ | No artist | the artist row is left out; the cover shrinks to the title's height |
17
+ | No title | "Unknown title" / "Titre inconnu" |
18
+ | No player | "No active media" / "Aucun média actif", with the cover's placeholder |
19
+ | The player went away | the same: the previous track is gone at once |
20
+ | The media source failed | "No active media" and, below it, "Media is temporarily unavailable." / "Le média est temporairement indisponible."; the widget asks again 5 s later |
21
+ | No permission | "No active media": the widget asks OverCrow nothing |
22
+
23
+ - The cover is square, cropped to its centre, with 6 px corners, as tall as
24
+ the title and artist rows beside it and centred on them.
25
+ - Title and artists stay on one line each; line breaks in the player's text
26
+ become spaces. Several artists are joined with ", ".
27
+ - A title wider than its column scrolls back and forth: it waits 2 s,
28
+ travels at 18 px/s (scaled with the content) until its end shows, waits
29
+ 2 s, travels back. It restarts on a new title or a new width, and does
30
+ not move while the widget is hidden. With "Scroll long titles" off it
31
+ ends with an ellipsis. The artists never scroll: they end with an
32
+ ellipsis, and hovering them shows them whole.
33
+ - The title's accessible name is the whole, unscrolled title; the cover and
34
+ its placeholder are "Cover artwork" / "Pochette".
35
+ - The width is the one you set: a long title never widens the widget. The
36
+ height follows the content, and Passive mode, without buttons, is lower.
37
+
38
+ The player's text is shown as it comes, in any language; only the widget's
39
+ own messages and labels follow OverCrow's language.
40
+
41
+ ## Controls
42
+
43
+ | Mode | Control | Does |
44
+ | --- | --- | --- |
45
+ | Interactive | Previous ("Previous" / "Précédent") | `media.previous` |
46
+ | Interactive | Play or pause ("Play" / "Lire", "Pause") | `media.playPause` |
47
+ | Interactive | Next ("Next" / "Suivant") | `media.next` |
48
+ | Interactive | Enter or Space on a focused button | the same |
49
+ | Interactive | Hover or focus a button, the title or the artists | its tooltip |
50
+ | Passive | — | no buttons, no tooltips: the track only |
51
+
52
+ A button shows only when the player offers its action: it is left out, not
53
+ greyed. Each click is one call, on its own gesture (OverCrow refuses a call
54
+ made outside one, `gesture_required`), and names the player shown. The
55
+ player's new state arrives with the next update; a call OverCrow cannot
56
+ apply — another player took over (`stale_context`), the player dropped the
57
+ action (`unsupported`), no player (`unavailable`) — changes nothing on
58
+ screen.
59
+
60
+ ## Menu
61
+
62
+ | Row | Type | Default |
63
+ | --- | --- | --- |
64
+ | Show cover artwork / Afficher la pochette | toggle `show-cover` | on |
65
+ | Scroll long titles / Faire défiler les titres longs | toggle `scroll-title` | on |
66
+
67
+ With the cover off, the cover's slot goes, and the widget asks OverCrow for
68
+ no cover: OverCrow then reads no cover file and contacts no image server
69
+ for it.
70
+
71
+ ## Permissions
72
+
73
+ - `media.read`: the current player's title, artists, playing state, the
74
+ actions it offers, and its cover as an image OverCrow decoded. Sensitive:
75
+ it tells what you listen to.
76
+ - `media.control`: previous, play/pause and next. Sensitive. Without it the
77
+ widget shows no buttons.
78
+
79
+ As sensitive capabilities rule out the network and the clipboard, the
80
+ widget declares neither, and no storage: it keeps nothing.
81
+
82
+ ## Platforms
83
+
84
+ | | Linux | Windows |
85
+ | --- | --- | --- |
86
+ | Source | MPRIS players on the session bus | the system's current media session |
87
+ | Which player | the playing one, else a paused one, else a stopped one; on a tie the first by bus name | the one Windows shows as current |
88
+ | Artists | up to 8, joined with ", " | the one text the player gives |
89
+ | Cover | the player's art URL: a local file in a cache, music or temporary folder, or an image from Spotify, YouTube or Tidal | the session's thumbnail |
90
+
91
+ OverCrow runs its media source only while a widget subscribes, and loads
92
+ covers only for a widget that asks for them. It hands the widget a cover
93
+ of at most 256 px as an `asset:` handle: the widget never sees a URL, a
94
+ file path or a player's bus name. The same values give the same pixels on
95
+ both systems.
96
+
97
+ ## How it works
98
+
99
+ `media.subscribe({ cover })` gives `null` without a player, or `{ player,
100
+ title, artists, playing, canPrevious, canPlayPause, canNext, cover }`;
101
+ `player` is an opaque ID the buttons pass back. The view draws `cover` with
102
+ an `image` and the title with `text-overflow: marquee`: OverCrow measures,
103
+ scrolls and holds it, so the widget runs no timer and sends nothing while a
104
+ title scrolls. When OverCrow's media source fails, the subscription ends
105
+ with `unavailable`; the widget shows it and subscribes again after 5 s,
106
+ which restarts the source.
107
+
108
+ ## Tests
109
+
110
+ - `tests/logic.test.mjs`, `tests/refused.test.mjs`: the logic on
111
+ `@overcrow/sdk/testing`.
112
+ - Scenarios with their reference images, covers drawn by
113
+ `tests/assets/generate.mjs` (synthetic images, no real album art):
114
+ - `render`: a playing track with its cover and buttons, in both themes and
115
+ languages at 100 and 150 %, and in Passive mode;
116
+ - `states`: no player, paused, no artist, unknown title (EN, FR), only
117
+ play/pause, no button, no cover (dark, light), the player gone;
118
+ - `covers`: a square cover, a wide one cropped to its centre, a new
119
+ track's cover, the cover option off and on (and the subscription it
120
+ makes);
121
+ - `marquee`: the pause at the start, the travel, the pause at the end, the
122
+ way back, the ellipsis with the option off, 150 %;
123
+ - `gesture`: Passive clicks call nothing; each button by click, Enter and
124
+ Space, with its player; refusals; a tooltip;
125
+ - `unavailable`: the failure's message (EN, FR), the new subscription
126
+ 5 s later and the player it brings;
127
+ - `refused`, `read-only`.
128
+
129
+ ## Cost
130
+
131
+ Measured on 2026-09-30 with OverCrow's release build, on an AMD Ryzen 7
132
+ 5800X3D Linux workstation and in a Windows 11 virtual machine (2 vCPU),
133
+ at 320 px wide with a 256 px cover:
134
+
135
+ | | Linux | Windows (VM) |
136
+ | --- | --- | --- |
137
+ | Widget start, warm, into the overlay's runtime | 6.9 ms (p50), 8.0 ms at most | VM ready in 14 ms (p50) |
138
+ | VM memory, cover included | 0.64 MiB private, 0.82 MiB PSS: the VM holds the cover's handle, not its pixels | 0.90 MiB private working set, 1.18 MiB private commit |
139
+ | Private memory in all (VM, sandbox helpers, overlay share with the cover) | under 7 MB | — |
140
+ | CPU, a track held | 0.03 % of one core, no frame | — |
141
+ | CPU, a long title scrolling | 0.15 % of one core: one frame per physical pixel it moves (18 a second at 100 %), 0.07 ms each, none during its pauses | — |
142
+ | CPU, a new track and cover every 2 s | 0.05 % of one core: one frame per track | — |
143
+ | VM CPU | one short turn per track; none while a title scrolls | — |
144
+
145
+ The egui widget it replaces repainted a scrolling title at 30 Hz (0.04 ms
146
+ each, painting only), inside the overlay's own process, and OverCrow polled
147
+ the players every second whatever the widgets showed.
@@ -0,0 +1,19 @@
1
+ {
2
+ "author": "PlayerVox",
3
+ "spdxLicense": "MIT",
4
+ "sourceUrl": "https://github.com/Valhallab/playervox-overcrow-releases/tree/main/widgets/media",
5
+ "preview": "assets/preview.png",
6
+ "defaultLocale": "en",
7
+ "localizations": [
8
+ {
9
+ "locale": "en",
10
+ "name": "Media",
11
+ "description": "The track your music player is playing, with its cover, and previous, play/pause and next buttons in Interactive mode."
12
+ },
13
+ {
14
+ "locale": "fr",
15
+ "name": "Média",
16
+ "description": "Le morceau que joue votre lecteur de musique, avec sa pochette, et les boutons précédent, lecture/pause et suivant en mode interactif."
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "label": "Media",
3
+ "no-media": "No active media",
4
+ "unavailable": "Media is temporarily unavailable.",
5
+ "unknown-title": "Unknown title",
6
+ "cover": "Cover artwork",
7
+ "previous": "Previous",
8
+ "play": "Play",
9
+ "pause": "Pause",
10
+ "next": "Next"
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "label": "Média",
3
+ "no-media": "Aucun média actif",
4
+ "unavailable": "Le média est temporairement indisponible.",
5
+ "unknown-title": "Titre inconnu",
6
+ "cover": "Pochette",
7
+ "previous": "Précédent",
8
+ "play": "Lire",
9
+ "pause": "Pause",
10
+ "next": "Suivant"
11
+ }
@@ -0,0 +1,191 @@
1
+ // Media: the track a desktop player is playing, its cover and, in
2
+ // Interactive mode, the player's previous / play-pause / next buttons. The
3
+ // host picks the player (MPRIS on Linux, the system's current session on
4
+ // Windows), decodes the cover into an `asset:` handle and scrolls an
5
+ // overflowing title (`text-overflow: marquee`): the widget only lays out.
6
+ import {
7
+ hasGrant,
8
+ host,
9
+ initState,
10
+ media,
11
+ onHost,
12
+ option,
13
+ t,
14
+ timers,
15
+ type Media,
16
+ type Subscription,
17
+ type Timer,
18
+ } from "@overcrow/sdk";
19
+
20
+ declare module "@overcrow/sdk" {
21
+ interface WidgetState {
22
+ /** The current player, or `null` without one. */
23
+ media: Media | null;
24
+ /** The host's media source failed; it retries on the next subscription. */
25
+ unavailable: boolean;
26
+ /** The buttons take input only in Interactive mode. */
27
+ interactive: boolean;
28
+ showCover: boolean;
29
+ scrollTitle: boolean;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * After the host ends the subscription with a failure (its media source
35
+ * failed), the widget subscribes again this much later; the host restarts
36
+ * its source for the new subscription.
37
+ */
38
+ export const RETRY_MS = 5_000;
39
+
40
+ const canRead = hasGrant("media.read");
41
+ const canControl = hasGrant("media.control");
42
+
43
+ const state = initState({
44
+ media: null as Media | null,
45
+ unavailable: false,
46
+ interactive: host.mode === "interactive",
47
+ showCover: option("show-cover", true),
48
+ scrollTitle: option("scroll-title", true),
49
+ });
50
+
51
+ /** Provider text on one line: line breaks never make a second row. */
52
+ export function oneLine(text: string): string {
53
+ return text.replace(/\s*[\r\n]+\s*/g, " ").trim();
54
+ }
55
+
56
+ /** The title, or "Unknown title" without one. */
57
+ export function title(current: Media): string {
58
+ const text = current.title === null ? "" : oneLine(current.title);
59
+ return text === "" ? t("unknown-title") : text;
60
+ }
61
+
62
+ /** The artists on one line, or nothing: the row is then left out. */
63
+ export function artists(current: Media): string {
64
+ return current.artists
65
+ .map(oneLine)
66
+ .filter((artist) => artist !== "")
67
+ .join(", ");
68
+ }
69
+
70
+ /**
71
+ * The cover's size class: as tall as the title and artist rows, or as one
72
+ * row without artists or without a player (the placeholder).
73
+ */
74
+ export function coverClass(current: Media | null, cover: string | null): string {
75
+ const size = current !== null && artists(current) !== "" ? "cover tall" : "cover short";
76
+ return cover === null ? `${size} placeholder` : size;
77
+ }
78
+
79
+ /** The title scrolls when it overflows, or ends with an ellipsis. */
80
+ export function titleClass(scroll: boolean): string {
81
+ return scroll ? "title scrolling" : "title";
82
+ }
83
+
84
+ export function rootClass(showCover: boolean): string {
85
+ return showCover ? "media with-cover" : "media";
86
+ }
87
+
88
+ /** One transport button. */
89
+ export interface Control {
90
+ key: "previous" | "play-pause" | "next";
91
+ icon: string;
92
+ label: string;
93
+ }
94
+
95
+ /**
96
+ * The buttons the player supports, in the order previous, play/pause,
97
+ * next: an unsupported action has no button. None in Passive mode or
98
+ * without the control grant.
99
+ */
100
+ export function controls(current: Media | null, interactive: boolean): Control[] {
101
+ if (!interactive || !canControl || current === null) {
102
+ return [];
103
+ }
104
+ const list: Control[] = [];
105
+ if (current.canPrevious) {
106
+ list.push({ key: "previous", icon: "skip-back", label: t("previous") });
107
+ }
108
+ if (current.canPlayPause) {
109
+ list.push(
110
+ current.playing
111
+ ? { key: "play-pause", icon: "pause", label: t("pause") }
112
+ : { key: "play-pause", icon: "play", label: t("play") },
113
+ );
114
+ }
115
+ if (current.canNext) {
116
+ list.push({ key: "next", icon: "skip-forward", label: t("next") });
117
+ }
118
+ return list;
119
+ }
120
+
121
+ /**
122
+ * Sends a button's action to the player shown. The host refuses it with
123
+ * `stale_context` when another player replaced it meanwhile and with
124
+ * `unsupported` when the player no longer offers the action; the next
125
+ * update shows the player's state either way, so a failure shows nothing.
126
+ */
127
+ export function activate(key: Control["key"]): void {
128
+ const current = state.media;
129
+ if (current === null) {
130
+ return;
131
+ }
132
+ const params = { player: current.player };
133
+ const call =
134
+ key === "previous" ? media.previous(params) : key === "next" ? media.next(params) : media.playPause(params);
135
+ call.catch(() => {});
136
+ }
137
+
138
+ let subscription: Subscription | null = null;
139
+ let retry: Timer | null = null;
140
+
141
+ function subscribe(): void {
142
+ retry = null;
143
+ const current = media.subscribe({ cover: state.showCover }, (update) => {
144
+ if (update.ok) {
145
+ state.media = update.value;
146
+ state.unavailable = false;
147
+ return;
148
+ }
149
+ // The subscription ended: the host's source failed. Show it and ask
150
+ // again later; hidden, the timer waits until the widget shows.
151
+ if (subscription === current) {
152
+ subscription = null;
153
+ state.media = null;
154
+ state.unavailable = true;
155
+ retry = timers.after(RETRY_MS, subscribe);
156
+ }
157
+ });
158
+ subscription = current;
159
+ }
160
+
161
+ /** Subscribes again, for the cover option; the host sends the value at once. */
162
+ function resubscribe(): void {
163
+ retry?.cancel();
164
+ retry = null;
165
+ subscription?.cancel();
166
+ subscription = null;
167
+ subscribe();
168
+ }
169
+
170
+ onHost((changed) => {
171
+ if (changed.includes("mode")) {
172
+ state.interactive = host.mode === "interactive";
173
+ }
174
+ if (changed.includes("options")) {
175
+ state.scrollTitle = option("scroll-title", true);
176
+ const showCover = option("show-cover", true);
177
+ if (showCover !== state.showCover) {
178
+ state.showCover = showCover;
179
+ // Without the cover the host loads none: the subscription says so.
180
+ if (canRead && !state.unavailable) {
181
+ resubscribe();
182
+ }
183
+ }
184
+ }
185
+ });
186
+
187
+ // Without the grant the host would refuse the subscription: show that no
188
+ // media plays instead of asking.
189
+ if (canRead) {
190
+ subscribe();
191
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "apiVersion": 1,
4
+ "id": "com.playervox.overcrow.media",
5
+ "version": "1.0.2",
6
+ "name": {
7
+ "en": "Media",
8
+ "fr": "Média"
9
+ },
10
+ "sizing": {
11
+ "fit": "height",
12
+ "preferred": { "width": 320, "height": 160 },
13
+ "min": { "width": 160, "height": 24 },
14
+ "max": { "width": 900, "height": 240 }
15
+ },
16
+ "permissions": {
17
+ "capabilities": ["media.read", "media.control"]
18
+ },
19
+ "wrapper": {
20
+ "menu": [
21
+ {
22
+ "type": "toggle",
23
+ "id": "show-cover",
24
+ "label": { "en": "Show cover artwork", "fr": "Afficher la pochette" },
25
+ "default": true
26
+ },
27
+ {
28
+ "type": "toggle",
29
+ "id": "scroll-title",
30
+ "label": { "en": "Scroll long titles", "fr": "Faire défiler les titres longs" },
31
+ "default": true
32
+ }
33
+ ]
34
+ }
35
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "overcrow-widget-media",
3
+ "version": "1.0.2",
4
+ "private": true,
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "scripts": {
8
+ "check": "overcrow-widget check",
9
+ "package": "overcrow-widget package",
10
+ "test": "node --test tests/*.test.mjs"
11
+ },
12
+ "devDependencies": {
13
+ "@overcrow/sdk": "1.0.0",
14
+ "typescript": "6.0.3"
15
+ }
16
+ }
@@ -0,0 +1,148 @@
1
+ /* The cover on the left, as tall as the title and artist rows and centred
2
+ on them; the text column beside it, the buttons and the source message
3
+ under the text. The wrapper draws the panel and its 8 px margin; the
4
+ content adds 2 px on the sides (the built-in's 10 × 8 px margin), 8 px
5
+ beside the cover and 2 px between the rows. The width is the user's:
6
+ long text never widens the panel. */
7
+ .media {
8
+ display: grid;
9
+ grid-template-columns: minmax(0px, 1fr);
10
+ column-gap: 8px;
11
+ row-gap: 2px;
12
+ padding: 0 2px;
13
+ }
14
+
15
+ .with-cover {
16
+ grid-template-columns: auto minmax(0px, 1fr);
17
+ }
18
+
19
+ .with-cover .cover {
20
+ grid-column: 1;
21
+ grid-row: 1;
22
+ }
23
+
24
+ .with-cover .meta,
25
+ .with-cover .controls,
26
+ .with-cover .message {
27
+ grid-column: 2;
28
+ }
29
+
30
+ .with-cover .meta {
31
+ grid-row: 1;
32
+ }
33
+
34
+ /* Fixed line heights keep the cover exactly as tall as the rows beside
35
+ it: 18 px for the title, 2 px, 16 px for the artists. */
36
+ .cover {
37
+ align-self: center;
38
+ border-radius: var(--radius-sm);
39
+ object-fit: cover;
40
+ overflow: hidden;
41
+ }
42
+
43
+ .cover.tall {
44
+ width: 36px;
45
+ height: 36px;
46
+ }
47
+
48
+ .cover.short {
49
+ width: 18px;
50
+ height: 18px;
51
+ }
52
+
53
+ /* No cover: the music icon on a raised square, inset by 28 %. */
54
+ .placeholder {
55
+ background: var(--color-surface-raised);
56
+ align-items: center;
57
+ justify-content: center;
58
+ color: var(--color-text-muted);
59
+ }
60
+
61
+ .cover-icon {
62
+ width: 44%;
63
+ height: 44%;
64
+ }
65
+
66
+ .meta {
67
+ flex-direction: column;
68
+ gap: 2px;
69
+ min-width: 0px;
70
+ }
71
+
72
+ .title {
73
+ font-family: var(--font-ui);
74
+ font-size: var(--font-size-body);
75
+ font-weight: 700;
76
+ line-height: 18px;
77
+ color: var(--color-text);
78
+ white-space: nowrap;
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ }
82
+
83
+ .title.scrolling {
84
+ text-overflow: marquee;
85
+ }
86
+
87
+ .artist {
88
+ font-family: var(--font-ui);
89
+ font-size: 12px;
90
+ line-height: 16px;
91
+ color: var(--color-text-muted);
92
+ white-space: nowrap;
93
+ overflow: hidden;
94
+ text-overflow: ellipsis;
95
+ }
96
+
97
+ .empty {
98
+ font-family: var(--font-ui);
99
+ font-size: var(--font-size-body);
100
+ line-height: 18px;
101
+ color: var(--color-text-muted);
102
+ white-space: nowrap;
103
+ overflow: hidden;
104
+ text-overflow: ellipsis;
105
+ }
106
+
107
+ .message {
108
+ font-family: var(--font-ui);
109
+ font-size: var(--font-size-small);
110
+ color: var(--color-text-muted);
111
+ white-space: nowrap;
112
+ overflow: hidden;
113
+ text-overflow: ellipsis;
114
+ }
115
+
116
+ .controls {
117
+ flex-direction: row;
118
+ gap: 3px;
119
+ }
120
+
121
+ /* The built-in's compact icon buttons: raised with a border, lighter under
122
+ the pointer or with the keyboard focus, the accent while pressed. The
123
+ icon (sized by `font-size`) fills the 12 px content box exactly and is
124
+ centred in it. */
125
+ .control {
126
+ width: var(--icon-button-size);
127
+ height: var(--icon-button-size);
128
+ padding: 4px;
129
+ border: 1px solid var(--color-border);
130
+ font-size: 12px;
131
+ justify-content: center;
132
+ align-items: center;
133
+ border-radius: var(--radius-sm);
134
+ background: var(--color-surface-raised);
135
+ color: var(--color-text-secondary);
136
+ }
137
+
138
+ .control:hover,
139
+ .control:focus {
140
+ background: var(--color-surface-hover);
141
+ color: var(--color-text);
142
+ }
143
+
144
+ .control:active {
145
+ background: var(--color-accent-soft);
146
+ border-color: var(--color-accent);
147
+ color: var(--color-accent-hover);
148
+ }