@overcrow/mcp 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +90 -2
- package/dist/audit/index.js +103 -0
- package/dist/audit/lexer.js +417 -0
- package/dist/audit/project.js +200 -0
- package/dist/audit/rules.js +661 -0
- package/dist/bootstrap/download.js +229 -0
- package/dist/bootstrap/install.js +280 -0
- package/dist/bootstrap/pin.js +85 -0
- package/dist/bootstrap/pin.json +119 -0
- package/dist/bootstrap/zip.js +258 -0
- package/dist/cli.js +200 -0
- package/dist/confine.js +244 -0
- package/dist/content/docs/en/cli.md +353 -0
- package/dist/content/docs/en/dev-channel.md +130 -0
- package/dist/content/docs/en/elements.md +469 -0
- package/dist/content/docs/en/forms.md +328 -0
- package/dist/content/docs/en/guide.md +253 -0
- package/dist/content/docs/en/index.md +85 -0
- package/dist/content/docs/en/limits.md +241 -0
- package/dist/content/docs/en/logic.md +446 -0
- package/dist/content/docs/en/manifest.md +381 -0
- package/dist/content/docs/en/mcp.md +165 -0
- package/dist/content/docs/en/package.md +131 -0
- package/dist/content/docs/en/publishing.md +162 -0
- package/dist/content/docs/en/reference.md +55 -0
- package/dist/content/docs/en/result-shapes.md +332 -0
- package/dist/content/docs/en/sdk.md +295 -0
- package/dist/content/docs/en/security.md +115 -0
- package/dist/content/docs/en/service-reference.md +344 -0
- package/dist/content/docs/en/services.md +517 -0
- package/dist/content/docs/en/style-properties.md +270 -0
- package/dist/content/docs/en/style.md +266 -0
- package/dist/content/docs/en/testing.md +400 -0
- package/dist/content/docs/en/view.md +338 -0
- package/dist/content/docs/en/widgets.md +76 -0
- package/dist/content/docs/fr/cli.md +367 -0
- package/dist/content/docs/fr/dev-channel.md +140 -0
- package/dist/content/docs/fr/elements.md +475 -0
- package/dist/content/docs/fr/forms.md +341 -0
- package/dist/content/docs/fr/guide.md +264 -0
- package/dist/content/docs/fr/index.md +90 -0
- package/dist/content/docs/fr/limits.md +244 -0
- package/dist/content/docs/fr/logic.md +461 -0
- package/dist/content/docs/fr/manifest.md +392 -0
- package/dist/content/docs/fr/mcp.md +173 -0
- package/dist/content/docs/fr/package.md +139 -0
- package/dist/content/docs/fr/publishing.md +172 -0
- package/dist/content/docs/fr/reference.md +59 -0
- package/dist/content/docs/fr/result-shapes.md +333 -0
- package/dist/content/docs/fr/sdk.md +301 -0
- package/dist/content/docs/fr/security.md +129 -0
- package/dist/content/docs/fr/service-reference.md +346 -0
- package/dist/content/docs/fr/services.md +540 -0
- package/dist/content/docs/fr/style-properties.md +272 -0
- package/dist/content/docs/fr/style.md +281 -0
- package/dist/content/docs/fr/testing.md +417 -0
- package/dist/content/docs/fr/view.md +352 -0
- package/dist/content/docs/fr/widgets.md +81 -0
- package/dist/content/examples/clock/LICENSE +21 -0
- package/dist/content/examples/clock/README.md +75 -0
- package/dist/content/examples/clock/listing.json +19 -0
- package/dist/content/examples/clock/locales/en.json +4 -0
- package/dist/content/examples/clock/locales/fr.json +4 -0
- package/dist/content/examples/clock/logic.ts +85 -0
- package/dist/content/examples/clock/manifest.json +44 -0
- package/dist/content/examples/clock/package.json +16 -0
- package/dist/content/examples/clock/style.ocss +26 -0
- package/dist/content/examples/clock/tests/hidden.scenario.json +68 -0
- package/dist/content/examples/clock/tests/load-logic.mjs +22 -0
- package/dist/content/examples/clock/tests/logic.test.mjs +89 -0
- package/dist/content/examples/clock/tests/options.scenario.json +108 -0
- package/dist/content/examples/clock/tests/preview.scenario.json +25 -0
- package/dist/content/examples/clock/tests/render.scenario.json +94 -0
- package/dist/content/examples/clock/tests/summer-time.scenario.json +74 -0
- package/dist/content/examples/clock/tests/ticks.scenario.json +120 -0
- package/dist/content/examples/clock/tsconfig.json +16 -0
- package/dist/content/examples/clock/view.ocml +8 -0
- package/dist/content/examples/countdown/LICENSE +20 -0
- package/dist/content/examples/countdown/listing.json +10 -0
- package/dist/content/examples/countdown/locales/en.json +15 -0
- package/dist/content/examples/countdown/locales/fr.json +15 -0
- package/dist/content/examples/countdown/logic.ts +179 -0
- package/dist/content/examples/countdown/manifest.json +34 -0
- package/dist/content/examples/countdown/package.json +15 -0
- package/dist/content/examples/countdown/style.ocss +94 -0
- package/dist/content/examples/countdown/tsconfig.json +16 -0
- package/dist/content/examples/countdown/view.ocml +42 -0
- package/dist/content/examples/fps/LICENSE +21 -0
- package/dist/content/examples/fps/README.md +64 -0
- package/dist/content/examples/fps/listing.json +19 -0
- package/dist/content/examples/fps/locales/en.json +11 -0
- package/dist/content/examples/fps/locales/fr.json +11 -0
- package/dist/content/examples/fps/logic.ts +64 -0
- package/dist/content/examples/fps/manifest.json +30 -0
- package/dist/content/examples/fps/package.json +16 -0
- package/dist/content/examples/fps/style.ocss +31 -0
- package/dist/content/examples/fps/tests/label.scenario.json +63 -0
- package/dist/content/examples/fps/tests/load-logic.mjs +22 -0
- package/dist/content/examples/fps/tests/logic.test.mjs +60 -0
- package/dist/content/examples/fps/tests/preview.scenario.json +29 -0
- package/dist/content/examples/fps/tests/refused.scenario.json +20 -0
- package/dist/content/examples/fps/tests/render.scenario.json +96 -0
- package/dist/content/examples/fps/tests/stale.scenario.json +91 -0
- package/dist/content/examples/fps/tests/unavailable.scenario.json +111 -0
- package/dist/content/examples/fps/tsconfig.json +16 -0
- package/dist/content/examples/fps/view.ocml +11 -0
- package/dist/content/examples/media/LICENSE +21 -0
- package/dist/content/examples/media/README.md +147 -0
- package/dist/content/examples/media/listing.json +19 -0
- package/dist/content/examples/media/locales/en.json +11 -0
- package/dist/content/examples/media/locales/fr.json +11 -0
- package/dist/content/examples/media/logic.ts +191 -0
- package/dist/content/examples/media/manifest.json +35 -0
- package/dist/content/examples/media/package.json +16 -0
- package/dist/content/examples/media/style.ocss +148 -0
- package/dist/content/examples/media/tests/covers.scenario.json +144 -0
- package/dist/content/examples/media/tests/gesture.scenario.json +173 -0
- package/dist/content/examples/media/tests/load-logic.mjs +22 -0
- package/dist/content/examples/media/tests/logic.test.mjs +176 -0
- package/dist/content/examples/media/tests/marquee.scenario.json +127 -0
- package/dist/content/examples/media/tests/preview.scenario.json +52 -0
- package/dist/content/examples/media/tests/read-only.scenario.json +53 -0
- package/dist/content/examples/media/tests/refused.scenario.json +22 -0
- package/dist/content/examples/media/tests/refused.test.mjs +31 -0
- package/dist/content/examples/media/tests/render.scenario.json +136 -0
- package/dist/content/examples/media/tests/states.scenario.json +257 -0
- package/dist/content/examples/media/tests/unavailable.scenario.json +120 -0
- package/dist/content/examples/media/tsconfig.json +16 -0
- package/dist/content/examples/media/view.ocml +41 -0
- package/dist/content/examples/notes/LICENSE +21 -0
- package/dist/content/examples/notes/README.md +219 -0
- package/dist/content/examples/notes/listing.json +19 -0
- package/dist/content/examples/notes/locales/en.json +25 -0
- package/dist/content/examples/notes/locales/fr.json +25 -0
- package/dist/content/examples/notes/logic.ts +799 -0
- package/dist/content/examples/notes/manifest.json +40 -0
- package/dist/content/examples/notes/package.json +16 -0
- package/dist/content/examples/notes/style.ocss +469 -0
- package/dist/content/examples/notes/tests/checks.scenario.json +237 -0
- package/dist/content/examples/notes/tests/create.scenario.json +260 -0
- package/dist/content/examples/notes/tests/delete.scenario.json +342 -0
- package/dist/content/examples/notes/tests/drafts.scenario.json +714 -0
- package/dist/content/examples/notes/tests/editor.scenario.json +232 -0
- package/dist/content/examples/notes/tests/frame.scenario.json +93 -0
- package/dist/content/examples/notes/tests/interactive.scenario.json +344 -0
- package/dist/content/examples/notes/tests/limits.scenario.json +1351 -0
- package/dist/content/examples/notes/tests/load-logic.mjs +22 -0
- package/dist/content/examples/notes/tests/logic.test.mjs +514 -0
- package/dist/content/examples/notes/tests/preview.scenario.json +99 -0
- package/dist/content/examples/notes/tests/read-only.scenario.json +105 -0
- package/dist/content/examples/notes/tests/refused.scenario.json +33 -0
- package/dist/content/examples/notes/tests/save.scenario.json +378 -0
- package/dist/content/examples/notes/tests/states.scenario.json +640 -0
- package/dist/content/examples/notes/tests/typing.scenario.json +562 -0
- package/dist/content/examples/notes/tests/unavailable.scenario.json +246 -0
- package/dist/content/examples/notes/tsconfig.json +16 -0
- package/dist/content/examples/notes/view.ocml +154 -0
- package/dist/content/examples/performance/LICENSE +21 -0
- package/dist/content/examples/performance/README.md +134 -0
- package/dist/content/examples/performance/listing.json +19 -0
- package/dist/content/examples/performance/locales/en.json +20 -0
- package/dist/content/examples/performance/locales/fr.json +20 -0
- package/dist/content/examples/performance/logic.ts +210 -0
- package/dist/content/examples/performance/manifest.json +83 -0
- package/dist/content/examples/performance/package.json +16 -0
- package/dist/content/examples/performance/style.ocss +75 -0
- package/dist/content/examples/performance/tests/fahrenheit.scenario.json +110 -0
- package/dist/content/examples/performance/tests/horizontal.scenario.json +96 -0
- package/dist/content/examples/performance/tests/load-logic.mjs +22 -0
- package/dist/content/examples/performance/tests/logic.test.mjs +226 -0
- package/dist/content/examples/performance/tests/menu.scenario.json +170 -0
- package/dist/content/examples/performance/tests/no-fps-source.scenario.json +47 -0
- package/dist/content/examples/performance/tests/preview.scenario.json +45 -0
- package/dist/content/examples/performance/tests/refused.scenario.json +20 -0
- package/dist/content/examples/performance/tests/refused.test.mjs +19 -0
- package/dist/content/examples/performance/tests/region.scenario.json +89 -0
- package/dist/content/examples/performance/tests/render.scenario.json +112 -0
- package/dist/content/examples/performance/tests/sources.scenario.json +151 -0
- package/dist/content/examples/performance/tests/stale.scenario.json +111 -0
- package/dist/content/examples/performance/tests/telemetry-only.scenario.json +44 -0
- package/dist/content/examples/performance/tests/thresholds.scenario.json +147 -0
- package/dist/content/examples/performance/tests/waiting.scenario.json +105 -0
- package/dist/content/examples/performance/tsconfig.json +16 -0
- package/dist/content/examples/performance/view.ocml +18 -0
- package/dist/content/examples/playervox-journal/LICENSE +21 -0
- package/dist/content/examples/playervox-journal/README.md +150 -0
- package/dist/content/examples/playervox-journal/listing.json +19 -0
- package/dist/content/examples/playervox-journal/locales/en.json +25 -0
- package/dist/content/examples/playervox-journal/locales/fr.json +25 -0
- package/dist/content/examples/playervox-journal/logic.ts +556 -0
- package/dist/content/examples/playervox-journal/manifest.json +19 -0
- package/dist/content/examples/playervox-journal/package.json +16 -0
- package/dist/content/examples/playervox-journal/style.ocss +142 -0
- package/dist/content/examples/playervox-journal/tests/changes.scenario.json +482 -0
- package/dist/content/examples/playervox-journal/tests/delete.scenario.json +432 -0
- package/dist/content/examples/playervox-journal/tests/frame.scenario.json +169 -0
- package/dist/content/examples/playervox-journal/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-journal/tests/logic.test.mjs +349 -0
- package/dist/content/examples/playervox-journal/tests/paging.scenario.json +428 -0
- package/dist/content/examples/playervox-journal/tests/preview.scenario.json +184 -0
- package/dist/content/examples/playervox-journal/tests/read-only.scenario.json +157 -0
- package/dist/content/examples/playervox-journal/tests/refused.scenario.json +39 -0
- package/dist/content/examples/playervox-journal/tests/scale.scenario.json +286 -0
- package/dist/content/examples/playervox-journal/tests/signed-out.scenario.json +134 -0
- package/dist/content/examples/playervox-journal/tests/states.scenario.json +741 -0
- package/dist/content/examples/playervox-journal/tests/unavailable.scenario.json +183 -0
- package/dist/content/examples/playervox-journal/tsconfig.json +16 -0
- package/dist/content/examples/playervox-journal/view.ocml +52 -0
- package/dist/content/examples/playervox-rating/LICENSE +21 -0
- package/dist/content/examples/playervox-rating/README.md +166 -0
- package/dist/content/examples/playervox-rating/listing.json +19 -0
- package/dist/content/examples/playervox-rating/locales/en.json +35 -0
- package/dist/content/examples/playervox-rating/locales/fr.json +35 -0
- package/dist/content/examples/playervox-rating/logic.ts +581 -0
- package/dist/content/examples/playervox-rating/manifest.json +19 -0
- package/dist/content/examples/playervox-rating/package.json +16 -0
- package/dist/content/examples/playervox-rating/style.ocss +333 -0
- package/dist/content/examples/playervox-rating/tests/account.scenario.json +125 -0
- package/dist/content/examples/playervox-rating/tests/bounds.scenario.json +73 -0
- package/dist/content/examples/playervox-rating/tests/draft.scenario.json +199 -0
- package/dist/content/examples/playervox-rating/tests/form.scenario.json +142 -0
- package/dist/content/examples/playervox-rating/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-rating/tests/logic.test.mjs +346 -0
- package/dist/content/examples/playervox-rating/tests/offline.scenario.json +142 -0
- package/dist/content/examples/playervox-rating/tests/preview.scenario.json +44 -0
- package/dist/content/examples/playervox-rating/tests/publish-keyboard.scenario.json +221 -0
- package/dist/content/examples/playervox-rating/tests/publish-pointer.scenario.json +314 -0
- package/dist/content/examples/playervox-rating/tests/read-only.scenario.json +70 -0
- package/dist/content/examples/playervox-rating/tests/refused.scenario.json +45 -0
- package/dist/content/examples/playervox-rating/tests/states.scenario.json +362 -0
- package/dist/content/examples/playervox-rating/tsconfig.json +16 -0
- package/dist/content/examples/playervox-rating/view.ocml +62 -0
- package/dist/content/examples/playervox-reviews/LICENSE +21 -0
- package/dist/content/examples/playervox-reviews/README.md +194 -0
- package/dist/content/examples/playervox-reviews/listing.json +19 -0
- package/dist/content/examples/playervox-reviews/locales/en.json +32 -0
- package/dist/content/examples/playervox-reviews/locales/fr.json +32 -0
- package/dist/content/examples/playervox-reviews/logic.ts +822 -0
- package/dist/content/examples/playervox-reviews/manifest.json +30 -0
- package/dist/content/examples/playervox-reviews/package.json +16 -0
- package/dist/content/examples/playervox-reviews/style.ocss +260 -0
- package/dist/content/examples/playervox-reviews/tests/account.scenario.json +249 -0
- package/dist/content/examples/playervox-reviews/tests/bounds.scenario.json +336 -0
- package/dist/content/examples/playervox-reviews/tests/changes.scenario.json +515 -0
- package/dist/content/examples/playervox-reviews/tests/filter.scenario.json +400 -0
- package/dist/content/examples/playervox-reviews/tests/frame.scenario.json +185 -0
- package/dist/content/examples/playervox-reviews/tests/keyboard.scenario.json +188 -0
- package/dist/content/examples/playervox-reviews/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-reviews/tests/logic.test.mjs +559 -0
- package/dist/content/examples/playervox-reviews/tests/offline-start.scenario.json +152 -0
- package/dist/content/examples/playervox-reviews/tests/offline.scenario.json +311 -0
- package/dist/content/examples/playervox-reviews/tests/paging.scenario.json +497 -0
- package/dist/content/examples/playervox-reviews/tests/preview.scenario.json +286 -0
- package/dist/content/examples/playervox-reviews/tests/reading.scenario.json +591 -0
- package/dist/content/examples/playervox-reviews/tests/refused.scenario.json +54 -0
- package/dist/content/examples/playervox-reviews/tests/scale.scenario.json +303 -0
- package/dist/content/examples/playervox-reviews/tests/states.scenario.json +700 -0
- package/dist/content/examples/playervox-reviews/tests/unavailable.scenario.json +192 -0
- package/dist/content/examples/playervox-reviews/tsconfig.json +16 -0
- package/dist/content/examples/playervox-reviews/view.ocml +110 -0
- package/dist/content/examples/playervox-score/LICENSE +21 -0
- package/dist/content/examples/playervox-score/README.md +131 -0
- package/dist/content/examples/playervox-score/listing.json +19 -0
- package/dist/content/examples/playervox-score/locales/en.json +23 -0
- package/dist/content/examples/playervox-score/locales/fr.json +23 -0
- package/dist/content/examples/playervox-score/logic.ts +430 -0
- package/dist/content/examples/playervox-score/manifest.json +47 -0
- package/dist/content/examples/playervox-score/package.json +16 -0
- package/dist/content/examples/playervox-score/style.ocss +247 -0
- package/dist/content/examples/playervox-score/tests/grades.scenario.json +279 -0
- package/dist/content/examples/playervox-score/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-score/tests/logic.test.mjs +229 -0
- package/dist/content/examples/playervox-score/tests/options.scenario.json +302 -0
- package/dist/content/examples/playervox-score/tests/preview.scenario.json +68 -0
- package/dist/content/examples/playervox-score/tests/pulse.scenario.json +157 -0
- package/dist/content/examples/playervox-score/tests/refused.scenario.json +21 -0
- package/dist/content/examples/playervox-score/tests/signed-out.scenario.json +46 -0
- package/dist/content/examples/playervox-score/tests/states.scenario.json +450 -0
- package/dist/content/examples/playervox-score/tests/unavailable.scenario.json +84 -0
- package/dist/content/examples/playervox-score/tsconfig.json +16 -0
- package/dist/content/examples/playervox-score/view.ocml +51 -0
- package/dist/content/examples/session/LICENSE +21 -0
- package/dist/content/examples/session/README.md +57 -0
- package/dist/content/examples/session/listing.json +19 -0
- package/dist/content/examples/session/locales/en.json +3 -0
- package/dist/content/examples/session/locales/fr.json +3 -0
- package/dist/content/examples/session/logic.ts +26 -0
- package/dist/content/examples/session/manifest.json +19 -0
- package/dist/content/examples/session/package.json +16 -0
- package/dist/content/examples/session/style.ocss +16 -0
- package/dist/content/examples/session/tests/hidden.scenario.json +45 -0
- package/dist/content/examples/session/tests/load-logic.mjs +22 -0
- package/dist/content/examples/session/tests/logic.test.mjs +37 -0
- package/dist/content/examples/session/tests/preview.scenario.json +28 -0
- package/dist/content/examples/session/tests/refused.scenario.json +20 -0
- package/dist/content/examples/session/tests/refused.test.mjs +16 -0
- package/dist/content/examples/session/tests/render.scenario.json +95 -0
- package/dist/content/examples/session/tests/ticks.scenario.json +139 -0
- package/dist/content/examples/session/tests/unknown.scenario.json +37 -0
- package/dist/content/examples/session/tsconfig.json +16 -0
- package/dist/content/examples/session/view.ocml +11 -0
- package/dist/content/examples/stopwatch/LICENSE +21 -0
- package/dist/content/examples/stopwatch/README.md +99 -0
- package/dist/content/examples/stopwatch/listing.json +19 -0
- package/dist/content/examples/stopwatch/locales/en.json +8 -0
- package/dist/content/examples/stopwatch/locales/fr.json +8 -0
- package/dist/content/examples/stopwatch/logic.ts +93 -0
- package/dist/content/examples/stopwatch/manifest.json +19 -0
- package/dist/content/examples/stopwatch/package.json +16 -0
- package/dist/content/examples/stopwatch/style.ocss +54 -0
- package/dist/content/examples/stopwatch/tests/errors.scenario.json +121 -0
- package/dist/content/examples/stopwatch/tests/gesture.scenario.json +154 -0
- package/dist/content/examples/stopwatch/tests/hidden.scenario.json +57 -0
- package/dist/content/examples/stopwatch/tests/hundredths.scenario.json +173 -0
- package/dist/content/examples/stopwatch/tests/load-logic.mjs +22 -0
- package/dist/content/examples/stopwatch/tests/logic.test.mjs +99 -0
- package/dist/content/examples/stopwatch/tests/preview.scenario.json +36 -0
- package/dist/content/examples/stopwatch/tests/read-only.scenario.json +42 -0
- package/dist/content/examples/stopwatch/tests/read-only.test.mjs +18 -0
- package/dist/content/examples/stopwatch/tests/refused.scenario.json +22 -0
- package/dist/content/examples/stopwatch/tests/refused.test.mjs +18 -0
- package/dist/content/examples/stopwatch/tests/render.scenario.json +105 -0
- package/dist/content/examples/stopwatch/tests/states.scenario.json +246 -0
- package/dist/content/examples/stopwatch/tests/unknown.scenario.json +40 -0
- package/dist/content/examples/stopwatch/tsconfig.json +16 -0
- package/dist/content/examples/stopwatch/view.ocml +29 -0
- package/dist/content/examples/twitch-chat/LICENSE +21 -0
- package/dist/content/examples/twitch-chat/README.md +283 -0
- package/dist/content/examples/twitch-chat/listing.json +19 -0
- package/dist/content/examples/twitch-chat/locales/en.json +52 -0
- package/dist/content/examples/twitch-chat/locales/fr.json +52 -0
- package/dist/content/examples/twitch-chat/logic.ts +887 -0
- package/dist/content/examples/twitch-chat/manifest.json +33 -0
- package/dist/content/examples/twitch-chat/package.json +16 -0
- package/dist/content/examples/twitch-chat/style.ocss +544 -0
- package/dist/content/examples/twitch-chat/tests/account.scenario.json +325 -0
- package/dist/content/examples/twitch-chat/tests/burst.scenario.json +1866 -0
- package/dist/content/examples/twitch-chat/tests/channel-refused.scenario.json +372 -0
- package/dist/content/examples/twitch-chat/tests/channel.scenario.json +480 -0
- package/dist/content/examples/twitch-chat/tests/draft.scenario.json +279 -0
- package/dist/content/examples/twitch-chat/tests/fade.scenario.json +269 -0
- package/dist/content/examples/twitch-chat/tests/failures.scenario.json +420 -0
- package/dist/content/examples/twitch-chat/tests/favorites.scenario.json +376 -0
- package/dist/content/examples/twitch-chat/tests/frame.scenario.json +152 -0
- package/dist/content/examples/twitch-chat/tests/lifetime.scenario.json +186 -0
- package/dist/content/examples/twitch-chat/tests/load-logic.mjs +22 -0
- package/dist/content/examples/twitch-chat/tests/logic.test.mjs +1053 -0
- package/dist/content/examples/twitch-chat/tests/passive.scenario.json +536 -0
- package/dist/content/examples/twitch-chat/tests/preview.scenario.json +147 -0
- package/dist/content/examples/twitch-chat/tests/read-only.scenario.json +202 -0
- package/dist/content/examples/twitch-chat/tests/read-only.test.mjs +63 -0
- package/dist/content/examples/twitch-chat/tests/refused.scenario.json +65 -0
- package/dist/content/examples/twitch-chat/tests/refused.test.mjs +43 -0
- package/dist/content/examples/twitch-chat/tests/render.scenario.json +347 -0
- package/dist/content/examples/twitch-chat/tests/reply.scenario.json +384 -0
- package/dist/content/examples/twitch-chat/tests/send-refused.scenario.json +383 -0
- package/dist/content/examples/twitch-chat/tests/send.scenario.json +300 -0
- package/dist/content/examples/twitch-chat/tests/states.scenario.json +455 -0
- package/dist/content/examples/twitch-chat/tests/unavailable.scenario.json +256 -0
- package/dist/content/examples/twitch-chat/tests/unread.scenario.json +997 -0
- package/dist/content/examples/twitch-chat/tsconfig.json +16 -0
- package/dist/content/examples/twitch-chat/view.ocml +141 -0
- package/dist/content/examples/warframe-market/LICENSE +21 -0
- package/dist/content/examples/warframe-market/README.md +176 -0
- package/dist/content/examples/warframe-market/listing.json +19 -0
- package/dist/content/examples/warframe-market/locales/en.json +44 -0
- package/dist/content/examples/warframe-market/locales/fr.json +44 -0
- package/dist/content/examples/warframe-market/logic.ts +1265 -0
- package/dist/content/examples/warframe-market/manifest.json +92 -0
- package/dist/content/examples/warframe-market/package.json +16 -0
- package/dist/content/examples/warframe-market/style.ocss +422 -0
- package/dist/content/examples/warframe-market/tests/bounds.scenario.json +60 -0
- package/dist/content/examples/warframe-market/tests/cached.scenario.json +70 -0
- package/dist/content/examples/warframe-market/tests/detail.scenario.json +251 -0
- package/dist/content/examples/warframe-market/tests/errors.scenario.json +105 -0
- package/dist/content/examples/warframe-market/tests/fixtures.json +295 -0
- package/dist/content/examples/warframe-market/tests/load-logic.mjs +22 -0
- package/dist/content/examples/warframe-market/tests/logic.test.mjs +505 -0
- package/dist/content/examples/warframe-market/tests/no-clipboard.scenario.json +104 -0
- package/dist/content/examples/warframe-market/tests/passive.scenario.json +87 -0
- package/dist/content/examples/warframe-market/tests/preview.scenario.json +146 -0
- package/dist/content/examples/warframe-market/tests/refresh.scenario.json +56 -0
- package/dist/content/examples/warframe-market/tests/refused.scenario.json +74 -0
- package/dist/content/examples/warframe-market/tests/render.scenario.json +210 -0
- package/dist/content/examples/warframe-market/tests/search.scenario.json +189 -0
- package/dist/content/examples/warframe-market/tests/unavailable.scenario.json +54 -0
- package/dist/content/examples/warframe-market/tsconfig.json +16 -0
- package/dist/content/examples/warframe-market/view.ocml +116 -0
- package/dist/content/examples/weather/LICENSE +20 -0
- package/dist/content/examples/weather/listing.json +10 -0
- package/dist/content/examples/weather/locales/en.json +9 -0
- package/dist/content/examples/weather/locales/fr.json +9 -0
- package/dist/content/examples/weather/logic.ts +62 -0
- package/dist/content/examples/weather/manifest.json +51 -0
- package/dist/content/examples/weather/package.json +15 -0
- package/dist/content/examples/weather/style.ocss +29 -0
- package/dist/content/examples/weather/tsconfig.json +16 -0
- package/dist/content/examples/weather/view.ocml +12 -0
- package/dist/content/index.json +3601 -0
- package/dist/content/reference/errors.json +1712 -0
- package/dist/content/reference/limits.json +1154 -0
- package/dist/content/reference/manifest.md +223 -0
- package/dist/content/reference/permissions.json +413 -0
- package/dist/content/templates/blank/locales/en.json +3 -0
- package/dist/content/templates/blank/locales/fr.json +3 -0
- package/dist/content/templates/blank/logic.ts +7 -0
- package/dist/content/templates/blank/manifest.json +16 -0
- package/dist/content/templates/blank/style.ocss +10 -0
- package/dist/content/templates/blank/tests/example.scenario.json +10 -0
- package/dist/content/templates/blank/view.ocml +6 -0
- package/dist/content/templates/chart/locales/en.json +3 -0
- package/dist/content/templates/chart/locales/fr.json +3 -0
- package/dist/content/templates/chart/logic.ts +27 -0
- package/dist/content/templates/chart/manifest.json +16 -0
- package/dist/content/templates/chart/style.ocss +27 -0
- package/dist/content/templates/chart/tests/example.scenario.json +10 -0
- package/dist/content/templates/chart/view.ocml +10 -0
- package/dist/content/templates/counter/locales/en.json +4 -0
- package/dist/content/templates/counter/locales/fr.json +4 -0
- package/dist/content/templates/counter/logic.ts +20 -0
- package/dist/content/templates/counter/manifest.json +16 -0
- package/dist/content/templates/counter/style.ocss +26 -0
- package/dist/content/templates/counter/tests/example.scenario.json +11 -0
- package/dist/content/templates/counter/view.ocml +13 -0
- package/dist/content/templates/list/locales/en.json +8 -0
- package/dist/content/templates/list/locales/fr.json +8 -0
- package/dist/content/templates/list/logic.ts +35 -0
- package/dist/content/templates/list/manifest.json +16 -0
- package/dist/content/templates/list/style.ocss +42 -0
- package/dist/content/templates/list/tests/example.scenario.json +10 -0
- package/dist/content/templates/list/view.ocml +17 -0
- package/dist/content/templates/shared/LICENSE +20 -0
- package/dist/content/templates/shared/package.json +14 -0
- package/dist/content/templates/shared/tsconfig.json +16 -0
- package/dist/content.js +68 -0
- package/dist/env.js +156 -0
- package/dist/fingerprint.js +32 -0
- package/dist/ids.js +27 -0
- package/dist/index.js +82 -0
- package/dist/knowledge.js +222 -0
- package/dist/npm.js +149 -0
- package/dist/prompts.js +47 -0
- package/dist/resources.js +82 -0
- package/dist/run.js +120 -0
- package/dist/server.js +43 -0
- package/dist/session.js +205 -0
- package/dist/sources.js +218 -0
- package/dist/text.js +80 -0
- package/dist/texts.js +44 -0
- package/dist/tools/audit.js +105 -0
- package/dist/tools/build.js +453 -0
- package/dist/tools/common.js +122 -0
- package/dist/tools/docs.js +196 -0
- package/dist/tools/preview.js +87 -0
- package/dist/tools/project.js +178 -0
- package/dist/tools/setup.js +146 -0
- package/dist/tools/submission.js +369 -0
- package/dist/version.js +3 -0
- package/npm-shrinkwrap.json +402 -0
- package/package.json +52 -4
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
// PlayerVox Journal: the finished play sessions of the active game, newest
|
|
2
|
+
// first, five per page. OverCrow records the sessions, merges the local
|
|
3
|
+
// ones with the PlayerVox journal and owns the deletion's confirmation;
|
|
4
|
+
// the widget follows the journal's revision, reads its own page and lays
|
|
5
|
+
// it out.
|
|
6
|
+
import {
|
|
7
|
+
draw,
|
|
8
|
+
formatDate,
|
|
9
|
+
formatTime,
|
|
10
|
+
hasGrant,
|
|
11
|
+
host,
|
|
12
|
+
initState,
|
|
13
|
+
journal,
|
|
14
|
+
onHost,
|
|
15
|
+
t,
|
|
16
|
+
timers,
|
|
17
|
+
type Color,
|
|
18
|
+
type DrawCommand,
|
|
19
|
+
type JournalPage,
|
|
20
|
+
type JournalSession,
|
|
21
|
+
type JournalState,
|
|
22
|
+
type ServiceError,
|
|
23
|
+
type Subscription,
|
|
24
|
+
type Timer,
|
|
25
|
+
} from "@overcrow/sdk";
|
|
26
|
+
|
|
27
|
+
/** What the widget shows in place of, or above, the sessions. */
|
|
28
|
+
export type Phase = "loading" | "idle" | "ready" | "failed";
|
|
29
|
+
|
|
30
|
+
declare module "@overcrow/sdk" {
|
|
31
|
+
interface WidgetState {
|
|
32
|
+
/**
|
|
33
|
+
* `loading` until the first page, `idle` without an active game,
|
|
34
|
+
* `ready` with a page, `failed` without one after a failure.
|
|
35
|
+
*/
|
|
36
|
+
phase: Phase;
|
|
37
|
+
/** The page shown; `null` until the first one. */
|
|
38
|
+
page: JournalPage | null;
|
|
39
|
+
/** The journal's own condition, from the host. */
|
|
40
|
+
notice: JournalState["notice"];
|
|
41
|
+
/** Message key of the last failed read or deletion; `""` for none. */
|
|
42
|
+
problem: string;
|
|
43
|
+
/** A read or a deletion is in flight: the controls wait. */
|
|
44
|
+
busy: boolean;
|
|
45
|
+
interactive: boolean;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* After the host ends the subscription with a failure, the widget
|
|
51
|
+
* subscribes again this much later.
|
|
52
|
+
*/
|
|
53
|
+
export const RETRY_MS = 5_000;
|
|
54
|
+
|
|
55
|
+
/** A read answered `busy` or `stale_context` is asked again this much later. */
|
|
56
|
+
export const REREAD_MS = 1_000;
|
|
57
|
+
|
|
58
|
+
/** Consecutive re-reads after `busy` or `stale_context`; the next revision reads again. */
|
|
59
|
+
export const MAX_REREADS = 3;
|
|
60
|
+
|
|
61
|
+
/** `label` and `tooltip` bound (`MAX_LABEL_BYTES`). */
|
|
62
|
+
const MAX_LABEL_BYTES = 256;
|
|
63
|
+
|
|
64
|
+
// <shared:playervox-badge/badge.ts>
|
|
65
|
+
// The PlayerVox grade badge and mark, shared by the Score and Rating
|
|
66
|
+
// built-ins; the Journal draws the mark alone, and Player reviews the mark
|
|
67
|
+
// and a smaller badge per review. scripts/sync-shared-widgets.mjs
|
|
68
|
+
// copies this file, as is, into
|
|
69
|
+
// their logic.ts between the markers `// <shared:playervox-badge/badge.ts>`;
|
|
70
|
+
// edit it here, never in a copy. The copy relies on the widget importing
|
|
71
|
+
// the `Color` and `DrawCommand` types from @overcrow/sdk.
|
|
72
|
+
|
|
73
|
+
/** A PlayerVox grade, or `--` without a valid score. */
|
|
74
|
+
export type BadgeGrade = "S+" | "S" | "A" | "B" | "C" | "D" | "F" | "--";
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The grade of a 0–100 value with PlayerVox's thresholds, on the raw value
|
|
78
|
+
* (89.6 is S, although it shows as 90); `--` for none or out of range. The
|
|
79
|
+
* host grades the overall score itself; widgets grade the criteria.
|
|
80
|
+
*/
|
|
81
|
+
export function gradeOf(value: number | null): BadgeGrade {
|
|
82
|
+
if (value === null || !Number.isFinite(value) || value < 0 || value > 100) {
|
|
83
|
+
return "--";
|
|
84
|
+
}
|
|
85
|
+
if (value >= 90) return "S+";
|
|
86
|
+
if (value >= 80) return "S";
|
|
87
|
+
if (value >= 70) return "A";
|
|
88
|
+
if (value >= 60) return "B";
|
|
89
|
+
if (value >= 40) return "C";
|
|
90
|
+
if (value >= 20) return "D";
|
|
91
|
+
return "F";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The grade colours of PlayerVox's site (its Tailwind palette in sRGB), in
|
|
96
|
+
* both themes: the badge keeps its black face on a light panel too.
|
|
97
|
+
*/
|
|
98
|
+
export const GRADE_COLORS: Readonly<Record<BadgeGrade, Color>> = {
|
|
99
|
+
"S+": "#9ae600",
|
|
100
|
+
S: "#7ccf00",
|
|
101
|
+
A: "#00d3f3",
|
|
102
|
+
B: "#fdc700",
|
|
103
|
+
C: "#fe9a00",
|
|
104
|
+
D: "#e7000b",
|
|
105
|
+
F: "#52525c",
|
|
106
|
+
"--": "#52525c",
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** The style class of a grade's colour (`badge.ocss`): `grade-s-plus`… */
|
|
110
|
+
export function gradeClass(grade: BadgeGrade): string {
|
|
111
|
+
return grade === "--" ? "grade-none" : `grade-${grade.toLowerCase().replace("+", "-plus")}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Side of the badge canvas in logical px: the 56 px badge turned by 4°,
|
|
116
|
+
* its 3 px border and the S+ glow, centred.
|
|
117
|
+
*/
|
|
118
|
+
export const BADGE_CANVAS = 76;
|
|
119
|
+
|
|
120
|
+
const BADGE_HALF = 28;
|
|
121
|
+
const BADGE_RADIUS = 12;
|
|
122
|
+
/** The letter's baseline below the centre: the capitals sit centred. */
|
|
123
|
+
const LETTER_BASELINE = 9;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The neutral `--` badge per theme: a translucent face and a grey border
|
|
127
|
+
* and text that keep their contrast on the panel. A graded badge keeps its
|
|
128
|
+
* black face in both themes.
|
|
129
|
+
*/
|
|
130
|
+
const NEUTRAL: Readonly<Record<"dark" | "light", { face: Color; color: Color }>> = {
|
|
131
|
+
dark: { face: "#27272a80", color: "#52525c" },
|
|
132
|
+
light: { face: "#e4e4e780", color: "#71717a" },
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The badge of a grade: a rounded square with a black face and a border
|
|
137
|
+
* of the grade's colour, turned by 4° with its letter (not `--`, which is
|
|
138
|
+
* neutral and square), and a faint static glow for S+.
|
|
139
|
+
*/
|
|
140
|
+
export function badgeCommands(grade: BadgeGrade, theme: "dark" | "light"): DrawCommand[] {
|
|
141
|
+
const neutral = grade === "--" ? NEUTRAL[theme] : null;
|
|
142
|
+
const color = neutral?.color ?? GRADE_COLORS[grade];
|
|
143
|
+
const centre = BADGE_CANVAS / 2;
|
|
144
|
+
const square = (): DrawCommand => [
|
|
145
|
+
"rect",
|
|
146
|
+
-BADGE_HALF,
|
|
147
|
+
-BADGE_HALF,
|
|
148
|
+
BADGE_HALF * 2,
|
|
149
|
+
BADGE_HALF * 2,
|
|
150
|
+
BADGE_RADIUS,
|
|
151
|
+
];
|
|
152
|
+
const commands: DrawCommand[] = [
|
|
153
|
+
["save"],
|
|
154
|
+
["translate", centre, centre],
|
|
155
|
+
["rotate", grade === "--" ? 0 : 4],
|
|
156
|
+
];
|
|
157
|
+
if (grade === "S+") {
|
|
158
|
+
// Eight widening rings of the accent at 2 % each: a soft halo.
|
|
159
|
+
for (let ring = 8; ring >= 1; ring -= 1) {
|
|
160
|
+
commands.push(square(), ["stroke", "#a3e63505", 3 + ring * 1.5]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
commands.push(
|
|
164
|
+
square(),
|
|
165
|
+
["fill", neutral?.face ?? "#000000"],
|
|
166
|
+
square(),
|
|
167
|
+
["stroke", color, 3],
|
|
168
|
+
["text", 0, LETTER_BASELINE, grade, 26, color, "center", "display", "700"],
|
|
169
|
+
["restore"],
|
|
170
|
+
);
|
|
171
|
+
return commands;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The badge's pulse class after `current` (`badge.ocss`): it alternates, so
|
|
176
|
+
* that each new score restarts the host's animation.
|
|
177
|
+
*/
|
|
178
|
+
export function nextPulse(current: string): "pulse-a" | "pulse-b" {
|
|
179
|
+
return current === "pulse-a" ? "pulse-b" : "pulse-a";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The accessible name of a badge: "Grade S+, 94/100". */
|
|
183
|
+
export function badgeLabel(grade: BadgeGrade, score: number | null, words: { grade: string; none: string }): string {
|
|
184
|
+
if (grade === "--" || score === null) {
|
|
185
|
+
return words.none;
|
|
186
|
+
}
|
|
187
|
+
return `${words.grade} ${grade}, ${roundHalfAway(score)}/100`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Rounds half away from zero: 72.5 is 73 (parity sheet decision 6). */
|
|
191
|
+
export function roundHalfAway(value: number): number {
|
|
192
|
+
return Math.sign(value) * Math.round(Math.abs(value));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// The PlayerVox mark: three rising bars, in unit coordinates of its box.
|
|
196
|
+
const MARK_BARS: readonly (readonly number[])[] = [
|
|
197
|
+
[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],
|
|
198
|
+
[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],
|
|
199
|
+
[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],
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The PlayerVox mark, `height` px tall and 0.85 as wide, filled with
|
|
204
|
+
* `color` and centred in a canvas `canvasWidth` px wide: give the canvas a
|
|
205
|
+
* whole number of pixels, so that its texture is never resampled.
|
|
206
|
+
*/
|
|
207
|
+
export function markCommands(canvasWidth: number, height: number, color: Color): DrawCommand[] {
|
|
208
|
+
const width = height * 0.85;
|
|
209
|
+
const left = (canvasWidth - width) / 2;
|
|
210
|
+
const commands: DrawCommand[] = [];
|
|
211
|
+
for (const bar of MARK_BARS) {
|
|
212
|
+
for (let index = 0; index + 1 < bar.length; index += 2) {
|
|
213
|
+
const x = left + (bar[index] ?? 0) * width;
|
|
214
|
+
const y = (bar[index + 1] ?? 0) * height;
|
|
215
|
+
commands.push(index === 0 ? ["moveTo", x, y] : ["lineTo", x, y]);
|
|
216
|
+
}
|
|
217
|
+
commands.push(["close"], ["fill", color]);
|
|
218
|
+
}
|
|
219
|
+
return commands;
|
|
220
|
+
}
|
|
221
|
+
// </shared:playervox-badge/badge.ts>
|
|
222
|
+
|
|
223
|
+
const canRead = hasGrant("journal.read");
|
|
224
|
+
const canDelete = hasGrant("journal.delete");
|
|
225
|
+
|
|
226
|
+
const state = initState({
|
|
227
|
+
phase: (canRead ? "loading" : "failed") as Phase,
|
|
228
|
+
page: null as JournalPage | null,
|
|
229
|
+
notice: null as JournalState["notice"],
|
|
230
|
+
problem: canRead ? "" : "no-permission",
|
|
231
|
+
busy: false,
|
|
232
|
+
interactive: host.mode === "interactive",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
/** Provider text on one line. */
|
|
236
|
+
export function oneLine(text: string): string {
|
|
237
|
+
return text.replace(/\s*[\r\n]+\s*/g, " ").trim();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** `text` cut to `limit` UTF-8 bytes on a character boundary. */
|
|
241
|
+
export function cutBytes(text: string, limit: number): string {
|
|
242
|
+
let bytes = 0;
|
|
243
|
+
let end = 0;
|
|
244
|
+
for (const character of text) {
|
|
245
|
+
const code = character.codePointAt(0) ?? 0;
|
|
246
|
+
const size = code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
|
|
247
|
+
if (bytes + size > limit) {
|
|
248
|
+
return text.slice(0, end);
|
|
249
|
+
}
|
|
250
|
+
bytes += size;
|
|
251
|
+
end += character.length;
|
|
252
|
+
}
|
|
253
|
+
return text;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** The game's name, when the host has one. */
|
|
257
|
+
export function gameName(page: JournalPage | null): string {
|
|
258
|
+
return page === null ? "" : oneLine(page.gameName);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* A session's start in the local time it carries: the date in the user's
|
|
263
|
+
* order, then the 24-hour time ("09/20/2026 · 20:30", FR "20/09/2026 ·
|
|
264
|
+
* 20:30"); `--` for a timestamp outside the calendar.
|
|
265
|
+
*/
|
|
266
|
+
export function startText(session: JournalSession): string {
|
|
267
|
+
const options = { offsetMinutes: session.offsetMinutes };
|
|
268
|
+
if (!Number.isFinite(session.startedAt) || session.startedAt < 0) {
|
|
269
|
+
return "--";
|
|
270
|
+
}
|
|
271
|
+
return `${formatDate(session.startedAt, options)} · ${formatTime(session.startedAt, options)}`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** A duration as whole hours and minutes: 2700 s is "0 h 45 min". */
|
|
275
|
+
export function durationText(durationMs: number): string {
|
|
276
|
+
const minutes = Math.floor(Math.max(0, Number.isFinite(durationMs) ? durationMs : 0) / 60_000);
|
|
277
|
+
return t("duration", {
|
|
278
|
+
hours: String(Math.floor(minutes / 60)),
|
|
279
|
+
minutes: String(minutes % 60).padStart(2, "0"),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** One session row. */
|
|
284
|
+
export interface Row {
|
|
285
|
+
id: string;
|
|
286
|
+
start: string;
|
|
287
|
+
duration: string;
|
|
288
|
+
/** Accessible name: the start and the duration. */
|
|
289
|
+
label: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function rows(page: JournalPage | null): Row[] {
|
|
293
|
+
return (page?.items ?? []).map((session) => {
|
|
294
|
+
const start = startText(session);
|
|
295
|
+
const duration = durationText(session.durationMs);
|
|
296
|
+
return {
|
|
297
|
+
id: session.id,
|
|
298
|
+
start,
|
|
299
|
+
duration,
|
|
300
|
+
label: cutBytes(t("session", { date: start, duration }), MAX_LABEL_BYTES),
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** The message key of a journal condition. */
|
|
306
|
+
export function noticeKey(notice: JournalState["notice"]): string {
|
|
307
|
+
switch (notice) {
|
|
308
|
+
case null:
|
|
309
|
+
return "";
|
|
310
|
+
case "offline":
|
|
311
|
+
return "notice-offline";
|
|
312
|
+
case "storage_unavailable":
|
|
313
|
+
return "notice-storage";
|
|
314
|
+
case "full":
|
|
315
|
+
return "notice-full";
|
|
316
|
+
case "expired":
|
|
317
|
+
return "notice-expired";
|
|
318
|
+
case "busy":
|
|
319
|
+
return "notice-busy";
|
|
320
|
+
default:
|
|
321
|
+
return "notice-unavailable";
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* The line above the sessions: the last failed action, else the journal's
|
|
327
|
+
* condition; `""` for none. Without a page, a failed read shows the
|
|
328
|
+
* journal's condition when the host gave one (it says why).
|
|
329
|
+
*/
|
|
330
|
+
export function message(problem: string, notice: JournalState["notice"]): string {
|
|
331
|
+
if (problem === "read-failed" && notice !== null) {
|
|
332
|
+
return t(noticeKey(notice));
|
|
333
|
+
}
|
|
334
|
+
const key = problem !== "" ? problem : noticeKey(notice);
|
|
335
|
+
return key === "" ? "" : t(key);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** The message key of a refused deletion. */
|
|
339
|
+
export function deleteProblem(code: string): string {
|
|
340
|
+
switch (code) {
|
|
341
|
+
case "not_connected":
|
|
342
|
+
return "delete-not-connected";
|
|
343
|
+
case "busy":
|
|
344
|
+
return "notice-busy";
|
|
345
|
+
case "quota_exceeded":
|
|
346
|
+
return "notice-full";
|
|
347
|
+
default:
|
|
348
|
+
return "delete-failed";
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** The trash buttons show in Interactive mode, with the grant. */
|
|
353
|
+
export function canRemove(interactive: boolean): boolean {
|
|
354
|
+
return interactive && canDelete;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** The footer shows as soon as the journal has more than one page. */
|
|
358
|
+
export function paged(page: JournalPage | null): boolean {
|
|
359
|
+
return page !== null && (page.previous !== null || page.next !== null);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** "Page 2", and on the last of several pages "Page 3 · End of the journal". */
|
|
363
|
+
export function pageLabel(page: JournalPage): string {
|
|
364
|
+
const number = String(Math.max(1, Math.floor(page.page)));
|
|
365
|
+
return t(page.next === null && page.previous !== null ? "page-end" : "page", { page: number });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Handle of the page shown; `undefined` for the first page. */
|
|
369
|
+
let cursor: string | undefined;
|
|
370
|
+
/** The journal revision the page shown was read for. */
|
|
371
|
+
let revision: number | null = null;
|
|
372
|
+
let reading = false;
|
|
373
|
+
let deleting = false;
|
|
374
|
+
/** The journal changed during a read: read once more after it. */
|
|
375
|
+
let again = false;
|
|
376
|
+
let rereads = 0;
|
|
377
|
+
let reread: Timer | null = null;
|
|
378
|
+
/** Bumped when the journal goes: a read answered after it is dropped. */
|
|
379
|
+
let epoch = 0;
|
|
380
|
+
|
|
381
|
+
function settle(): void {
|
|
382
|
+
state.busy = reading || deleting;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** Reads the page at `target` (the first page without one) and shows it. */
|
|
386
|
+
function read(target: string | undefined): void {
|
|
387
|
+
if (reading) {
|
|
388
|
+
again = true;
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
reread?.cancel();
|
|
392
|
+
reread = null;
|
|
393
|
+
reading = true;
|
|
394
|
+
settle();
|
|
395
|
+
const started = epoch;
|
|
396
|
+
journal.page(target === undefined ? {} : { cursor: target }).then(
|
|
397
|
+
(page) => {
|
|
398
|
+
reading = false;
|
|
399
|
+
if (started !== epoch) {
|
|
400
|
+
finish();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
rereads = 0;
|
|
404
|
+
// The host answers the last page for a page that no longer exists:
|
|
405
|
+
// back on the first one, the widget follows the first page again.
|
|
406
|
+
cursor = page.previous === null ? undefined : target;
|
|
407
|
+
state.page = page;
|
|
408
|
+
state.phase = "ready";
|
|
409
|
+
if (state.problem === "read-failed") {
|
|
410
|
+
state.problem = "";
|
|
411
|
+
}
|
|
412
|
+
finish();
|
|
413
|
+
},
|
|
414
|
+
(error: ServiceError) => {
|
|
415
|
+
reading = false;
|
|
416
|
+
if (started === epoch) {
|
|
417
|
+
failed(error.code, target);
|
|
418
|
+
}
|
|
419
|
+
finish();
|
|
420
|
+
},
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function finish(): void {
|
|
425
|
+
if (again) {
|
|
426
|
+
again = false;
|
|
427
|
+
read(cursor);
|
|
428
|
+
}
|
|
429
|
+
settle();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function failed(code: string, target: string | undefined): void {
|
|
433
|
+
if (code === "invalid_request" && target !== undefined) {
|
|
434
|
+
// The host no longer knows this handle: back to the first page.
|
|
435
|
+
cursor = undefined;
|
|
436
|
+
again = true;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if ((code === "busy" || code === "stale_context") && rereads < MAX_REREADS) {
|
|
440
|
+
// The host was busy, or the game or account changed under the read:
|
|
441
|
+
// ask again shortly, from the first page after a change.
|
|
442
|
+
rereads += 1;
|
|
443
|
+
const next = code === "busy" ? target : undefined;
|
|
444
|
+
reread = timers.after(REREAD_MS, () => {
|
|
445
|
+
reread = null;
|
|
446
|
+
read(next);
|
|
447
|
+
});
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
// The page shown stays; the next change of the journal reads again.
|
|
451
|
+
state.problem = "read-failed";
|
|
452
|
+
if (state.page === null) {
|
|
453
|
+
state.phase = "failed";
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** Shows the previous or next page; one request at a time. */
|
|
458
|
+
export function turn(direction: "previous" | "next"): void {
|
|
459
|
+
const target = state.page?.[direction] ?? null;
|
|
460
|
+
if (!state.interactive || state.busy || target === null) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
state.problem = "";
|
|
464
|
+
read(target);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Deletes a session. The host asks the user to confirm first: declined,
|
|
469
|
+
* nothing changes; accepted, the journal's new revision shows the page
|
|
470
|
+
* without it.
|
|
471
|
+
*/
|
|
472
|
+
export function remove(session: string): void {
|
|
473
|
+
if (!canRemove(state.interactive) || state.busy) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
deleting = true;
|
|
477
|
+
state.problem = "";
|
|
478
|
+
settle();
|
|
479
|
+
journal.delete({ session }).then(
|
|
480
|
+
() => {
|
|
481
|
+
deleting = false;
|
|
482
|
+
read(cursor);
|
|
483
|
+
settle();
|
|
484
|
+
},
|
|
485
|
+
(error: ServiceError) => {
|
|
486
|
+
deleting = false;
|
|
487
|
+
if (error.code !== "cancelled") {
|
|
488
|
+
state.problem = deleteProblem(error.code);
|
|
489
|
+
}
|
|
490
|
+
settle();
|
|
491
|
+
},
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Without a game there is no journal: nothing of the last game stays. */
|
|
496
|
+
function clear(phase: Phase, problem: string): void {
|
|
497
|
+
reread?.cancel();
|
|
498
|
+
reread = null;
|
|
499
|
+
epoch += 1;
|
|
500
|
+
again = false;
|
|
501
|
+
rereads = 0;
|
|
502
|
+
cursor = undefined;
|
|
503
|
+
revision = null;
|
|
504
|
+
state.page = null;
|
|
505
|
+
state.notice = null;
|
|
506
|
+
state.problem = problem;
|
|
507
|
+
state.phase = phase;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
let subscription: Subscription | null = null;
|
|
511
|
+
|
|
512
|
+
function subscribe(): void {
|
|
513
|
+
const current = journal.subscribe((update) => {
|
|
514
|
+
if (!update.ok) {
|
|
515
|
+
// The subscription ended: the host's journal source failed. Show it
|
|
516
|
+
// and ask again later; hidden, the timer waits until the widget shows.
|
|
517
|
+
if (subscription === current) {
|
|
518
|
+
subscription = null;
|
|
519
|
+
clear("failed", "source-failed");
|
|
520
|
+
timers.after(RETRY_MS, subscribe);
|
|
521
|
+
}
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const value = update.value;
|
|
525
|
+
if (value === null) {
|
|
526
|
+
clear("idle", "");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (state.phase === "idle" || state.problem === "source-failed") {
|
|
530
|
+
state.phase = "loading";
|
|
531
|
+
state.problem = "";
|
|
532
|
+
}
|
|
533
|
+
state.notice = value.notice;
|
|
534
|
+
// A new revision, or a page still missing after a failure: read the
|
|
535
|
+
// page shown again (its handle stays valid).
|
|
536
|
+
if (value.revision !== revision || state.page === null) {
|
|
537
|
+
revision = value.revision;
|
|
538
|
+
rereads = 0;
|
|
539
|
+
read(cursor);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
subscription = current;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
onHost((changed) => {
|
|
546
|
+
if (changed.includes("mode")) {
|
|
547
|
+
state.interactive = host.mode === "interactive";
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
draw("mark", markCommands(16, 18, "var(--color-text-muted)"));
|
|
552
|
+
// Without the grant the host would refuse the subscription: say so
|
|
553
|
+
// instead of asking.
|
|
554
|
+
if (canRead) {
|
|
555
|
+
subscribe();
|
|
556
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"apiVersion": 1,
|
|
4
|
+
"id": "com.playervox.overcrow.playervox.journal",
|
|
5
|
+
"version": "1.0.2",
|
|
6
|
+
"name": {
|
|
7
|
+
"en": "Session journal",
|
|
8
|
+
"fr": "Journal de sessions"
|
|
9
|
+
},
|
|
10
|
+
"sizing": {
|
|
11
|
+
"fit": "height",
|
|
12
|
+
"preferred": { "width": 350, "height": 320 },
|
|
13
|
+
"min": { "width": 280, "height": 60 },
|
|
14
|
+
"max": { "width": 900, "height": 480 }
|
|
15
|
+
},
|
|
16
|
+
"permissions": {
|
|
17
|
+
"capabilities": ["journal.read", "journal.delete"]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "overcrow-widget-playervox-journal",
|
|
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
|
+
}
|