@overcrow/mcp 0.0.0 → 0.1.1
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 +91 -1
- 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 +768 -0
- package/dist/content/docs/en/creator-space.md +78 -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 +252 -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 +388 -0
- package/dist/content/docs/en/mcp.md +165 -0
- package/dist/content/docs/en/package.md +131 -0
- package/dist/content/docs/en/publish-a-widget.md +94 -0
- package/dist/content/docs/en/publish-keys.md +95 -0
- package/dist/content/docs/en/publishing.md +167 -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/verified-domain.md +46 -0
- package/dist/content/docs/en/view.md +338 -0
- package/dist/content/docs/en/widget-listing.md +65 -0
- package/dist/content/docs/en/widget-review.md +57 -0
- package/dist/content/docs/en/widget-statistics.md +134 -0
- package/dist/content/docs/en/widgets.md +76 -0
- package/dist/content/docs/fr/cli.md +802 -0
- package/dist/content/docs/fr/creator-space.md +83 -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 +263 -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 +401 -0
- package/dist/content/docs/fr/mcp.md +173 -0
- package/dist/content/docs/fr/package.md +139 -0
- package/dist/content/docs/fr/publish-a-widget.md +101 -0
- package/dist/content/docs/fr/publish-keys.md +97 -0
- package/dist/content/docs/fr/publishing.md +177 -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/verified-domain.md +49 -0
- package/dist/content/docs/fr/view.md +352 -0
- package/dist/content/docs/fr/widget-listing.md +69 -0
- package/dist/content/docs/fr/widget-review.md +59 -0
- package/dist/content/docs/fr/widget-statistics.md +141 -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/submission.json +8 -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/submission.json +12 -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/submission.json +12 -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/submission.json +12 -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/submission.json +12 -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/submission.json +12 -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/submission.json +8 -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/submission.json +8 -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/submission.json +8 -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/submission.json +12 -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/submission.json +12 -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/submission.json +24 -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/submission.json +17 -0
- package/dist/content/examples/weather/tsconfig.json +16 -0
- package/dist/content/examples/weather/view.ocml +12 -0
- package/dist/content/index.json +3705 -0
- package/dist/content/reference/errors.json +1924 -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/hidden.js +47 -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 +183 -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 +50 -4
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Limits
|
|
2
|
+
|
|
3
|
+
Everything in a PlayerVox OverCrow widget is bounded: the size of its
|
|
4
|
+
files, the number of elements of its view, the time of a turn of its logic,
|
|
5
|
+
the bytes of a response. The bounds keep a widget small and keep one widget
|
|
6
|
+
from slowing the game or the others. This page lists the ones a widget
|
|
7
|
+
author meets.
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
error[view.too_many_children]: the view exceeds a size bound of the schema
|
|
11
|
+
--> view.ocml:15:14375
|
|
12
|
+
= help: split long static content, or render repeated content with <for>
|
|
13
|
+
check: 1 error(s), 0 warning(s)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
An element with more than 1024 children: `check` names the bound, the place
|
|
17
|
+
and a way out.
|
|
18
|
+
|
|
19
|
+
## What happens at a limit
|
|
20
|
+
|
|
21
|
+
- **In the project's files**, a limit is an error of `overcrow-widget
|
|
22
|
+
check`, with a code that names it (`view.too_many_elements`,
|
|
23
|
+
`style.too_many_rules`, `project.file_size`…). Nothing is truncated.
|
|
24
|
+
- **In a service call**, a parameter out of bounds fails with
|
|
25
|
+
`invalid_request`, a full quota with `quota_exceeded`, and too many
|
|
26
|
+
requests at once with `busy`. See [errors](services.md#errors).
|
|
27
|
+
- **While the logic runs**, exceeding a budget of the VM (the time of a
|
|
28
|
+
turn, the heap, the messages of a turn) stops the widget; OverCrow
|
|
29
|
+
restarts it a few times. See
|
|
30
|
+
[what happens on a failure](logic.md#what-happens-on-a-failure).
|
|
31
|
+
- **In what the widget shows**, a value out of bounds (a text too long for
|
|
32
|
+
an attribute, too many nodes) is refused by OverCrow and stops the widget
|
|
33
|
+
too: bound what you show, for example the number of rows of a list.
|
|
34
|
+
|
|
35
|
+
Lengths are logical pixels at 100 % content scale. Text limits count UTF-8
|
|
36
|
+
bytes unless the unit says characters. KiB and MiB are 1024 and 1024²
|
|
37
|
+
bytes. The limits that the logic can read are also constants of the SDK:
|
|
38
|
+
see [limit constants](sdk.md#limit-constants).
|
|
39
|
+
|
|
40
|
+
## Limits by topic
|
|
41
|
+
|
|
42
|
+
<!-- generated:limits -->
|
|
43
|
+
### Project files and package
|
|
44
|
+
|
|
45
|
+
| Limit | Value | Meaning |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| `MAX_VIEW_SOURCE_BYTES` | 256 KiB | `view.ocml` source size. |
|
|
48
|
+
| `MAX_STYLE_SOURCE_BYTES` | 128 KiB | `style.ocss` source size. |
|
|
49
|
+
| `MAX_LOGIC_BYTES` | 512 KiB | The `logic.js` script, compiled code of the view included. |
|
|
50
|
+
| `MAX_LOCALE_ENTRIES` | 2048 | Messages in one `locales/<locale>.json` file. |
|
|
51
|
+
| `MAX_LOCALE_KEY_BYTES` | 128 bytes | Length of one message key. |
|
|
52
|
+
| `MAX_LOCALE_VALUE_BYTES` | 4 KiB | Length of one message text. |
|
|
53
|
+
| `MAX_LOCALE_FILE_BYTES` | 256 KiB | One `locales/<locale>.json` file. |
|
|
54
|
+
| `MAX_MANIFEST_BYTES` | 64 KiB | `manifest.json` size. |
|
|
55
|
+
| `MAX_LICENSE_BYTES` | 64 KiB | `LICENSE` text. |
|
|
56
|
+
| `MAX_COMPILED_VIEW_BYTES` | 512 KiB | `view.json`, the compiled view. |
|
|
57
|
+
| `MAX_PACKAGE_BYTES` | 16 MiB | Size of one `.ocpkg` archive. |
|
|
58
|
+
| `MAX_PACKAGE_FILES` | 256 | Entries in one archive, `manifest.json` and `ledger.json` included. |
|
|
59
|
+
| `MAX_PACKAGE_PATH_BYTES` | 128 bytes | Path of one archive entry. |
|
|
60
|
+
| `MAX_ASSET_PATH_SEGMENTS` | 4 | Path segments below `assets/`, the file name included. |
|
|
61
|
+
|
|
62
|
+
### Manifest and options menu
|
|
63
|
+
|
|
64
|
+
| Limit | Value | Meaning |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| `MIN_WIDGET_ID_BYTES` | 3 bytes | Shortest widget ID. |
|
|
67
|
+
| `MAX_WIDGET_ID_BYTES` | 128 bytes | Longest widget ID; each dot-separated segment is at most 63 bytes. |
|
|
68
|
+
| `MAX_VERSION_BYTES` | 64 bytes | Package version text. |
|
|
69
|
+
| `MAX_WIDGET_NAME_CHARS` | 48 characters | One English or French widget name in the manifest. |
|
|
70
|
+
| `MAX_WIDGET_EDGE_PX` | 4096 px | Width or height of a widget's frame. |
|
|
71
|
+
| `MIN_CONTENT_SCALE` | 500 ‰ | Smallest content scale (50 %). |
|
|
72
|
+
| `MAX_CONTENT_SCALE` | 1750 ‰ | Largest content scale (175 %). |
|
|
73
|
+
| `MAX_GAME_EVENTS` | 32 | Semantic game events declared by one package. |
|
|
74
|
+
| `MAX_MENU_ROWS` | 16 | Widget rows in `wrapper.menu`, group rows and their children included. |
|
|
75
|
+
| `MAX_MENU_DEPTH` | 1 | Submenu levels: a `group` cannot contain another `group`. |
|
|
76
|
+
| `MAX_MENU_LABEL_CHARS` | 80 characters | One English or French label of a row or choice. |
|
|
77
|
+
| `MAX_MENU_ID_BYTES` | 48 bytes | Row ID and choice value. |
|
|
78
|
+
| `MIN_MENU_CHOICES` | 2 | Choices of one `choice` row, lower bound. |
|
|
79
|
+
| `MAX_MENU_CHOICES` | 16 | Choices of one `choice` row, upper bound. |
|
|
80
|
+
| `MAX_MENU_NUMBER` | 1000000 absolute value | Absolute value of a slider bound, step or default. |
|
|
81
|
+
| `MAX_SLIDER_STEPS` | 10000 | `(max - min) / step` of one slider row. |
|
|
82
|
+
|
|
83
|
+
### Network rules
|
|
84
|
+
|
|
85
|
+
| Limit | Value | Meaning |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `MAX_NETWORK_RULES` | 32 | Network rules declared by one package. |
|
|
88
|
+
| `MAX_NETWORK_PATH_BYTES` | 1 KiB | `path` template of one network rule. |
|
|
89
|
+
| `MAX_PATH_PARAMS` | 8 | `pathParams` of one network rule. |
|
|
90
|
+
| `MAX_QUERY_PARAMS` | 16 | `queryParams` of one network rule. |
|
|
91
|
+
| `MAX_ENUM_VALUES` | 32 | Values of one `enum` parameter constraint. |
|
|
92
|
+
| `MAX_ENUM_VALUE_BYTES` | 128 bytes | One `enum` parameter value. |
|
|
93
|
+
| `MAX_PARAMETER_NAME_BYTES` | 32 bytes | Name of one path or query parameter. |
|
|
94
|
+
| `MAX_SLUG_PARAMETER_BYTES` | 128 bytes | Largest `maxLength` of a `slug` parameter constraint. |
|
|
95
|
+
| `MAX_STRING_PARAMETER_BYTES` | 256 bytes | Largest `maxLength` of a `string` query parameter constraint. |
|
|
96
|
+
| `MAX_DNS_LABEL_BYTES` | 63 bytes | One dot-separated label of a network origin host or of a widget ID. |
|
|
97
|
+
| `MAX_DNS_NAME_BYTES` | 253 bytes | Host name of one network origin. |
|
|
98
|
+
| `MAX_REQUEST_URL_BYTES` | 2 KiB | URL text of one request. |
|
|
99
|
+
| `MAX_HTTP_REQUEST_BYTES` | 256 KiB | Body of one outgoing request. |
|
|
100
|
+
| `MAX_HTTP_RESPONSE_BYTES` | 1 MiB | Body of one response delivered to the widget when its network rule declares no `maxResponseBytes`, and of every `as: "image"` response. |
|
|
101
|
+
| `MAX_HTTP_DECLARED_RESPONSE_BYTES` | 3 MiB | Largest `maxResponseBytes` of one network rule: the body of one response the rule allows, delivered to the widget. |
|
|
102
|
+
| `MAX_HTTP_RESPONSE_BYTES_PER_WIDGET` | 4 MiB | Response bytes one widget's requests in flight may reserve, each the bound of its rule; `busy` beyond. Equals `MAX_HTTP_CONCURRENT_PER_WIDGET` × `MAX_HTTP_RESPONSE_BYTES`, so rules without `maxResponseBytes` never reach it. |
|
|
103
|
+
| `MAX_HTTP_RESPONSE_BYTES_GLOBAL` | 64 MiB | Response bytes all widgets' requests in flight may reserve; `busy` beyond. Equals `MAX_HTTP_CONCURRENT_GLOBAL` × `MAX_HTTP_RESPONSE_BYTES`. |
|
|
104
|
+
| `MAX_HTTP_CONCURRENT_PER_WIDGET` | 4 | Requests in flight for one widget. |
|
|
105
|
+
| `MAX_HTTP_CONCURRENT_GLOBAL` | 64 | Requests in flight for all widgets. |
|
|
106
|
+
| `HTTP_TIMEOUT_MS` | 30000 ms | Total duration of one request. |
|
|
107
|
+
|
|
108
|
+
### Listing
|
|
109
|
+
|
|
110
|
+
| Limit | Value | Meaning |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `MAX_LISTING_LOCALIZATIONS` | 16 | Localized texts of one listing. |
|
|
113
|
+
| `MAX_LISTING_NAME_BYTES` | 128 bytes | Marketplace name of one localization. |
|
|
114
|
+
| `MAX_LISTING_DESCRIPTION_BYTES` | 512 bytes | Marketplace description of one localization. |
|
|
115
|
+
| `MAX_AUTHOR_BYTES` | 128 bytes | Author shown in a listing. |
|
|
116
|
+
| `MAX_SPDX_LICENSE_BYTES` | 64 bytes | SPDX license expression of a listing. |
|
|
117
|
+
| `MAX_CATALOG_URL_BYTES` | 2 KiB | Source URL of a listing. |
|
|
118
|
+
| `MAX_PREVIEW_BYTES` | 256 KiB | PNG preview image of a listing. |
|
|
119
|
+
|
|
120
|
+
### View
|
|
121
|
+
|
|
122
|
+
| Limit | Value | Meaning |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `MAX_VIEW_ELEMENTS` | 4096 | Elements written in `view.ocml`, components included. |
|
|
125
|
+
| `MAX_COMPONENTS` | 64 | Local components declared in one view. |
|
|
126
|
+
| `MAX_COMPONENT_PROPS` | 32 | Declared properties of one local component. |
|
|
127
|
+
| `MAX_VIEW_EXPRESSIONS` | 4096 | Compiled expressions of one view, handlers included. |
|
|
128
|
+
| `MAX_EXPRESSION_BYTES` | 1 KiB | Source length of one `{expr}` expression. |
|
|
129
|
+
| `MAX_EXPRESSION_DEPTH` | 32 | Nesting of operators, calls, members and literals in one expression. |
|
|
130
|
+
| `MAX_CALL_ARGUMENTS` | 8 | Arguments of one call, and entries of one object or array literal, in an expression. |
|
|
131
|
+
| `MAX_SCENE_NODES` | 4096 | Elements present in the view of a running widget, list items included. |
|
|
132
|
+
| `MAX_TREE_DEPTH` | 32 | Depth of the view's tree below its root, also the nesting limit of `if`, `for` and components. |
|
|
133
|
+
| `MAX_CHILDREN` | 1024 | Children of one element. |
|
|
134
|
+
| `MAX_NODE_TEXT_BYTES` | 16 KiB | Text content of one `text`, `span`, `badge` or `option` element. |
|
|
135
|
+
| `MAX_ATTRIBUTE_TEXT_BYTES` | 1 KiB | Any text attribute not bounded more tightly. |
|
|
136
|
+
| `MAX_LABEL_BYTES` | 256 bytes | `label`, `tooltip` and `placeholder` attributes. |
|
|
137
|
+
| `MAX_IDENTIFIER_BYTES` | 64 bytes | Class names, component names, field names, `@keyframes` names and similar identifiers. |
|
|
138
|
+
| `MAX_INITIALS_BYTES` | 16 bytes | `initials` fallback of an `avatar`. |
|
|
139
|
+
| `MAX_CLASSES_PER_NODE` | 16 | Classes on one element. |
|
|
140
|
+
| `MAX_FIELD_TEXT_BYTES` | 16 KiB | Content of one `field` or `textarea`, and of one `input` or `change` event. |
|
|
141
|
+
| `MAX_CHART_POINTS` | 1024 | Values in one chart series. |
|
|
142
|
+
| `MAX_CHART_SERIES` | 4 | `series` children of one `chart`. |
|
|
143
|
+
| `MAX_KEY_BYTES` | 32 bytes | `key` of one `keydown` event: one printable character or a named key. |
|
|
144
|
+
|
|
145
|
+
### Style
|
|
146
|
+
|
|
147
|
+
| Limit | Value | Meaning |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| `MAX_STYLE_RULES` | 1024 | Rules in one style sheet, `@keyframes` blocks excluded. |
|
|
150
|
+
| `MAX_SELECTORS_PER_RULE` | 8 | Comma-separated selectors of one rule. |
|
|
151
|
+
| `MAX_COMPOUNDS_PER_SELECTOR` | 4 | Compound selectors joined by combinators in one selector. |
|
|
152
|
+
| `MAX_SIMPLE_SELECTORS_PER_COMPOUND` | 6 | Type, class and state selectors in one compound selector. |
|
|
153
|
+
| `MAX_DECLARATIONS_PER_RULE` | 64 | Declarations in one rule or `@keyframes` stop. |
|
|
154
|
+
| `MAX_KEYFRAMES` | 32 | `@keyframes` blocks in one style sheet. |
|
|
155
|
+
| `MAX_KEYFRAME_STOPS` | 16 | Stops in one `@keyframes` block. |
|
|
156
|
+
| `MAX_SHADOWS` | 2 | Layers in one `box-shadow` value. |
|
|
157
|
+
| `MAX_SHADOW_BLUR_PX` | 64 px | Blur and spread radius of one shadow. |
|
|
158
|
+
| `MAX_GRADIENT_STOPS` | 8 | Colour stops in one `linear-gradient()`. |
|
|
159
|
+
| `MAX_GRID_TRACKS` | 24 | Tracks in one `grid-template-rows` or `grid-template-columns`. |
|
|
160
|
+
| `MAX_GRID_FRACTION` | 1000 absolute value | Largest `<fr>` factor of one grid track. |
|
|
161
|
+
| `MAX_TRANSITIONS` | 8 | Entries in one `transition` or `animation` list. |
|
|
162
|
+
| `MAX_ANIMATION_MS` | 300000 ms | Duration or delay of one transition or animation. |
|
|
163
|
+
| `MAX_ACTIVE_ANIMATIONS` | 64 | Transitions and animations running at once in one widget; extra ones jump to their end state. |
|
|
164
|
+
| `ANIMATION_RATE_HZ` | 60 Hz | Maximum repaint rate while an animation runs; nothing repaints when nothing changes. |
|
|
165
|
+
| `MAX_LENGTH_PX` | 16384 px | Absolute value of any length, offset or coordinate. |
|
|
166
|
+
| `MIN_FONT_SIZE_PX` | 6 px | Smallest `font-size` before content scale. |
|
|
167
|
+
| `MAX_FONT_SIZE_PX` | 96 px | Largest `font-size` before content scale. |
|
|
168
|
+
| `MAX_PERCENT` | 1000 absolute value | Absolute value of any `<percent>` in a style value. |
|
|
169
|
+
| `MAX_ANGLE_DEG` | 3600 absolute value | Absolute value of any `<angle>`, in degrees (ten turns). |
|
|
170
|
+
| `MAX_TRANSFORM_FUNCTIONS` | 4 | Functions in one `transform` value. |
|
|
171
|
+
| `MAX_TRANSFORM_SCALE` | 8 absolute value | Largest `scale()` factor; the smallest is 0. |
|
|
172
|
+
| `MAX_EASING_STEPS` | 60 | Steps of one `steps()` easing. |
|
|
173
|
+
| `MAX_ANIMATION_ITERATIONS` | 10000 | Finite iteration count of one animation; `infinite` stays allowed. |
|
|
174
|
+
|
|
175
|
+
### Drawing and images
|
|
176
|
+
|
|
177
|
+
| Limit | Value | Meaning |
|
|
178
|
+
| --- | --- | --- |
|
|
179
|
+
| `MAX_CANVASES` | 8 | `canvas` elements in one widget. |
|
|
180
|
+
| `MAX_DRAW_COMMANDS` | 4096 | Commands in one call of `draw`, replacing the canvas content. |
|
|
181
|
+
| `MAX_PATH_POINTS` | 16384 | Vertices the host draws for one call of `draw`, all paths included, counted with a fixed ceiling per command: `moveTo` and `lineTo` 1, `quadTo` 8, `cubicTo` 16, `arc` and `circle` 64, `rect` 36, other commands 0. |
|
|
182
|
+
| `MAX_DRAW_STATE_DEPTH` | 16 | Nested `save` commands. |
|
|
183
|
+
| `MAX_IMAGE_EDGE_PX` | 2048 px | Width or height of one decoded image. |
|
|
184
|
+
| `MAX_IMAGE_ENCODED_BYTES` | 2 MiB | Encoded size of one PNG, JPEG or WebP image before decoding. |
|
|
185
|
+
|
|
186
|
+
### Logic
|
|
187
|
+
|
|
188
|
+
| Limit | Value | Meaning |
|
|
189
|
+
| --- | --- | --- |
|
|
190
|
+
| `VM_HEAP_BYTES` | 16 MiB | Heap ceiling of the VM when the manifest requests none. |
|
|
191
|
+
| `VM_MAX_HEAP_BYTES` | 48 MiB | Largest heap ceiling a manifest may request. |
|
|
192
|
+
| `VM_STACK_BYTES` | 256 KiB | Stack ceiling of the VM. |
|
|
193
|
+
| `VM_PROCESS_MEMORY_BYTES` | 64 MiB | Hard memory ceiling of one widget's process. |
|
|
194
|
+
| `VM_CPU_PERCENT` | 25 % of one core | CPU ceiling of one widget's process. |
|
|
195
|
+
| `VM_TURN_BUDGET_MS` | 50 ms | Wall time of one turn of the logic before the VM interrupts it. |
|
|
196
|
+
| `VM_JOBS_PER_TURN` | 1024 | Promise jobs run in one turn; a longer queue is a resource failure. |
|
|
197
|
+
| `VM_MESSAGES_PER_TURN` | 64 | Messages the widget may emit during one turn. |
|
|
198
|
+
| `MAX_VM_MESSAGES_PER_SECOND` | 120 per second | Sustained rate of messages from the widget to the host. |
|
|
199
|
+
| `MAX_VM_MESSAGE_BURST` | 240 | Burst of messages allowed above that rate. |
|
|
200
|
+
| `MAX_PATCH_BYTES` | 256 KiB | Size of what the widget sends at once: one change of the view or one call of `draw`. |
|
|
201
|
+
| `MAX_PATCH_OPS` | 4096 | Operations in one change of the view (elements created, removed, moved or changed) after a turn. |
|
|
202
|
+
| `MAX_TIMERS` | 16 | Active timers of one widget. |
|
|
203
|
+
| `MIN_TIMER_INTERVAL_MS` | 100 ms | Shortest timer period; animation belongs to style, not to timers. |
|
|
204
|
+
| `CLOCK_RESOLUTION_MS` | 1 ms | Granularity of `Date.now()` inside the VM. |
|
|
205
|
+
| `MAX_LOG_BYTES` | 4 KiB | Text of one `log` call. |
|
|
206
|
+
|
|
207
|
+
### Services
|
|
208
|
+
|
|
209
|
+
| Limit | Value | Meaning |
|
|
210
|
+
| --- | --- | --- |
|
|
211
|
+
| `MAX_SERVICE_CALLS_IN_FLIGHT` | 16 | Unanswered service calls of one widget, subscriptions excluded. |
|
|
212
|
+
| `MAX_SUBSCRIPTIONS` | 16 | Open service subscriptions of one widget. |
|
|
213
|
+
| `STORAGE_QUOTA_BYTES` | 256 KiB | Keys and values stored by one widget. |
|
|
214
|
+
| `MAX_STORAGE_KEYS` | 512 | Keys stored by one widget. |
|
|
215
|
+
| `MAX_STORAGE_KEY_BYTES` | 128 bytes | Length of one storage key. |
|
|
216
|
+
| `MAX_STORAGE_VALUE_BYTES` | 64 KiB | Serialized JSON size of one storage value. |
|
|
217
|
+
| `MAX_CLIPBOARD_BYTES` | 16 KiB | Text of one clipboard write. |
|
|
218
|
+
| `MAX_OBJECT_ID_BYTES` | 128 bytes | Opaque object ID or cursor exchanged with a capability service. |
|
|
219
|
+
|
|
220
|
+
### Notes, reviews and chat
|
|
221
|
+
|
|
222
|
+
| Limit | Value | Meaning |
|
|
223
|
+
| --- | --- | --- |
|
|
224
|
+
| `MAX_NOTES` | 8 | Notes in the user's notes document; `notes.create` fails beyond it. |
|
|
225
|
+
| `MAX_NOTE_TITLE_BYTES` | 96 bytes | Note title, non-empty after trimming. |
|
|
226
|
+
| `MAX_NOTE_BODY_BYTES` | 8 KiB | Note body. |
|
|
227
|
+
| `MAX_NOTE_ITEMS` | 64 | Checklist items of one note. |
|
|
228
|
+
| `MAX_NOTE_ITEM_BYTES` | 256 bytes | One checklist item. |
|
|
229
|
+
| `MAX_REVIEW_CHARS` | 2000 characters | PlayerVox review text. |
|
|
230
|
+
| `MAX_CHAT_MESSAGE_CHARS` | 500 characters | Twitch chat message, the Twitch limit; not empty or whitespace-only, no control characters. |
|
|
231
|
+
| `MAX_CHAT_FRAGMENTS` | 16 | Text and emote runs of one delivered chat message; the host merges the rest into text. |
|
|
232
|
+
| `MAX_CHAT_FAVORITES` | 20 | Favourite Twitch channels kept by the host. |
|
|
233
|
+
| `MAX_CHAT_CHANNEL_BYTES` | 25 bytes | Twitch channel login after normalization: ASCII letters, digits and `_`, lowercased. |
|
|
234
|
+
|
|
235
|
+
### Reference images
|
|
236
|
+
|
|
237
|
+
| Limit | Value | Meaning |
|
|
238
|
+
| --- | --- | --- |
|
|
239
|
+
| `PARITY_CHANNEL_TOLERANCE` | 2 levels of 255 | Largest per-channel difference for a pixel to count as equal to the reference image's. |
|
|
240
|
+
| `PARITY_MAX_DIFFERENT_PIXELS` | 100 ppm | Share of pixels allowed beyond the channel tolerance. One wrong 10 px letter in a 360 × 260 widget is 385 ppm. |
|
|
241
|
+
<!-- /generated:limits -->
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# The logic
|
|
2
|
+
|
|
3
|
+
`logic.ts` is the code of a PlayerVox OverCrow widget: it owns the widget's
|
|
4
|
+
state, exports the functions the view calls, and talks to OverCrow through
|
|
5
|
+
`@overcrow/sdk`. It is one TypeScript (or JavaScript) module, bundled with
|
|
6
|
+
the SDK into the package's only script, `logic.js`.
|
|
7
|
+
|
|
8
|
+
<!-- source: templates/counter/logic.ts -->
|
|
9
|
+
```ts
|
|
10
|
+
// A counter. The view reads `state.count` and calls `increment` and
|
|
11
|
+
// `decrement` on activation; after each handler the VM renders the view
|
|
12
|
+
// again and sends only what changed.
|
|
13
|
+
import { initState } from "@overcrow/sdk";
|
|
14
|
+
|
|
15
|
+
declare module "@overcrow/sdk" {
|
|
16
|
+
interface WidgetState {
|
|
17
|
+
count: number;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const state = initState({ count: 0 });
|
|
22
|
+
|
|
23
|
+
export function increment(): void {
|
|
24
|
+
state.count += 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function decrement(): void {
|
|
28
|
+
state.count -= 1;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
That is a complete logic module: a state with one member, and two handlers
|
|
33
|
+
that the view calls with `on:activate={increment}`.
|
|
34
|
+
|
|
35
|
+
## How the logic runs
|
|
36
|
+
|
|
37
|
+
- The module runs once, when the widget starts, in its own JavaScript VM
|
|
38
|
+
inside a sandboxed process. It is not a browser and not Node.js: there is
|
|
39
|
+
no DOM, `window`, `fetch`, `setTimeout`, `console`, `Intl` or
|
|
40
|
+
`require`. The standard ECMAScript objects are there (`Math`, `JSON`,
|
|
41
|
+
`Date`, `Map`, `Promise`…), and the SDK is the only way out.
|
|
42
|
+
- Everything after that happens in **turns**. OverCrow sends an event, a
|
|
43
|
+
timer tick, a service answer or a change of its own data; your code runs;
|
|
44
|
+
then the VM evaluates the view again and sends OverCrow only what
|
|
45
|
+
changed. You never update the view by hand.
|
|
46
|
+
- A turn has a time budget, and the VM a memory ceiling. A widget that
|
|
47
|
+
exceeds one is stopped and restarted by OverCrow, a few times; see
|
|
48
|
+
[what happens on a failure](#what-happens-on-a-failure).
|
|
49
|
+
|
|
50
|
+
The only import a logic module may have is `@overcrow/sdk`. Everything else
|
|
51
|
+
it needs is in the module itself.
|
|
52
|
+
|
|
53
|
+
## The state
|
|
54
|
+
|
|
55
|
+
`state` is a plain object. Declare the type of its members once, by
|
|
56
|
+
extending the SDK's `WidgetState` interface, give it its first value with
|
|
57
|
+
`initState`, and assign to it from then on.
|
|
58
|
+
|
|
59
|
+
<!-- source: templates/list/logic.ts -->
|
|
60
|
+
```ts
|
|
61
|
+
interface Item {
|
|
62
|
+
id: number;
|
|
63
|
+
text: string;
|
|
64
|
+
done: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
declare module "@overcrow/sdk" {
|
|
68
|
+
interface WidgetState {
|
|
69
|
+
items: Item[];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const state = initState({
|
|
74
|
+
items: [
|
|
75
|
+
{ id: 1, text: t("first"), done: false },
|
|
76
|
+
{ id: 2, text: t("second"), done: false },
|
|
77
|
+
{ id: 3, text: t("third"), done: true },
|
|
78
|
+
] as Item[],
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
After each turn the VM evaluates the whole view again and sends the
|
|
83
|
+
differences. There is nothing to subscribe to and no setter to call:
|
|
84
|
+
changing a member in place and replacing it both work.
|
|
85
|
+
|
|
86
|
+
<!-- source: templates/list/logic.ts -->
|
|
87
|
+
```ts
|
|
88
|
+
export function setDone(id: number, done: unknown): void {
|
|
89
|
+
state.items = state.items.map((item) => (item.id === id ? { ...item, done: done === true } : item));
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Keep in the state what the view shows, and in ordinary variables what only
|
|
94
|
+
the logic needs (a timer, a subscription). A value in the state must be
|
|
95
|
+
plain data: numbers, strings, booleans, `null`, arrays and objects of
|
|
96
|
+
those.
|
|
97
|
+
|
|
98
|
+
## Functions for the view
|
|
99
|
+
|
|
100
|
+
The view can call only what the logic module exports by name:
|
|
101
|
+
|
|
102
|
+
- in an **expression**, a function that returns what to show:
|
|
103
|
+
`{remaining(state.items)}`;
|
|
104
|
+
- as a **handler**, a function that changes the state:
|
|
105
|
+
`on:activate={remove(item.id)}`.
|
|
106
|
+
|
|
107
|
+
<!-- source: templates/list/logic.ts -->
|
|
108
|
+
```ts
|
|
109
|
+
export function remove(id: number): void {
|
|
110
|
+
state.items = state.items.filter((item) => item.id !== id);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
<!-- source: templates/list/logic.ts -->
|
|
115
|
+
```ts
|
|
116
|
+
export function remaining(items: readonly Item[]): number {
|
|
117
|
+
return items.filter((item) => !item.done).length;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Expression functions run every time the view is evaluated: keep them cheap
|
|
122
|
+
and without side effects. A handler named without arguments
|
|
123
|
+
(`on:change={setRepeat}`) receives the event's detail:
|
|
124
|
+
|
|
125
|
+
<!-- source: docs/content/examples/countdown/logic.ts -->
|
|
126
|
+
```ts
|
|
127
|
+
export function setRepeat(event: { value: unknown }): void {
|
|
128
|
+
state.repeat = event.value === true;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`export default` is refused: the view calls named exports. `t` needs no
|
|
133
|
+
export: a view that calls `t(…)` gets the SDK's, unless the logic exports
|
|
134
|
+
its own.
|
|
135
|
+
|
|
136
|
+
## Data from OverCrow: `host`
|
|
137
|
+
|
|
138
|
+
`host` holds what OverCrow knows about the user and the widget. Its members
|
|
139
|
+
always read the latest value.
|
|
140
|
+
|
|
141
|
+
| Member | Value |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `host.locale` | Interface language, `"en"` or `"fr"`. |
|
|
144
|
+
| `host.theme` | `"dark"` or `"light"`. |
|
|
145
|
+
| `host.mode` | `"passive"` or `"interactive"`: the widget receives input only in Interactive mode. |
|
|
146
|
+
| `host.visible` | Whether the widget is shown. |
|
|
147
|
+
| `host.scale` | Content scale in thousandths: `1000` is 100 %. |
|
|
148
|
+
| `host.viewport` | `{ width, height }` of the widget's content, in logical pixels. |
|
|
149
|
+
| `host.region` | The user's regional formats and UTC offset; the formatting helpers read it. |
|
|
150
|
+
| `host.options` | Values of the options menu; read them with `option`. |
|
|
151
|
+
| `host.grants` | The capabilities the user granted; read it with `hasGrant`. |
|
|
152
|
+
| `host.messages` | The messages of the active language; read them with `t`. |
|
|
153
|
+
|
|
154
|
+
`onHost(listener)` calls your listener when OverCrow sends new values, with
|
|
155
|
+
the names of the members that changed, before the view is evaluated again.
|
|
156
|
+
Use it when a change needs more than showing the new value: re-arm a timer,
|
|
157
|
+
subscribe again, redraw a canvas. A member may be sent again with the same
|
|
158
|
+
value: compare it with the one you kept when that matters.
|
|
159
|
+
|
|
160
|
+
<!-- source: widgets/stopwatch/logic.ts -->
|
|
161
|
+
```ts
|
|
162
|
+
onHost((changed) => {
|
|
163
|
+
if (changed.includes("mode")) {
|
|
164
|
+
state.interactive = host.mode === "interactive";
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Most widgets only show the buttons that work: in Passive mode a click goes
|
|
170
|
+
through the widget to the game, so the stopwatch hides its buttons unless
|
|
171
|
+
`host.mode` is `"interactive"`.
|
|
172
|
+
|
|
173
|
+
## The options menu
|
|
174
|
+
|
|
175
|
+
The rows declared in `wrapper.menu` of the [manifest](manifest.md#the-options-menu)
|
|
176
|
+
are drawn and stored by OverCrow. The logic reads them:
|
|
177
|
+
|
|
178
|
+
- `option(id, fallback)` returns the stored value of a toggle, slider or
|
|
179
|
+
choice row, or `fallback` when there is none or it has another type;
|
|
180
|
+
- `onHost` reports `"options"` when the user changes a row;
|
|
181
|
+
- `onMenu(handler)` receives the ID of an `action` row when the user
|
|
182
|
+
chooses it.
|
|
183
|
+
|
|
184
|
+
<!-- source: widgets/clock/logic.ts -->
|
|
185
|
+
```ts
|
|
186
|
+
export function menuValues(): { seconds: boolean; showDate: boolean; order: DateOrder } {
|
|
187
|
+
return {
|
|
188
|
+
seconds: option("show-seconds", false),
|
|
189
|
+
showDate: option("show-date", true),
|
|
190
|
+
order: DATE_ORDERS[option("date-format", "day-month-year")] ?? "dmy",
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
<!-- source: docs/content/examples/countdown/logic.ts -->
|
|
196
|
+
```ts
|
|
197
|
+
onMenu((row) => {
|
|
198
|
+
if (row === "reset-rounds") {
|
|
199
|
+
resetRounds();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Choosing a menu row is not a
|
|
205
|
+
[user action](services.md#calls-that-need-a-user-action) for a protected
|
|
206
|
+
service: an `action` row can change the state, not write to the clipboard.
|
|
207
|
+
|
|
208
|
+
## Timers
|
|
209
|
+
|
|
210
|
+
There is no `setTimeout`. Timers are OverCrow's, through `timers`:
|
|
211
|
+
|
|
212
|
+
| Function | Calls back |
|
|
213
|
+
| --- | --- |
|
|
214
|
+
| `timers.after(ms, callback)` | once, after `ms` |
|
|
215
|
+
| `timers.every(ms, callback)` | every `ms` |
|
|
216
|
+
| `timers.atEach(unit, callback)` | at each `"second"`, `"minute"`, `"hour"` or `"day"` of the user's local time |
|
|
217
|
+
|
|
218
|
+
Each returns a `Timer` with `cancel()`.
|
|
219
|
+
|
|
220
|
+
<!-- source: templates/chart/logic.ts -->
|
|
221
|
+
```ts
|
|
222
|
+
timers.every(1000, () => {
|
|
223
|
+
const previous = state.values.at(-1) ?? 50;
|
|
224
|
+
state.values = [...state.values.slice(1 - POINTS), sample(previous)];
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Three rules follow from OverCrow owning the timers:
|
|
229
|
+
|
|
230
|
+
- **No timer shorter than 100 ms.** A shorter interval is raised to it.
|
|
231
|
+
Motion belongs to the [style](style.md#transitions-and-animations), not to
|
|
232
|
+
timers.
|
|
233
|
+
- **A hidden widget does not tick.** A repeating timer skips the ticks that
|
|
234
|
+
fell while the widget was hidden; a one-shot timer that fell due while
|
|
235
|
+
hidden ticks once when the widget is shown again. Compute elapsed time
|
|
236
|
+
from `Date.now()`, never from a count of ticks:
|
|
237
|
+
|
|
238
|
+
<!-- source: docs/content/examples/countdown/logic.ts -->
|
|
239
|
+
```ts
|
|
240
|
+
// A timer does not tick while the widget is hidden: the time left comes
|
|
241
|
+
// from the clock, not from the number of ticks.
|
|
242
|
+
function tick(): void {
|
|
243
|
+
state.remainingMs = Math.max(0, endsAt - Date.now());
|
|
244
|
+
if (state.remainingMs > 0) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
stop();
|
|
248
|
+
setRounds(state.rounds + 1);
|
|
249
|
+
log.info(`round ${state.rounds} finished`);
|
|
250
|
+
if (state.repeat) {
|
|
251
|
+
start();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
- **At most 16 timers** at once.
|
|
257
|
+
|
|
258
|
+
For a clock, `timers.atEach` is the right tool: one repeating timer aligned
|
|
259
|
+
on the boundaries of local time, which also wakes when the UTC offset
|
|
260
|
+
changes (summer time) and once when the widget is shown again.
|
|
261
|
+
|
|
262
|
+
<!-- source: widgets/clock/logic.ts -->
|
|
263
|
+
```ts
|
|
264
|
+
/** Ticks at each second or minute of local time, and shows the time now. */
|
|
265
|
+
function start(): void {
|
|
266
|
+
clock?.cancel();
|
|
267
|
+
clock = timers.atEach(state.seconds ? "second" : "minute", tick);
|
|
268
|
+
tick();
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
A duration that only has to advance on screen needs no timer at all: the
|
|
273
|
+
`elapsed` element is advanced by OverCrow from a starting point the logic
|
|
274
|
+
gives it. See the [`elapsed` element](elements.md#elapsed).
|
|
275
|
+
|
|
276
|
+
## Time, dates and numbers
|
|
277
|
+
|
|
278
|
+
Inside the VM, local time is UTC: `Date` never gives the user's time, and
|
|
279
|
+
there is no `Intl`. The SDK formats instead, from `host.region`:
|
|
280
|
+
|
|
281
|
+
| Helper | Gives |
|
|
282
|
+
| --- | --- |
|
|
283
|
+
| `formatTime(ms, { seconds })` | `14:08`, or `14:08:42`: 24-hour, in the user's time zone |
|
|
284
|
+
| `formatDate(ms, { order })` | `17/07/2026`, `07/17/2026` or `2026-07-17`, in the user's date order by default |
|
|
285
|
+
| `formatNumber(value, options)` | `1,234.5`, `1 234,5` or `1.234,5`: the user's separators, whatever the interface language |
|
|
286
|
+
| `formatDuration(ms, options)` | `04:05`, `1:02:03`, or `04:05.67` with hundredths |
|
|
287
|
+
| `localTime(ms)` | the fields of the instant in the user's time zone: `year`, `month`, `day`, `weekday`, `hour`… |
|
|
288
|
+
|
|
289
|
+
<!-- source: widgets/clock/logic.ts -->
|
|
290
|
+
```ts
|
|
291
|
+
export function clockTime(now: number, seconds: boolean): string {
|
|
292
|
+
return formatTime(now, { seconds });
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
<!-- source: templates/chart/logic.ts -->
|
|
297
|
+
```ts
|
|
298
|
+
export function latest(values: readonly number[]): string {
|
|
299
|
+
const last = values.at(-1);
|
|
300
|
+
return last === undefined ? "" : formatNumber(last, { maximumFractionDigits: 0 });
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
A timestamp that comes from a service carries its own `offsetMinutes`: pass
|
|
305
|
+
it to the helper (`formatTime(at, { offsetMinutes })`), so that a session
|
|
306
|
+
recorded in another time zone shows the time it had there. Month and
|
|
307
|
+
weekday names and 12-hour clocks are not provided.
|
|
308
|
+
|
|
309
|
+
## Messages
|
|
310
|
+
|
|
311
|
+
Put the texts of the widget in `locales/en.json` and `locales/fr.json`:
|
|
312
|
+
two flat objects of strings with the same keys. Both files or neither.
|
|
313
|
+
|
|
314
|
+
<!-- source: templates/list/locales/en.json -->
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"first": "Warm up",
|
|
318
|
+
"remaining": "{count} left",
|
|
319
|
+
"remove": "Remove",
|
|
320
|
+
"second": "Play three matches",
|
|
321
|
+
"third": "Update the drivers",
|
|
322
|
+
"title": "Today"
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`t(key, params)` returns the message of the user's language and replaces
|
|
327
|
+
each `{name}` by `params.name`; a missing key gives the key itself. It
|
|
328
|
+
works in the view and in the logic alike:
|
|
329
|
+
|
|
330
|
+
<!-- source: templates/list/view.ocml -->
|
|
331
|
+
```xml
|
|
332
|
+
<text class="summary">{t("remaining", { count: remaining(state.items) })}</text>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
<!-- source: widgets/stopwatch/logic.ts -->
|
|
336
|
+
```ts
|
|
337
|
+
export function toggleLabel(running: boolean): string {
|
|
338
|
+
return t(running ? "pause" : "start");
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
When the user changes OverCrow's language, the view is evaluated again
|
|
343
|
+
with the new messages. A text that the logic computed and stored in the
|
|
344
|
+
state is not: compute texts in functions the view calls, or refresh them in
|
|
345
|
+
`onHost` when `"locale"` changes. The widget's name and its menu labels are
|
|
346
|
+
in the manifest, not in the locales.
|
|
347
|
+
|
|
348
|
+
`overcrow-widget check` warns when the view calls `t("key")` with a key
|
|
349
|
+
that has no message, and refuses two files whose keys differ.
|
|
350
|
+
|
|
351
|
+
## Drawing
|
|
352
|
+
|
|
353
|
+
For what the elements cannot show, a `canvas` element is a surface that
|
|
354
|
+
the logic paints with a list of commands: paths, fills, strokes, text and
|
|
355
|
+
images. `draw(ref, commands)` replaces the whole content of the canvas
|
|
356
|
+
named `ref`.
|
|
357
|
+
|
|
358
|
+
<!-- source: docs/content/examples/countdown/logic.ts -->
|
|
359
|
+
```ts
|
|
360
|
+
/** One dot per finished round, on the `dots` canvas. */
|
|
361
|
+
function paint(): void {
|
|
362
|
+
const dots: DrawCommand[] = [];
|
|
363
|
+
for (let index = 0; index < Math.min(state.rounds, MAX_DOTS); index += 1) {
|
|
364
|
+
dots.push(["circle", 6 + index * 14, 6, 4], ["fill", "var(--color-accent)"]);
|
|
365
|
+
}
|
|
366
|
+
draw("dots", dots);
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Coordinates are logical pixels from the top-left corner of the canvas;
|
|
371
|
+
colours are literal (`#rrggbb`) or colour tokens. OverCrow checks every
|
|
372
|
+
command and draws them itself: there is no pixel access and no shader.
|
|
373
|
+
Draw again when what you show changes, and when `host.theme` changes if you
|
|
374
|
+
use literal colours.
|
|
375
|
+
|
|
376
|
+
<!-- generated:draw-commands -->
|
|
377
|
+
| Command | Arguments | Meaning |
|
|
378
|
+
| --- | --- | --- |
|
|
379
|
+
| `moveTo` | `x`: number -16384 to 16384; `y`: number -16384 to 16384 | Starts a subpath. |
|
|
380
|
+
| `lineTo` | `x`: number -16384 to 16384; `y`: number -16384 to 16384 | Straight segment. |
|
|
381
|
+
| `quadTo` | `cx`: number -16384 to 16384; `cy`: number -16384 to 16384; `x`: number -16384 to 16384; `y`: number -16384 to 16384 | Quadratic Bézier segment. |
|
|
382
|
+
| `cubicTo` | `c1x`: number -16384 to 16384; `c1y`: number -16384 to 16384; `c2x`: number -16384 to 16384; `c2y`: number -16384 to 16384; `x`: number -16384 to 16384; `y`: number -16384 to 16384 | Cubic Bézier segment. |
|
|
383
|
+
| `arc` | `cx`: number -16384 to 16384; `cy`: number -16384 to 16384; `r`: number -16384 to 16384; `start`: number -720 to 720; `end`: number -720 to 720 | Circular arc in degrees, clockwise. |
|
|
384
|
+
| `rect` | `x`: number -16384 to 16384; `y`: number -16384 to 16384; `w`: number -16384 to 16384; `h`: number -16384 to 16384; `radius`: number -16384 to 16384 | Closed rounded-rectangle subpath. |
|
|
385
|
+
| `circle` | `cx`: number -16384 to 16384; `cy`: number -16384 to 16384; `r`: number -16384 to 16384 | Closed circle subpath. |
|
|
386
|
+
| `close` | none | Closes the subpath. |
|
|
387
|
+
| `fill` | `color`: `<color>` | Fills and clears the current path. |
|
|
388
|
+
| `stroke` | `color`: `<color>`; `width`: number 0 to 256 | Strokes and clears the current path. |
|
|
389
|
+
| `text` | `x`: number -16384 to 16384; `y`: number -16384 to 16384; `text`: text ≤ 1 KiB; `size`: number 6 to 96; `color`: `<color>`; `align`: `start` \| `center` \| `end`; `family`: `ui` \| `mono` \| `display`; `weight`: `400` \| `500` \| `600` \| `700` | One line of text on its baseline. |
|
|
390
|
+
| `image` | `src`: image source; `x`: number -16384 to 16384; `y`: number -16384 to 16384; `w`: number -16384 to 16384; `h`: number -16384 to 16384 | Draws an image scaled into the rectangle. |
|
|
391
|
+
| `save` | none | Pushes transform, clip and alpha, ≤ 16. |
|
|
392
|
+
| `restore` | none | Pops the state; a `restore` without a `save` is an error. |
|
|
393
|
+
| `clip` | `x`: number -16384 to 16384; `y`: number -16384 to 16384; `w`: number -16384 to 16384; `h`: number -16384 to 16384 | Intersects the clip rectangle with this one. |
|
|
394
|
+
| `translate` | `x`: number -16384 to 16384; `y`: number -16384 to 16384 | Translates. |
|
|
395
|
+
| `rotate` | `degrees`: number -360 to 360 | Rotates. |
|
|
396
|
+
| `scale` | `x`: number 0 to 8; `y`: number 0 to 8 | Scales. |
|
|
397
|
+
| `alpha` | `value`: number 0 to 1 | Multiplies opacity. |
|
|
398
|
+
<!-- /generated:draw-commands -->
|
|
399
|
+
|
|
400
|
+
A chart, a gauge or a progress bar needs no canvas: the `chart`, `gauge`
|
|
401
|
+
and `progress` elements draw them from values.
|
|
402
|
+
|
|
403
|
+
## Logging
|
|
404
|
+
|
|
405
|
+
`log.debug`, `log.info`, `log.warn` and `log.error` take one text. They
|
|
406
|
+
appear in the terminal of `overcrow-widget dev` and nowhere else: an
|
|
407
|
+
installed widget's logs are dropped. Never log user data.
|
|
408
|
+
|
|
409
|
+
<!-- source: docs/content/examples/countdown/logic.ts -->
|
|
410
|
+
```ts
|
|
411
|
+
log.info(`round ${state.rounds} finished`);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## What the logic cannot use
|
|
415
|
+
|
|
416
|
+
`overcrow-widget check` lints the logic against what the VM runs and names
|
|
417
|
+
the SDK replacement:
|
|
418
|
+
|
|
419
|
+
| Code | Rule |
|
|
420
|
+
| --- | --- |
|
|
421
|
+
| `logic.syntax` | The module parses as ES2023; TypeScript types are allowed. |
|
|
422
|
+
| `logic.import` | Its only import is `@overcrow/sdk`: no other module, file or package, no `export *`. |
|
|
423
|
+
| `logic.dynamic_import`, `logic.import_meta` | No `import()` and no `import.meta`. |
|
|
424
|
+
| `logic.eval`, `logic.function_constructor` | No `eval`, no `Function(…)` or `new Function(…)`. |
|
|
425
|
+
| `logic.unavailable_global` | Every global it reads exists in the VM. `Intl`, `setTimeout`, `console`, `fetch`, `WebAssembly`, `SharedArrayBuffer`, `performance`, `WeakRef`, browser and Node.js globals do not. |
|
|
426
|
+
| `logic.top_level_await`, `logic.top_level_this` | No top-level `await` or `for await`, no top-level `this`. |
|
|
427
|
+
| `logic.syntax_version` | Nothing newer than ES2023: decorators, `using`, the regular expression `v` flag, import attributes. |
|
|
428
|
+
| `logic.default_export` | The view calls named exports; `export default` is refused. |
|
|
429
|
+
| `logic.missing_export` | Every function the view calls is exported. |
|
|
430
|
+
| `logic.debugger` | `debugger` is a warning. |
|
|
431
|
+
|
|
432
|
+
The lint helps you; it is not what protects the user. The sandbox, the
|
|
433
|
+
VM's budgets and OverCrow's check of everything the widget sends do that.
|
|
434
|
+
|
|
435
|
+
## What happens on a failure
|
|
436
|
+
|
|
437
|
+
- An exception in a handler ends that turn; the widget keeps running, and
|
|
438
|
+
`overcrow-widget dev` shows the fault.
|
|
439
|
+
- A turn longer than its budget (50 ms), a heap over its ceiling or too
|
|
440
|
+
many messages in a turn stop the widget. OverCrow shows a fixed error in
|
|
441
|
+
its frame and restarts it after 1, 5 and 15 seconds, at most three times.
|
|
442
|
+
- A rejected promise of a service call is an ordinary error of your code:
|
|
443
|
+
catch it. See [errors](services.md#errors).
|
|
444
|
+
|
|
445
|
+
The numbers are in [limits](limits.md#logic). The whole API of the SDK is
|
|
446
|
+
in the [SDK reference](sdk.md).
|