@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,430 @@
1
+ // PlayerVox Score: the public PlayerVox community grade of the active Steam
2
+ // game. OverCrow fetches the score and follows PlayerVox's live updates (no
3
+ // account needed) and grades it; the widget lays it out, draws the badge
4
+ // and asks the host for one pulse when a new score shows.
5
+ import {
6
+ draw,
7
+ formatNumber,
8
+ hasGrant,
9
+ host,
10
+ initState,
11
+ onHost,
12
+ option,
13
+ playervox,
14
+ t,
15
+ timers,
16
+ type Color,
17
+ type DrawCommand,
18
+ type Score,
19
+ type Subscription,
20
+ type Timer,
21
+ } from "@overcrow/sdk";
22
+
23
+ declare module "@overcrow/sdk" {
24
+ interface WidgetState {
25
+ /** The latest score; `null` until the host's first answer. */
26
+ score: Score | null;
27
+ showScore: boolean;
28
+ showCriteria: boolean;
29
+ showTitle: boolean;
30
+ showVotes: boolean;
31
+ /** The badge canvas's classes, with its pulse. */
32
+ badgeClass: string;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * After the host ends the subscription with a failure, the widget
38
+ * subscribes again this much later.
39
+ */
40
+ export const RETRY_MS = 5_000;
41
+
42
+ /** `label` and `tooltip` bound (`MAX_LABEL_BYTES`). */
43
+ const MAX_LABEL_BYTES = 256;
44
+
45
+ // <shared:playervox-badge/badge.ts>
46
+ // The PlayerVox grade badge and mark, shared by the Score and Rating
47
+ // built-ins; the Journal draws the mark alone, and Player reviews the mark
48
+ // and a smaller badge per review. scripts/sync-shared-widgets.mjs
49
+ // copies this file, as is, into
50
+ // their logic.ts between the markers `// <shared:playervox-badge/badge.ts>`;
51
+ // edit it here, never in a copy. The copy relies on the widget importing
52
+ // the `Color` and `DrawCommand` types from @overcrow/sdk.
53
+
54
+ /** A PlayerVox grade, or `--` without a valid score. */
55
+ export type BadgeGrade = "S+" | "S" | "A" | "B" | "C" | "D" | "F" | "--";
56
+
57
+ /**
58
+ * The grade of a 0–100 value with PlayerVox's thresholds, on the raw value
59
+ * (89.6 is S, although it shows as 90); `--` for none or out of range. The
60
+ * host grades the overall score itself; widgets grade the criteria.
61
+ */
62
+ export function gradeOf(value: number | null): BadgeGrade {
63
+ if (value === null || !Number.isFinite(value) || value < 0 || value > 100) {
64
+ return "--";
65
+ }
66
+ if (value >= 90) return "S+";
67
+ if (value >= 80) return "S";
68
+ if (value >= 70) return "A";
69
+ if (value >= 60) return "B";
70
+ if (value >= 40) return "C";
71
+ if (value >= 20) return "D";
72
+ return "F";
73
+ }
74
+
75
+ /**
76
+ * The grade colours of PlayerVox's site (its Tailwind palette in sRGB), in
77
+ * both themes: the badge keeps its black face on a light panel too.
78
+ */
79
+ export const GRADE_COLORS: Readonly<Record<BadgeGrade, Color>> = {
80
+ "S+": "#9ae600",
81
+ S: "#7ccf00",
82
+ A: "#00d3f3",
83
+ B: "#fdc700",
84
+ C: "#fe9a00",
85
+ D: "#e7000b",
86
+ F: "#52525c",
87
+ "--": "#52525c",
88
+ };
89
+
90
+ /** The style class of a grade's colour (`badge.ocss`): `grade-s-plus`… */
91
+ export function gradeClass(grade: BadgeGrade): string {
92
+ return grade === "--" ? "grade-none" : `grade-${grade.toLowerCase().replace("+", "-plus")}`;
93
+ }
94
+
95
+ /**
96
+ * Side of the badge canvas in logical px: the 56 px badge turned by 4°,
97
+ * its 3 px border and the S+ glow, centred.
98
+ */
99
+ export const BADGE_CANVAS = 76;
100
+
101
+ const BADGE_HALF = 28;
102
+ const BADGE_RADIUS = 12;
103
+ /** The letter's baseline below the centre: the capitals sit centred. */
104
+ const LETTER_BASELINE = 9;
105
+
106
+ /**
107
+ * The neutral `--` badge per theme: a translucent face and a grey border
108
+ * and text that keep their contrast on the panel. A graded badge keeps its
109
+ * black face in both themes.
110
+ */
111
+ const NEUTRAL: Readonly<Record<"dark" | "light", { face: Color; color: Color }>> = {
112
+ dark: { face: "#27272a80", color: "#52525c" },
113
+ light: { face: "#e4e4e780", color: "#71717a" },
114
+ };
115
+
116
+ /**
117
+ * The badge of a grade: a rounded square with a black face and a border
118
+ * of the grade's colour, turned by 4° with its letter (not `--`, which is
119
+ * neutral and square), and a faint static glow for S+.
120
+ */
121
+ export function badgeCommands(grade: BadgeGrade, theme: "dark" | "light"): DrawCommand[] {
122
+ const neutral = grade === "--" ? NEUTRAL[theme] : null;
123
+ const color = neutral?.color ?? GRADE_COLORS[grade];
124
+ const centre = BADGE_CANVAS / 2;
125
+ const square = (): DrawCommand => [
126
+ "rect",
127
+ -BADGE_HALF,
128
+ -BADGE_HALF,
129
+ BADGE_HALF * 2,
130
+ BADGE_HALF * 2,
131
+ BADGE_RADIUS,
132
+ ];
133
+ const commands: DrawCommand[] = [
134
+ ["save"],
135
+ ["translate", centre, centre],
136
+ ["rotate", grade === "--" ? 0 : 4],
137
+ ];
138
+ if (grade === "S+") {
139
+ // Eight widening rings of the accent at 2 % each: a soft halo.
140
+ for (let ring = 8; ring >= 1; ring -= 1) {
141
+ commands.push(square(), ["stroke", "#a3e63505", 3 + ring * 1.5]);
142
+ }
143
+ }
144
+ commands.push(
145
+ square(),
146
+ ["fill", neutral?.face ?? "#000000"],
147
+ square(),
148
+ ["stroke", color, 3],
149
+ ["text", 0, LETTER_BASELINE, grade, 26, color, "center", "display", "700"],
150
+ ["restore"],
151
+ );
152
+ return commands;
153
+ }
154
+
155
+ /**
156
+ * The badge's pulse class after `current` (`badge.ocss`): it alternates, so
157
+ * that each new score restarts the host's animation.
158
+ */
159
+ export function nextPulse(current: string): "pulse-a" | "pulse-b" {
160
+ return current === "pulse-a" ? "pulse-b" : "pulse-a";
161
+ }
162
+
163
+ /** The accessible name of a badge: "Grade S+, 94/100". */
164
+ export function badgeLabel(grade: BadgeGrade, score: number | null, words: { grade: string; none: string }): string {
165
+ if (grade === "--" || score === null) {
166
+ return words.none;
167
+ }
168
+ return `${words.grade} ${grade}, ${roundHalfAway(score)}/100`;
169
+ }
170
+
171
+ /** Rounds half away from zero: 72.5 is 73 (parity sheet decision 6). */
172
+ export function roundHalfAway(value: number): number {
173
+ return Math.sign(value) * Math.round(Math.abs(value));
174
+ }
175
+
176
+ // The PlayerVox mark: three rising bars, in unit coordinates of its box.
177
+ const MARK_BARS: readonly (readonly number[])[] = [
178
+ [0.09403, 0.29375, 0.04806, 0.3068, 0.02098, 0.34, 0.01785, 0.38207, 0.01785, 0.42385, 0.01785, 0.46563, 0.01785, 0.50742, 0.01785, 0.5492, 0.01785, 0.59098, 0.01785, 0.63276, 0.01785, 0.67454, 0.01785, 0.71632, 0.01785, 0.75811, 0.01785, 0.79989, 0.01785, 0.84167, 0.01785, 0.88345, 0.01785, 0.92523, 0.01785, 0.96701, 0.04115, 0.96149, 0.07617, 0.93218, 0.11119, 0.90286, 0.14621, 0.87354, 0.18123, 0.84422, 0.21625, 0.8149, 0.25127, 0.78558, 0.28629, 0.75626, 0.31138, 0.72341, 0.31138, 0.68162, 0.31138, 0.63984, 0.31138, 0.59806, 0.31138, 0.55628, 0.31138, 0.5145, 0.31138, 0.47272, 0.31138, 0.43094, 0.31138, 0.38915, 0.30981, 0.34705, 0.28603, 0.31194, 0.24179, 0.29574, 0.19233, 0.29497, 0.14318, 0.29436],
179
+ [0.43235, 0.1525, 0.38372, 0.16747, 0.35807, 0.20475, 0.35676, 0.24975, 0.35676, 0.29442, 0.35676, 0.33908, 0.35676, 0.38375, 0.35676, 0.42842, 0.35676, 0.47308, 0.35676, 0.51775, 0.35676, 0.56242, 0.35676, 0.60708, 0.35676, 0.65175, 0.35676, 0.69642, 0.3648, 0.73425, 0.41735, 0.73425, 0.4699, 0.73425, 0.52245, 0.73425, 0.575, 0.73425, 0.62755, 0.73425, 0.65059, 0.70917, 0.65059, 0.6645, 0.65059, 0.61983, 0.65059, 0.57517, 0.65059, 0.5305, 0.65059, 0.48583, 0.65059, 0.44117, 0.65059, 0.3965, 0.65059, 0.35183, 0.65059, 0.30717, 0.65059, 0.2625, 0.65059, 0.21783, 0.63377, 0.17623, 0.59039, 0.15382, 0.53745, 0.1525, 0.4849, 0.1525],
180
+ [0.76765, 0.01375, 0.71253, 0.03393, 0.69206, 0.08189, 0.69206, 0.13433, 0.69206, 0.18678, 0.69206, 0.23922, 0.69206, 0.29167, 0.69206, 0.34411, 0.69206, 0.39656, 0.69206, 0.449, 0.69206, 0.50144, 0.69206, 0.55389, 0.69206, 0.60633, 0.69206, 0.65878, 0.69206, 0.71122, 0.7252, 0.7355, 0.7869, 0.7355, 0.84859, 0.7355, 0.91029, 0.7355, 0.97199, 0.7355, 0.98588, 0.69486, 0.98588, 0.64242, 0.98588, 0.58997, 0.98588, 0.53753, 0.98588, 0.48508, 0.98588, 0.43264, 0.98588, 0.38019, 0.98588, 0.32775, 0.98588, 0.27531, 0.98588, 0.22286, 0.98588, 0.17042, 0.98588, 0.11797, 0.98438, 0.06513, 0.95084, 0.02369, 0.89105, 0.01375, 0.82935, 0.01375],
181
+ ];
182
+
183
+ /**
184
+ * The PlayerVox mark, `height` px tall and 0.85 as wide, filled with
185
+ * `color` and centred in a canvas `canvasWidth` px wide: give the canvas a
186
+ * whole number of pixels, so that its texture is never resampled.
187
+ */
188
+ export function markCommands(canvasWidth: number, height: number, color: Color): DrawCommand[] {
189
+ const width = height * 0.85;
190
+ const left = (canvasWidth - width) / 2;
191
+ const commands: DrawCommand[] = [];
192
+ for (const bar of MARK_BARS) {
193
+ for (let index = 0; index + 1 < bar.length; index += 2) {
194
+ const x = left + (bar[index] ?? 0) * width;
195
+ const y = (bar[index + 1] ?? 0) * height;
196
+ commands.push(index === 0 ? ["moveTo", x, y] : ["lineTo", x, y]);
197
+ }
198
+ commands.push(["close"], ["fill", color]);
199
+ }
200
+ return commands;
201
+ }
202
+ // </shared:playervox-badge/badge.ts>
203
+
204
+ const canRead = hasGrant("playervox.score.read");
205
+
206
+ /** The score shown without the grant, or once the host's source failed. */
207
+ const UNAVAILABLE: Score = {
208
+ state: "unavailable",
209
+ name: null,
210
+ score: null,
211
+ grade: "--",
212
+ ratingsCount: 0,
213
+ criteria: { gameplay: null, art: null, tech: null },
214
+ };
215
+
216
+ const state = initState({
217
+ score: canRead ? null : UNAVAILABLE,
218
+ showScore: option("show-score", true),
219
+ showCriteria: option("show-criteria", false),
220
+ showTitle: option("show-title", true),
221
+ showVotes: option("show-votes", true),
222
+ badgeClass: "badge",
223
+ });
224
+
225
+ interface Display {
226
+ showScore: boolean;
227
+ showCriteria: boolean;
228
+ showTitle: boolean;
229
+ showVotes: boolean;
230
+ }
231
+
232
+ /** Every option off: the badge alone, which always stays. */
233
+ export function badgeOnly(display: Display): boolean {
234
+ return !display.showScore && !display.showCriteria && !display.showTitle && !display.showVotes;
235
+ }
236
+
237
+ export function rootClass(display: Display): string {
238
+ if (badgeOnly(display)) {
239
+ return "score badge-only";
240
+ }
241
+ return display.showCriteria ? "score with-criteria" : "score";
242
+ }
243
+
244
+ /** The score beside the badge, or the badge centred alone. */
245
+ export function hasSide(display: Display): boolean {
246
+ return display.showScore || display.showVotes;
247
+ }
248
+
249
+ export function mainClass(display: Display): string {
250
+ return hasSide(display) ? "main" : "main centred";
251
+ }
252
+
253
+ /** A rated game: the score and its count show instead of a status. */
254
+ export function rated(score: Score | null): boolean {
255
+ return score !== null && score.state === "ready" && score.score !== null;
256
+ }
257
+
258
+ /** The game's name once PlayerVox answered, else "Community rating". */
259
+ export function title(score: Score | null): string {
260
+ if (score !== null && score.name !== null && score.name.trim() !== "") {
261
+ return oneLine(score.name);
262
+ }
263
+ return t("community");
264
+ }
265
+
266
+ /** Provider text on one line. */
267
+ export function oneLine(text: string): string {
268
+ return text.replace(/\s*[\r\n]+\s*/g, " ").trim();
269
+ }
270
+
271
+ /** The rounded score: 89.6 shows "90" (its grade stays S). */
272
+ export function scoreText(score: Score | null): string {
273
+ return score === null || score.score === null ? "--" : String(roundHalfAway(score.score));
274
+ }
275
+
276
+ /** "1,284 ratings", singular only for exactly one. */
277
+ export function votes(score: Score | null): string {
278
+ const count = score === null ? 0 : score.ratingsCount;
279
+ return t(count === 1 ? "votes-one" : "votes-other", {
280
+ count: formatNumber(count, { maximumFractionDigits: 0 }),
281
+ });
282
+ }
283
+
284
+ /** The count's row: under the score, or level with its middle without. */
285
+ export function votesClass(showScore: boolean): string {
286
+ return showScore ? "votes" : "votes alone";
287
+ }
288
+
289
+ /** Message keys of a state that shows no score: a label and a hint. */
290
+ export interface Status {
291
+ label: string;
292
+ hint: string;
293
+ }
294
+
295
+ export function status(score: Score | null): Status {
296
+ switch (score === null ? "loading" : score.state) {
297
+ case "idle":
298
+ return { label: "waiting", hint: "" };
299
+ case "unsupported":
300
+ return { label: "unsupported", hint: "steam-required" };
301
+ case "loading":
302
+ return { label: "loading", hint: "" };
303
+ case "not_found":
304
+ return { label: "not-found", hint: "on-playervox" };
305
+ case "unavailable":
306
+ return { label: "unavailable", hint: "retrying" };
307
+ default:
308
+ // `no_ratings`, or `ready` without a score.
309
+ return { label: "unrated", hint: "no-votes" };
310
+ }
311
+ }
312
+
313
+ /** Hover text: the game's name once known, else the status. */
314
+ export function tooltip(score: Score | null): string {
315
+ const text = rated(score) || score?.state === "no_ratings" ? title(score) : t(status(score).label);
316
+ return cutBytes(text, MAX_LABEL_BYTES);
317
+ }
318
+
319
+ /** `text` cut to `limit` UTF-8 bytes on a character boundary. */
320
+ export function cutBytes(text: string, limit: number): string {
321
+ let bytes = 0;
322
+ let end = 0;
323
+ for (const character of text) {
324
+ const code = character.codePointAt(0) ?? 0;
325
+ const size = code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
326
+ if (bytes + size > limit) {
327
+ return text.slice(0, end);
328
+ }
329
+ bytes += size;
330
+ end += character.length;
331
+ }
332
+ return text;
333
+ }
334
+
335
+ /** The badge's accessible name: "Grade S+, 94/100", or "No grade". */
336
+ export function badgeName(score: Score | null): string {
337
+ return cutBytes(
338
+ badgeLabel(score?.grade ?? "--", score?.score ?? null, { grade: t("grade"), none: t("no-grade") }),
339
+ MAX_LABEL_BYTES,
340
+ );
341
+ }
342
+
343
+ /** One criterion row. */
344
+ export interface Criterion {
345
+ key: "gameplay" | "art" | "tech";
346
+ name: string;
347
+ /** Rounded value, or "--" when PlayerVox has none. */
348
+ text: string;
349
+ /** Bar value: 0 without one (an empty track). */
350
+ value: number;
351
+ /** The bar's classes, coloured with the value's own grade. */
352
+ bar: string;
353
+ label: string;
354
+ }
355
+
356
+ export function criteria(score: Score | null): Criterion[] {
357
+ const values = score === null ? UNAVAILABLE.criteria : score.criteria;
358
+ return (["gameplay", "art", "tech"] as const).map((key) => {
359
+ const value = values[key];
360
+ const name = t(key);
361
+ const text = value === null ? "--" : String(roundHalfAway(value));
362
+ return {
363
+ key,
364
+ name,
365
+ text,
366
+ value: value ?? 0,
367
+ bar: `bar ${gradeClass(gradeOf(value))}`,
368
+ label: t("criterion", { name, value: text }),
369
+ };
370
+ });
371
+ }
372
+
373
+ /** What the badge shows: a pulse starts when this changes to a score. */
374
+ let shown = "";
375
+
376
+ /** Draws the badge of `score` and pulses it for a new score. */
377
+ function showBadge(score: Score | null): void {
378
+ const grade = score?.grade ?? "--";
379
+ draw("badge", badgeCommands(grade, host.theme));
380
+ const key = rated(score) && grade !== "--" ? `${grade}:${scoreText(score)}` : "";
381
+ if (key !== "" && key !== shown) {
382
+ const current = state.badgeClass.split(" ")[1] ?? "";
383
+ state.badgeClass = `badge ${nextPulse(current)}`;
384
+ }
385
+ shown = key;
386
+ }
387
+
388
+ let subscription: Subscription | null = null;
389
+ let retry: Timer | null = null;
390
+
391
+ function subscribe(): void {
392
+ retry = null;
393
+ const current = playervox.score.subscribe((update) => {
394
+ if (update.ok) {
395
+ state.score = update.value;
396
+ showBadge(update.value);
397
+ return;
398
+ }
399
+ // The subscription ended: the host's source failed. Show it and ask
400
+ // again later; hidden, the timer waits until the widget shows.
401
+ if (subscription === current) {
402
+ subscription = null;
403
+ state.score = UNAVAILABLE;
404
+ showBadge(UNAVAILABLE);
405
+ retry = timers.after(RETRY_MS, subscribe);
406
+ }
407
+ });
408
+ subscription = current;
409
+ }
410
+
411
+ onHost((changed) => {
412
+ if (changed.includes("theme")) {
413
+ // The neutral badge follows the theme; the mark's token does already.
414
+ draw("badge", badgeCommands(state.score?.grade ?? "--", host.theme));
415
+ }
416
+ if (changed.includes("options")) {
417
+ state.showScore = option("show-score", true);
418
+ state.showCriteria = option("show-criteria", false);
419
+ state.showTitle = option("show-title", true);
420
+ state.showVotes = option("show-votes", true);
421
+ }
422
+ });
423
+
424
+ draw("mark", markCommands(16, 18, "var(--color-text-muted)"));
425
+ showBadge(state.score);
426
+ // Without the grant the host would refuse the subscription: show the score
427
+ // as unavailable instead of asking.
428
+ if (canRead) {
429
+ subscribe();
430
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "apiVersion": 1,
4
+ "id": "com.playervox.overcrow.playervox.score",
5
+ "version": "1.0.2",
6
+ "name": {
7
+ "en": "PlayerVox score",
8
+ "fr": "Note PlayerVox"
9
+ },
10
+ "sizing": {
11
+ "fit": "both",
12
+ "preferred": { "width": 240, "height": 104 },
13
+ "min": { "width": 24, "height": 24 },
14
+ "max": { "width": 480, "height": 240 }
15
+ },
16
+ "permissions": {
17
+ "capabilities": ["playervox.score.read"]
18
+ },
19
+ "wrapper": {
20
+ "menu": [
21
+ {
22
+ "type": "toggle",
23
+ "id": "show-score",
24
+ "label": { "en": "Show overall score", "fr": "Afficher le score global" },
25
+ "default": true
26
+ },
27
+ {
28
+ "type": "toggle",
29
+ "id": "show-criteria",
30
+ "label": { "en": "Show three criteria", "fr": "Afficher les trois critères" },
31
+ "default": false
32
+ },
33
+ {
34
+ "type": "toggle",
35
+ "id": "show-title",
36
+ "label": { "en": "Show game title", "fr": "Afficher le nom du jeu" },
37
+ "default": true
38
+ },
39
+ {
40
+ "type": "toggle",
41
+ "id": "show-votes",
42
+ "label": { "en": "Show rating count", "fr": "Afficher le nombre de notes" },
43
+ "default": true
44
+ }
45
+ ]
46
+ }
47
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "overcrow-widget-playervox-score",
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
+ }