@overcrow/mcp 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +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 +50 -4
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# Forms and write intents
|
|
2
|
+
|
|
3
|
+
A PlayerVox OverCrow widget takes input through controls that OverCrow
|
|
4
|
+
draws and edits itself: buttons, toggles, checkboxes, sliders, drop-down
|
|
5
|
+
lists, text fields. The logic hears about them through events. A `form`
|
|
6
|
+
groups controls and submits them together, and a form bound to a **write
|
|
7
|
+
intent** is how a widget lets the user write to their notes, publish a
|
|
8
|
+
rating or send a chat message, without the text ever passing through the
|
|
9
|
+
widget's code.
|
|
10
|
+
|
|
11
|
+
Widgets receive input only in OverCrow's Interactive mode. In Passive mode
|
|
12
|
+
a click goes through the widget to the game: hide or disable what cannot be
|
|
13
|
+
used, as the built-ins do with `host.mode`.
|
|
14
|
+
|
|
15
|
+
## Controls
|
|
16
|
+
|
|
17
|
+
| Element | Value | Events |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `button` | none | `activate` |
|
|
20
|
+
| `toggle`, `checkbox` | `checked` | `change` |
|
|
21
|
+
| `slider` | `value`, between `min` and `max` | `input` while dragging, `change` on release |
|
|
22
|
+
| `select` with `option` children | `value` of the chosen option | `change` |
|
|
23
|
+
| `field` | one line of text | `input` at each edit, `change` on Enter or when the focus leaves, `submit` on Enter |
|
|
24
|
+
| `textarea` | several lines of text | `input`, `change`, `submit` on Ctrl+Enter |
|
|
25
|
+
|
|
26
|
+
Each takes a `label`, its accessible name, and `disabled`. Their attributes
|
|
27
|
+
are in [elements and attributes](elements.md).
|
|
28
|
+
|
|
29
|
+
<!-- source: docs/content/examples/countdown/view.ocml -->
|
|
30
|
+
```xml
|
|
31
|
+
<select class="duration" value={minutesValue(state.minutes)} label={t("duration")} disabled={state.running} on:change={setMinutes}>
|
|
32
|
+
<for each={state.durations} as="minutes" key={minutes}>
|
|
33
|
+
<option value={minutesValue(minutes)}>{t("minutes", { count: minutes })}</option>
|
|
34
|
+
</for>
|
|
35
|
+
</select>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
<!-- source: docs/content/examples/countdown/logic.ts -->
|
|
39
|
+
```ts
|
|
40
|
+
export function setMinutes(event: { value: unknown }): void {
|
|
41
|
+
const minutes = Number(event.value);
|
|
42
|
+
if (state.durations.includes(minutes)) {
|
|
43
|
+
state.minutes = minutes;
|
|
44
|
+
state.remainingMs = total(minutes);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The view binds the control to the state (`value={…}`, `checked={…}`), and
|
|
50
|
+
the handler writes the new value back. The detail of `input` and `change`
|
|
51
|
+
is `{ value }`: a boolean for a toggle or a checkbox, a number for a
|
|
52
|
+
slider, text for the others.
|
|
53
|
+
|
|
54
|
+
<!-- source: docs/content/examples/countdown/view.ocml -->
|
|
55
|
+
```xml
|
|
56
|
+
<toggle class="repeat" label={t("repeat")} tooltip={t("repeat")} checked={state.repeat} on:change={setRepeat}/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Text fields
|
|
60
|
+
|
|
61
|
+
OverCrow edits the text of a `field` or a `textarea` itself: the caret, the
|
|
62
|
+
selection, copy and paste, and input methods for languages that compose
|
|
63
|
+
characters. The logic receives a copy of the text in each `input` event.
|
|
64
|
+
|
|
65
|
+
<!-- source: widgets/warframe-market/view.ocml -->
|
|
66
|
+
```xml
|
|
67
|
+
<field class="query" label={t("search-field")} value={state.query} placeholder={t("placeholder")} max-length="256" on:input={search}/>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
<!-- source: widgets/warframe-market/logic.ts -->
|
|
71
|
+
```ts
|
|
72
|
+
export function search(detail: { value: unknown }): void {
|
|
73
|
+
state.query = typeof detail.value === "string" ? detail.value : "";
|
|
74
|
+
state.results = searchItems(items, lowered, state.query);
|
|
75
|
+
state.detail = null;
|
|
76
|
+
state.loadingOrders = false;
|
|
77
|
+
state.refreshing = false;
|
|
78
|
+
state.copy = null;
|
|
79
|
+
if (state.error === "orders_unavailable") {
|
|
80
|
+
state.error = null;
|
|
81
|
+
}
|
|
82
|
+
selection += 1;
|
|
83
|
+
stopRefresh();
|
|
84
|
+
saveQuery();
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Binding `value` replaces what the field holds: bind it to the state your
|
|
89
|
+
`input` handler writes, as above, or leave it out and only listen.
|
|
90
|
+
`max-length` bounds the text in bytes, and `placeholder` shows a hint while
|
|
91
|
+
the field is empty.
|
|
92
|
+
|
|
93
|
+
A field also sends `keydown` when you ask for it, with the key and its
|
|
94
|
+
modifiers, for shortcuts such as Escape to cancel. Keys that OverCrow
|
|
95
|
+
reserves (Tab to move the focus, its own shortcuts, a composition in
|
|
96
|
+
progress) are not delivered.
|
|
97
|
+
|
|
98
|
+
### The focus when an editor opens
|
|
99
|
+
|
|
100
|
+
The logic cannot move the keyboard focus. A `field` or a `textarea` that
|
|
101
|
+
carries `autofocus` takes it by itself when it appears while the logic
|
|
102
|
+
handles a user action: a click on an Edit button that opens an editor puts
|
|
103
|
+
the caret in the field, after its text, as a click in it would.
|
|
104
|
+
|
|
105
|
+
<!-- source: widgets/notes/view.ocml -->
|
|
106
|
+
```xml
|
|
107
|
+
<field class="title-field" name="title" autofocus placeholder={t("title-hint")} label={t("title-hint")} on:input={titleInput(editor.key, event.value)} on:keydown={keyed(editor.key, event.key, event.ctrl)}/>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
OverCrow gives that focus only when the element appears during a
|
|
111
|
+
[user action](services.md#calls-that-need-a-user-action), in Interactive
|
|
112
|
+
mode, in the widget the user acted on; when several such elements appear
|
|
113
|
+
together, the first one of the view takes it. An element that appears when
|
|
114
|
+
the widget starts, from a timer or from the answer of a service (after an
|
|
115
|
+
`await` in the handler of the click too) does not take the focus, and
|
|
116
|
+
neither does an element that was already shown: `autofocus` is read when
|
|
117
|
+
the element appears. Give it to one field of an editor, and not to a field
|
|
118
|
+
that appears while the user types, such as a new row of a list.
|
|
119
|
+
|
|
120
|
+
## Forms
|
|
121
|
+
|
|
122
|
+
A `form` groups controls and gives them one `submit` event. It is
|
|
123
|
+
submitted by Enter in a `field`, by Ctrl+Enter in a `field` or a
|
|
124
|
+
`textarea`, or by activating a button with the `submit` attribute.
|
|
125
|
+
|
|
126
|
+
<!-- source: widgets/twitch-chat/view.ocml -->
|
|
127
|
+
```xml
|
|
128
|
+
<form class="selector" on:submit={join}>
|
|
129
|
+
<field class="input channel" max-length="26" autofocus placeholder={t("channel-name")} label={t("channel-name")} on:input={channelTyped(event.value)}/>
|
|
130
|
+
<button class="action" submit disabled={!canJoin(state)}>
|
|
131
|
+
<text class="action-label">{t("join")}</text>
|
|
132
|
+
</button>
|
|
133
|
+
</form>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
<!-- source: widgets/twitch-chat/logic.ts -->
|
|
137
|
+
```ts
|
|
138
|
+
export function join(): void {
|
|
139
|
+
const channel = normalizeChannel(state.channelDraft);
|
|
140
|
+
if (channel !== null) {
|
|
141
|
+
joinChannel(channel);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This form has no intent: its `submit` handler reads what the `input`
|
|
147
|
+
handler kept in the state, and calls a service. `submit` is a user action,
|
|
148
|
+
so the handler may call a service that needs one
|
|
149
|
+
(`twitch.chat.join` here).
|
|
150
|
+
|
|
151
|
+
A form keeps what the user entered for as long as it stays in the view.
|
|
152
|
+
Hide it with `display: none` to keep a draft; remove it from the view to
|
|
153
|
+
discard one. To start over with empty fields, give the form a new key in a
|
|
154
|
+
`for`: a new key is a new form.
|
|
155
|
+
|
|
156
|
+
## Forms that write user data
|
|
157
|
+
|
|
158
|
+
Three things a widget can do write text that the user typed into the
|
|
159
|
+
user's own data: saving a note, publishing a PlayerVox rating with its
|
|
160
|
+
review, and sending a Twitch chat message. They are not service calls. A
|
|
161
|
+
form with an `intent` attribute sends them:
|
|
162
|
+
|
|
163
|
+
<!-- source: widgets/twitch-chat/view.ocml -->
|
|
164
|
+
```xml
|
|
165
|
+
<form class={composerClass(state)} intent="twitch.chat.send" target={replyTarget(state)} on:submit={submitted(event.outcome, event.error)}>
|
|
166
|
+
<if test={state.reply}>
|
|
167
|
+
<box class="replying">
|
|
168
|
+
<text class="replying-label">{replyingTo(state)}</text>
|
|
169
|
+
<button class="icon-button small" label={t("cancel-reply")} tooltip={t("cancel-reply")} on:activate={cancelReply}>
|
|
170
|
+
<icon name="x"/>
|
|
171
|
+
</button>
|
|
172
|
+
</box>
|
|
173
|
+
</if>
|
|
174
|
+
<if test={state.sendError != ""}>
|
|
175
|
+
<text class="error">{t(state.sendError)}</text>
|
|
176
|
+
</if>
|
|
177
|
+
<box class="compose">
|
|
178
|
+
<field class="input message-field" name="message" max-length="500" placeholder={t(composerHint(state))} label={t("message-label")} disabled={!state.canSend} on:input={drafted(event.value)} on:keydown={keyed(event.key)}/>
|
|
179
|
+
<if test={sendMark(state) != ""}>
|
|
180
|
+
<text class={sendMarkClass(state)}>{t(sendMark(state))}</text>
|
|
181
|
+
</if>
|
|
182
|
+
<button class="action" submit disabled={!canSubmit(state)} on:activate={sendPressed}>
|
|
183
|
+
<text class="action-label">{t("send")}</text>
|
|
184
|
+
</button>
|
|
185
|
+
</box>
|
|
186
|
+
</form>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
When the user submits such a form, **OverCrow reads the values of the
|
|
190
|
+
form's own controls and sends them itself**. The widget's logic never
|
|
191
|
+
supplies the text, so a widget cannot write something the user did not
|
|
192
|
+
type, and cannot submit without the user: the form goes only on a user
|
|
193
|
+
action, in Interactive mode, with the capability granted.
|
|
194
|
+
|
|
195
|
+
What this changes for the view and the logic:
|
|
196
|
+
|
|
197
|
+
- **The controls belong to OverCrow.** The `field`, `textarea` and `slider`
|
|
198
|
+
controls of the form take no `value`: `overcrow-widget check` refuses it
|
|
199
|
+
(`view.bound_value`), and OverCrow stops a widget that sets one at run
|
|
200
|
+
time. Their `name` attribute says which field of the intent each one is.
|
|
201
|
+
- **OverCrow fills them** where the intent says so: the note's title, body
|
|
202
|
+
and checklist, the user's published rating. A fill sends the control's
|
|
203
|
+
`input` event with the value, so the logic knows what is shown; that
|
|
204
|
+
event is not a user action.
|
|
205
|
+
- **The logic follows through `input` events**, to show a counter or to
|
|
206
|
+
enable the button, and may disable the controls.
|
|
207
|
+
- **The outcome arrives in the form's `submit` event**: `event.outcome` is
|
|
208
|
+
`accepted`, `rejected` (with the [error code](services.md#errors) in
|
|
209
|
+
`event.error`) or `cancelled`. A form without an intent gives
|
|
210
|
+
`submitted`.
|
|
211
|
+
|
|
212
|
+
<!-- source: widgets/playervox-rating/view.ocml -->
|
|
213
|
+
```xml
|
|
214
|
+
<slider class="criterion-slider" name="gameplay" min="0" max="100" step="1" label={t("gameplay")} disabled={!editable(state)} on:input={scored("gameplay", event.value)}/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
<!-- source: widgets/playervox-rating/logic.ts -->
|
|
218
|
+
```ts
|
|
219
|
+
export function scored(key: CriterionKey, value: number): void {
|
|
220
|
+
state[key] = value;
|
|
221
|
+
edited();
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
<!-- source: widgets/playervox-rating/logic.ts -->
|
|
226
|
+
```ts
|
|
227
|
+
export function submitted(outcome: string, error: ServiceErrorPayload | undefined): void {
|
|
228
|
+
state.pending = Math.max(0, state.pending - 1);
|
|
229
|
+
if (state.pending === 0) {
|
|
230
|
+
answer?.cancel();
|
|
231
|
+
answer = null;
|
|
232
|
+
}
|
|
233
|
+
if (outcome === "accepted") {
|
|
234
|
+
// The subscription sends the stored rating; nothing is read again.
|
|
235
|
+
state.saved = true;
|
|
236
|
+
state.error = "";
|
|
237
|
+
} else if (outcome !== "cancelled") {
|
|
238
|
+
state.error = errorKey(error?.code ?? null);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
A write does not change what a subscription says by itself: after an
|
|
244
|
+
accepted rating, `playervox.rating.subscribe` delivers the stored rating,
|
|
245
|
+
and the widget shows that.
|
|
246
|
+
|
|
247
|
+
### The intents
|
|
248
|
+
|
|
249
|
+
<!-- generated:write-intents -->
|
|
250
|
+
### `notes.save`
|
|
251
|
+
|
|
252
|
+
Capability `notes.write`; `target` attribute required (note ID; the host fills the bound controls from that note). Enter in a `field` of the form moves the focus to its next `field` or `textarea` and does not submit; Ctrl+Enter and a `submit` button do.
|
|
253
|
+
|
|
254
|
+
| Field | Type | Required | Meaning |
|
|
255
|
+
| --- | --- | --- | --- |
|
|
256
|
+
| `title` | text ≤ 96 bytes | yes | `field`; saved trimmed, and not empty. |
|
|
257
|
+
| `body` | text ≤ 8 KiB | no | `textarea`; saved as written. |
|
|
258
|
+
| `item` | list of `NoteItem` ≤ 64 | no | One `field` per checklist row, in order, each ≤ 256 bytes. The host remembers which item it filled each field with: a row keeps that item's ID and check whichever rows the widget removes or moves, and a field that appeared since is a new, unchecked item. Rows are saved trimmed; an empty row is dropped. A note saved as it is stored is accepted without a write. |
|
|
259
|
+
|
|
260
|
+
### `playervox.rating.publish`
|
|
261
|
+
|
|
262
|
+
Capability `playervox.rating.write`; `target` attribute refused.
|
|
263
|
+
|
|
264
|
+
| Field | Type | Required | Meaning |
|
|
265
|
+
| --- | --- | --- | --- |
|
|
266
|
+
| `gameplay` | integer 0 to 100 | yes | `slider`; filled from the user's rating, 50 before the first one. |
|
|
267
|
+
| `art` | integer 0 to 100 | yes | `slider`; filled like `gameplay`. |
|
|
268
|
+
| `tech` | integer 0 to 100 | yes | `slider`; filled like `gameplay`. |
|
|
269
|
+
| `review` | text ≤ 2000 characters | no | `textarea`; filled from the published review. An unchanged review is not sent again; an emptied one removes it. |
|
|
270
|
+
|
|
271
|
+
### `twitch.chat.send`
|
|
272
|
+
|
|
273
|
+
Capability `twitch.chat.compose`; `target` attribute optional (ID of the message replied to).
|
|
274
|
+
|
|
275
|
+
| Field | Type | Required | Meaning |
|
|
276
|
+
| --- | --- | --- | --- |
|
|
277
|
+
| `message` | text ≤ 500 characters | yes | `field`; the host clears it after a successful send. |
|
|
278
|
+
<!-- /generated:write-intents -->
|
|
279
|
+
|
|
280
|
+
`target` is the form's attribute that names the object written: the note's
|
|
281
|
+
ID for `notes.save`, the ID of the message replied to for
|
|
282
|
+
`twitch.chat.send`.
|
|
283
|
+
|
|
284
|
+
### Several rows of one field
|
|
285
|
+
|
|
286
|
+
The `notes.save` intent takes one `item` field per checklist row. The
|
|
287
|
+
logic decides which rows the form has, with a `for`; OverCrow fills each
|
|
288
|
+
row's field from the stored note and remembers which checklist entry it
|
|
289
|
+
put there. A row that the logic removes or moves keeps its entry, and a
|
|
290
|
+
row added since is a new entry.
|
|
291
|
+
|
|
292
|
+
<!-- source: widgets/notes/view.ocml -->
|
|
293
|
+
```xml
|
|
294
|
+
<for each={editor.rows} as="row" key={row.key}>
|
|
295
|
+
<box class="row">
|
|
296
|
+
<if test={rowStored(state, editor, row)}>
|
|
297
|
+
<checkbox class="row-check" checked={rowChecked(state, editor, row)} label={rowLabel(row)} on:change={check(editor.note, row.id, event.value)}/>
|
|
298
|
+
</if>
|
|
299
|
+
<else>
|
|
300
|
+
<icon class="row-new" name="circle"/>
|
|
301
|
+
</else>
|
|
302
|
+
<field class="row-field" name="item" placeholder={rowHint(editor, row)} label={t("item-hint")} on:input={rowInput(editor.key, row.key, event.value)} on:keydown={keyed(editor.key, event.key, event.ctrl)}/>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
In that form, Enter moves to the next field instead of submitting;
|
|
306
|
+
Ctrl+Enter or the `submit` button saves.
|
|
307
|
+
|
|
308
|
+
## Confirmations
|
|
309
|
+
|
|
310
|
+
Two services delete user data: `notes.delete` and `journal.delete`. They
|
|
311
|
+
are ordinary calls that need a user action, and OverCrow then asks the
|
|
312
|
+
user to confirm in a dialog that it draws itself, outside the widget. The
|
|
313
|
+
call resolves when the user confirms and fails with `cancelled` when they
|
|
314
|
+
decline. Do not draw your own "Are you sure?": OverCrow's is the one that
|
|
315
|
+
counts.
|
|
316
|
+
|
|
317
|
+
## Checklist for a form
|
|
318
|
+
|
|
319
|
+
- Every control has a `label`.
|
|
320
|
+
- Buttons that cannot act are `disabled`, and nothing that needs input
|
|
321
|
+
shows in Passive mode.
|
|
322
|
+
- The `submit` handler handles a refusal: `not_connected` when the account
|
|
323
|
+
is signed out, `busy` when the user sends too fast, `unavailable`.
|
|
324
|
+
- A form bound to an intent has no `value` on its text fields and
|
|
325
|
+
sliders, and every named control is a field of the intent.
|
|
326
|
+
- Test it with a scenario: `text` steps type into the focused field,
|
|
327
|
+
`key` steps press Enter, and `expect.intents` checks exactly what
|
|
328
|
+
OverCrow sent. See [testing a widget](testing.md#steps).
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Creator guide
|
|
2
|
+
|
|
3
|
+
This guide takes a PlayerVox OverCrow widget from an empty directory to a
|
|
4
|
+
submission: create a project, edit its view, style and logic, run it in
|
|
5
|
+
OverCrow, test it, package it and submit it. Each step links to the page
|
|
6
|
+
that covers it in full.
|
|
7
|
+
|
|
8
|
+
To work with an AI assistant instead, add OverCrow's MCP server to it: see
|
|
9
|
+
[MCP](mcp.md).
|
|
10
|
+
|
|
11
|
+
## 1. Install the tools
|
|
12
|
+
|
|
13
|
+
- **`overcrow-widget`**, the widget CLI, one binary for Linux and Windows.
|
|
14
|
+
See [installing the CLI](cli.md#installing).
|
|
15
|
+
- **Node.js 22 or later** and npm, so that `check` can type-check your
|
|
16
|
+
logic with the project's own TypeScript and the SDK from npm. The CLI checks and packages without it, but
|
|
17
|
+
warns that types were not checked.
|
|
18
|
+
- **OverCrow**, to run the widget while you write it.
|
|
19
|
+
|
|
20
|
+
`overcrow-widget doctor` tells what your setup has and lacks.
|
|
21
|
+
|
|
22
|
+
## 2. Create a project
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
overcrow-widget init my-counter --template counter
|
|
26
|
+
cd my-counter
|
|
27
|
+
npm install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`npm install` brings TypeScript and `@overcrow/sdk` 1.0.0, named in the
|
|
31
|
+
project's `package.json`.
|
|
32
|
+
|
|
33
|
+
The templates are `blank`, `counter`, `list` and `chart`. The ID defaults to
|
|
34
|
+
`com.example.<dir>`: pass `--id` with a reverse-DNS name you control
|
|
35
|
+
(`com.playervox.*` is reserved). [Files of a project](cli.md#files-of-a-project)
|
|
36
|
+
lists every file and says which ones are packaged;
|
|
37
|
+
[the SDK types](cli.md#the-sdk-types) says how to add the SDK to another
|
|
38
|
+
project.
|
|
39
|
+
|
|
40
|
+
## 3. The view
|
|
41
|
+
|
|
42
|
+
`view.ocml` is markup: elements, attributes, `{…}` expressions bound to the
|
|
43
|
+
state, and `on:<event>` handlers that call functions exported by the logic.
|
|
44
|
+
|
|
45
|
+
<!-- source: templates/counter/view.ocml -->
|
|
46
|
+
```xml
|
|
47
|
+
<box class="counter">
|
|
48
|
+
<text class="value">{state.count}</text>
|
|
49
|
+
<box class="actions">
|
|
50
|
+
<button class="step" label={t("decrement")} on:activate={decrement}>
|
|
51
|
+
<icon name="minus"/>
|
|
52
|
+
</button>
|
|
53
|
+
<button class="step" label={t("increment")} on:activate={increment}>
|
|
54
|
+
<icon name="plus"/>
|
|
55
|
+
</button>
|
|
56
|
+
</box>
|
|
57
|
+
</box>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The view also has `if`, `for` with a mandatory key, and local components:
|
|
61
|
+
see [the view](view.md). Every element and attribute is in
|
|
62
|
+
[elements and attributes](elements.md).
|
|
63
|
+
|
|
64
|
+
## 4. The style
|
|
65
|
+
|
|
66
|
+
`style.ocss` is a bounded subset of CSS: class, element, descendant and
|
|
67
|
+
child selectors, the `:hover`, `:active`, `:focus`, `:disabled` and
|
|
68
|
+
`:checked` states, flex and grid layout, and the theme's design tokens as
|
|
69
|
+
`var(--…)`, which follow the user's light or dark theme.
|
|
70
|
+
|
|
71
|
+
<!-- source: templates/counter/style.ocss -->
|
|
72
|
+
```css
|
|
73
|
+
.step {
|
|
74
|
+
width: var(--icon-button-size);
|
|
75
|
+
height: var(--icon-button-size);
|
|
76
|
+
border-radius: var(--radius-md);
|
|
77
|
+
background: var(--color-surface-raised);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
<!-- source: templates/counter/style.ocss -->
|
|
82
|
+
```css
|
|
83
|
+
.step:hover {
|
|
84
|
+
background: var(--color-surface-hover);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
There is no `@media`, `@import`, `url()` or `calc()`: an unknown
|
|
89
|
+
property or selector is an error, not a silent no-op. See
|
|
90
|
+
[the style](style.md) and [style properties and tokens](style-properties.md).
|
|
91
|
+
|
|
92
|
+
## 5. The logic
|
|
93
|
+
|
|
94
|
+
`logic.ts` owns the state. Declare its type once, mutate it in your
|
|
95
|
+
handlers, and the view follows: after each turn the VM evaluates the view
|
|
96
|
+
again and sends only what changed.
|
|
97
|
+
|
|
98
|
+
<!-- source: templates/counter/logic.ts -->
|
|
99
|
+
```ts
|
|
100
|
+
import { initState } from "@overcrow/sdk";
|
|
101
|
+
|
|
102
|
+
declare module "@overcrow/sdk" {
|
|
103
|
+
interface WidgetState {
|
|
104
|
+
count: number;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
<!-- source: templates/counter/logic.ts -->
|
|
110
|
+
```ts
|
|
111
|
+
export function increment(): void {
|
|
112
|
+
state.count += 1;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The logic runs in a sandboxed VM, not in a browser: there is no DOM,
|
|
117
|
+
`fetch`, `setTimeout`, `console` or `Intl`. The SDK provides timers,
|
|
118
|
+
services, formatting helpers for times and numbers in the user's regional
|
|
119
|
+
format, messages and drawing. See [the logic](logic.md) and the
|
|
120
|
+
[SDK reference](sdk.md).
|
|
121
|
+
|
|
122
|
+
## 6. Messages
|
|
123
|
+
|
|
124
|
+
`t("key")` in the view or the logic reads `locales/en.json` or
|
|
125
|
+
`locales/fr.json` for the user's language, and fills `{name}` parameters.
|
|
126
|
+
|
|
127
|
+
<!-- source: templates/counter/locales/en.json -->
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"decrement": "Decrease",
|
|
131
|
+
"increment": "Increase"
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Provide both files or neither, with the same keys. See
|
|
136
|
+
[messages](logic.md#messages).
|
|
137
|
+
|
|
138
|
+
## 7. Check
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
overcrow-widget check
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`check` runs the same validators as OverCrow: manifest, view, style,
|
|
145
|
+
locales, a lint of the logic against what the VM runs, the project's
|
|
146
|
+
TypeScript, and a package built in memory and read back. Each problem has a
|
|
147
|
+
stable code, a position and a help line; see
|
|
148
|
+
[diagnostics](cli.md#diagnostics).
|
|
149
|
+
|
|
150
|
+
## 8. Run it in OverCrow
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
overcrow-widget dev
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`dev` sends the widget to the OverCrow overlay running on your machine and
|
|
157
|
+
reloads it each time you save. OverCrow offers this only when it was
|
|
158
|
+
started with development installs allowed (`OVERCROW_WIDGET_DEVELOPMENT=1`;
|
|
159
|
+
`overcrow-widget doctor` prints the commands). The widget shows an
|
|
160
|
+
**Unverified · development package** marker; its declared permissions are
|
|
161
|
+
granted for the session only, and nothing is stored. `log.info(…)` from your
|
|
162
|
+
logic prints in the terminal. See
|
|
163
|
+
[the development channel](dev-channel.md).
|
|
164
|
+
|
|
165
|
+
## 9. Test it
|
|
166
|
+
|
|
167
|
+
Two kinds of tests, both described in [testing a widget](testing.md):
|
|
168
|
+
|
|
169
|
+
- unit tests of the logic with `@overcrow/sdk/testing`, which runs your
|
|
170
|
+
module with virtual time and simulated services;
|
|
171
|
+
- scenarios played by `overcrow-widget test` in OverCrow's headless
|
|
172
|
+
runtime: the real VM and renderer, services answered by fixtures, and
|
|
173
|
+
images compared with references.
|
|
174
|
+
|
|
175
|
+
<!-- source: templates/counter/tests/example.scenario.json -->
|
|
176
|
+
```json
|
|
177
|
+
"host": { "mode": "interactive" },
|
|
178
|
+
"steps": [
|
|
179
|
+
{ "expect": { "state": "running", "text": ["0"], "image": "initial" } },
|
|
180
|
+
{ "pointer": { "click": { "label": "Increase" } } },
|
|
181
|
+
{ "expect": { "text": ["1"], "noText": ["0"], "image": "increased", "fault": "none" } }
|
|
182
|
+
]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
overcrow-widget test
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## 10. Ask for what you need
|
|
190
|
+
|
|
191
|
+
A widget gets nothing by default. To call an API, keep data or read game
|
|
192
|
+
information, declare it in `manifest.json`; the user consents in the
|
|
193
|
+
Control Center before the widget gets it. A network rule names one HTTPS origin, one method and one
|
|
194
|
+
complete path, with typed parameters:
|
|
195
|
+
|
|
196
|
+
<!-- source: docs/content/examples/weather/manifest.json -->
|
|
197
|
+
```json
|
|
198
|
+
"permissions": {
|
|
199
|
+
"network": [
|
|
200
|
+
{
|
|
201
|
+
"origin": "https://api.example.com",
|
|
202
|
+
"method": "GET",
|
|
203
|
+
"path": "/v1/forecast/{city}",
|
|
204
|
+
"pathParams": { "city": { "type": "slug", "maxLength": 32 } },
|
|
205
|
+
"queryParams": {
|
|
206
|
+
"units": { "type": "enum", "values": ["metric", "imperial"], "required": true }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"storage": true
|
|
211
|
+
},
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
<!-- source: docs/content/examples/weather/logic.ts -->
|
|
215
|
+
```ts
|
|
216
|
+
const url = `https://api.example.com/v1/forecast/${state.city}?units=${state.units}`;
|
|
217
|
+
const { status, body } = await http.fetch(url, { as: "json" });
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Game and system data come from subscriptions (`fps.subscribe`,
|
|
221
|
+
`media.subscribe`…), which update your state until you cancel them. See
|
|
222
|
+
[services and permissions](services.md) for what each permission allows,
|
|
223
|
+
and [security](security.md) for what a widget can never do. The complete
|
|
224
|
+
example is in
|
|
225
|
+
[`docs/content/examples/weather`](../examples/weather/).
|
|
226
|
+
|
|
227
|
+
## 11. Package
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
overcrow-widget package
|
|
231
|
+
overcrow-widget inspect dist/com.example.my-counter-0.1.0.ocpkg
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`package` writes a deterministic `.ocpkg` to `dist/`: the same sources and
|
|
235
|
+
CLI version give the same bytes on every system. `inspect` shows what a
|
|
236
|
+
package holds and asks for, as a reviewer sees it. A package installed
|
|
237
|
+
locally stays unverified; users get widgets from the signed catalog. See
|
|
238
|
+
[the package](package.md).
|
|
239
|
+
|
|
240
|
+
## 12. Submit
|
|
241
|
+
|
|
242
|
+
Add a `listing.json` with the marketplace text, run the admission yourself,
|
|
243
|
+
then open a pull request that adds your widget under `widgets/<dir>/` on the
|
|
244
|
+
`candidate` branch of the
|
|
245
|
+
[public repository](https://github.com/Valhallab/playervox-overcrow-releases):
|
|
246
|
+
|
|
247
|
+
```sh
|
|
248
|
+
overcrow-widget admit
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`admit` runs exactly the checks of the marketplace CI. What happens next,
|
|
252
|
+
from review to the signed catalog, is in
|
|
253
|
+
[publishing and review](publishing.md).
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Build widgets for PlayerVox OverCrow
|
|
2
|
+
|
|
3
|
+
A PlayerVox OverCrow widget is a small package that the overlay draws over
|
|
4
|
+
your game: a clock, a frame-rate counter, a checklist, a chat. You describe
|
|
5
|
+
its view in markup, style it with a small subset of CSS, and write its logic
|
|
6
|
+
in TypeScript against `@overcrow/sdk`. The `overcrow-widget` command-line
|
|
7
|
+
tool checks, tests and packages it; the signed catalog brings it to users.
|
|
8
|
+
|
|
9
|
+
Every built-in widget of OverCrow is written this way, with the same public
|
|
10
|
+
SDK, package format and sandbox as yours. Their sources are the
|
|
11
|
+
[reference widgets](widgets.md).
|
|
12
|
+
|
|
13
|
+
## What a widget is made of
|
|
14
|
+
|
|
15
|
+
| File | Role | Page |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `manifest.json` | Identity, version, names, size rules, the options menu and the permissions the widget asks for. | [The manifest](manifest.md) |
|
|
18
|
+
| `view.ocml` | The view: elements such as `box`, `text`, `button`, `list` or `chart`, bound to the state with `{…}` expressions. | [The view](view.md) |
|
|
19
|
+
| `style.ocss` | Optional style: flex and grid layout, colours, borders, typography, with the theme's design tokens. | [The style](style.md) |
|
|
20
|
+
| `logic.ts` | The logic: the state, the event handlers, timers and calls to services. | [The logic](logic.md) |
|
|
21
|
+
| `locales/en.json`, `locales/fr.json` | Optional messages in English and French. | [Messages](logic.md#messages) |
|
|
22
|
+
| `LICENSE` | The license of your widget. | [The package](package.md) |
|
|
23
|
+
|
|
24
|
+
The view and the logic meet like this: the view reads `state.count` and
|
|
25
|
+
calls the handlers the logic exports.
|
|
26
|
+
|
|
27
|
+
<!-- source: templates/counter/view.ocml -->
|
|
28
|
+
```xml
|
|
29
|
+
<box class="counter">
|
|
30
|
+
<text class="value">{state.count}</text>
|
|
31
|
+
<box class="actions">
|
|
32
|
+
<button class="step" label={t("decrement")} on:activate={decrement}>
|
|
33
|
+
<icon name="minus"/>
|
|
34
|
+
</button>
|
|
35
|
+
<button class="step" label={t("increment")} on:activate={increment}>
|
|
36
|
+
<icon name="plus"/>
|
|
37
|
+
</button>
|
|
38
|
+
</box>
|
|
39
|
+
</box>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
<!-- source: templates/counter/logic.ts -->
|
|
43
|
+
```ts
|
|
44
|
+
const state = initState({ count: 0 });
|
|
45
|
+
|
|
46
|
+
export function increment(): void {
|
|
47
|
+
state.count += 1;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## How a widget runs
|
|
52
|
+
|
|
53
|
+
- The logic runs in its own small JavaScript VM, in its own sandboxed
|
|
54
|
+
process. It has no DOM, no file system, no network and no access to other
|
|
55
|
+
widgets; it reaches OverCrow only through the SDK.
|
|
56
|
+
- After each event, timer tick or service answer, the VM evaluates the view
|
|
57
|
+
again and sends OverCrow only what changed. OverCrow lays out and paints
|
|
58
|
+
the widget itself: no web engine is involved.
|
|
59
|
+
- Everything that reaches the outside (network, storage, clipboard, game
|
|
60
|
+
data) is a service of OverCrow, which checks the widget's declared and
|
|
61
|
+
consented permissions at each call. See
|
|
62
|
+
[services and permissions](services.md).
|
|
63
|
+
|
|
64
|
+
These pages call OverCrow **the host** when they describe what it does for a
|
|
65
|
+
widget: the host draws the view, answers the services and keeps the user's
|
|
66
|
+
data.
|
|
67
|
+
|
|
68
|
+
## Where to start
|
|
69
|
+
|
|
70
|
+
1. Follow the [creator guide](guide.md): from `overcrow-widget init` to a
|
|
71
|
+
submitted widget, in twelve short steps.
|
|
72
|
+
2. Read the [reference widgets](widgets.md): the built-ins, with their tests.
|
|
73
|
+
3. Go deeper, one topic per page: the [manifest](manifest.md), the
|
|
74
|
+
[view](view.md), the [style](style.md), the [logic](logic.md),
|
|
75
|
+
[services and permissions](services.md), [forms](forms.md).
|
|
76
|
+
4. Use the tools: the [CLI](cli.md), [tests](testing.md) and the
|
|
77
|
+
[development channel](dev-channel.md) that runs your widget in OverCrow
|
|
78
|
+
while you write it.
|
|
79
|
+
5. Look up an element, a style property, a service or an SDK function in the
|
|
80
|
+
[reference](reference.md).
|
|
81
|
+
6. Before you publish, read [the package](package.md),
|
|
82
|
+
[security](security.md) and [publishing and review](publishing.md).
|
|
83
|
+
|
|
84
|
+
Widgets run on Windows and Linux alike: the same package, the same SDK and
|
|
85
|
+
the same rendering.
|