@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,147 @@
|
|
|
1
|
+
# Media
|
|
2
|
+
|
|
3
|
+
`com.playervox.overcrow.media` — the track a music or video player of this
|
|
4
|
+
computer is playing: its cover, its title in bold and its artists, and in
|
|
5
|
+
Interactive mode the player's previous, play/pause and next buttons. A
|
|
6
|
+
built-in of OverCrow, written against the public `@overcrow/sdk` and
|
|
7
|
+
packaged with `overcrow-widget` like any creator's widget. MIT.
|
|
8
|
+
|
|
9
|
+
## What it shows
|
|
10
|
+
|
|
11
|
+
| State | Shows |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| A track | the square cover, the title in bold, the artists below it |
|
|
14
|
+
| Playing / paused or stopped | the middle button shows pause ("Pause") / play ("Play", FR "Lire"); nothing else differs |
|
|
15
|
+
| No cover, or it is loading | a raised square with a music note in its place |
|
|
16
|
+
| No artist | the artist row is left out; the cover shrinks to the title's height |
|
|
17
|
+
| No title | "Unknown title" / "Titre inconnu" |
|
|
18
|
+
| No player | "No active media" / "Aucun média actif", with the cover's placeholder |
|
|
19
|
+
| The player went away | the same: the previous track is gone at once |
|
|
20
|
+
| The media source failed | "No active media" and, below it, "Media is temporarily unavailable." / "Le média est temporairement indisponible."; the widget asks again 5 s later |
|
|
21
|
+
| No permission | "No active media": the widget asks OverCrow nothing |
|
|
22
|
+
|
|
23
|
+
- The cover is square, cropped to its centre, with 6 px corners, as tall as
|
|
24
|
+
the title and artist rows beside it and centred on them.
|
|
25
|
+
- Title and artists stay on one line each; line breaks in the player's text
|
|
26
|
+
become spaces. Several artists are joined with ", ".
|
|
27
|
+
- A title wider than its column scrolls back and forth: it waits 2 s,
|
|
28
|
+
travels at 18 px/s (scaled with the content) until its end shows, waits
|
|
29
|
+
2 s, travels back. It restarts on a new title or a new width, and does
|
|
30
|
+
not move while the widget is hidden. With "Scroll long titles" off it
|
|
31
|
+
ends with an ellipsis. The artists never scroll: they end with an
|
|
32
|
+
ellipsis, and hovering them shows them whole.
|
|
33
|
+
- The title's accessible name is the whole, unscrolled title; the cover and
|
|
34
|
+
its placeholder are "Cover artwork" / "Pochette".
|
|
35
|
+
- The width is the one you set: a long title never widens the widget. The
|
|
36
|
+
height follows the content, and Passive mode, without buttons, is lower.
|
|
37
|
+
|
|
38
|
+
The player's text is shown as it comes, in any language; only the widget's
|
|
39
|
+
own messages and labels follow OverCrow's language.
|
|
40
|
+
|
|
41
|
+
## Controls
|
|
42
|
+
|
|
43
|
+
| Mode | Control | Does |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| Interactive | Previous ("Previous" / "Précédent") | `media.previous` |
|
|
46
|
+
| Interactive | Play or pause ("Play" / "Lire", "Pause") | `media.playPause` |
|
|
47
|
+
| Interactive | Next ("Next" / "Suivant") | `media.next` |
|
|
48
|
+
| Interactive | Enter or Space on a focused button | the same |
|
|
49
|
+
| Interactive | Hover or focus a button, the title or the artists | its tooltip |
|
|
50
|
+
| Passive | — | no buttons, no tooltips: the track only |
|
|
51
|
+
|
|
52
|
+
A button shows only when the player offers its action: it is left out, not
|
|
53
|
+
greyed. Each click is one call, on its own gesture (OverCrow refuses a call
|
|
54
|
+
made outside one, `gesture_required`), and names the player shown. The
|
|
55
|
+
player's new state arrives with the next update; a call OverCrow cannot
|
|
56
|
+
apply — another player took over (`stale_context`), the player dropped the
|
|
57
|
+
action (`unsupported`), no player (`unavailable`) — changes nothing on
|
|
58
|
+
screen.
|
|
59
|
+
|
|
60
|
+
## Menu
|
|
61
|
+
|
|
62
|
+
| Row | Type | Default |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Show cover artwork / Afficher la pochette | toggle `show-cover` | on |
|
|
65
|
+
| Scroll long titles / Faire défiler les titres longs | toggle `scroll-title` | on |
|
|
66
|
+
|
|
67
|
+
With the cover off, the cover's slot goes, and the widget asks OverCrow for
|
|
68
|
+
no cover: OverCrow then reads no cover file and contacts no image server
|
|
69
|
+
for it.
|
|
70
|
+
|
|
71
|
+
## Permissions
|
|
72
|
+
|
|
73
|
+
- `media.read`: the current player's title, artists, playing state, the
|
|
74
|
+
actions it offers, and its cover as an image OverCrow decoded. Sensitive:
|
|
75
|
+
it tells what you listen to.
|
|
76
|
+
- `media.control`: previous, play/pause and next. Sensitive. Without it the
|
|
77
|
+
widget shows no buttons.
|
|
78
|
+
|
|
79
|
+
As sensitive capabilities rule out the network and the clipboard, the
|
|
80
|
+
widget declares neither, and no storage: it keeps nothing.
|
|
81
|
+
|
|
82
|
+
## Platforms
|
|
83
|
+
|
|
84
|
+
| | Linux | Windows |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| Source | MPRIS players on the session bus | the system's current media session |
|
|
87
|
+
| Which player | the playing one, else a paused one, else a stopped one; on a tie the first by bus name | the one Windows shows as current |
|
|
88
|
+
| Artists | up to 8, joined with ", " | the one text the player gives |
|
|
89
|
+
| Cover | the player's art URL: a local file in a cache, music or temporary folder, or an image from Spotify, YouTube or Tidal | the session's thumbnail |
|
|
90
|
+
|
|
91
|
+
OverCrow runs its media source only while a widget subscribes, and loads
|
|
92
|
+
covers only for a widget that asks for them. It hands the widget a cover
|
|
93
|
+
of at most 256 px as an `asset:` handle: the widget never sees a URL, a
|
|
94
|
+
file path or a player's bus name. The same values give the same pixels on
|
|
95
|
+
both systems.
|
|
96
|
+
|
|
97
|
+
## How it works
|
|
98
|
+
|
|
99
|
+
`media.subscribe({ cover })` gives `null` without a player, or `{ player,
|
|
100
|
+
title, artists, playing, canPrevious, canPlayPause, canNext, cover }`;
|
|
101
|
+
`player` is an opaque ID the buttons pass back. The view draws `cover` with
|
|
102
|
+
an `image` and the title with `text-overflow: marquee`: OverCrow measures,
|
|
103
|
+
scrolls and holds it, so the widget runs no timer and sends nothing while a
|
|
104
|
+
title scrolls. When OverCrow's media source fails, the subscription ends
|
|
105
|
+
with `unavailable`; the widget shows it and subscribes again after 5 s,
|
|
106
|
+
which restarts the source.
|
|
107
|
+
|
|
108
|
+
## Tests
|
|
109
|
+
|
|
110
|
+
- `tests/logic.test.mjs`, `tests/refused.test.mjs`: the logic on
|
|
111
|
+
`@overcrow/sdk/testing`.
|
|
112
|
+
- Scenarios with their reference images, covers drawn by
|
|
113
|
+
`tests/assets/generate.mjs` (synthetic images, no real album art):
|
|
114
|
+
- `render`: a playing track with its cover and buttons, in both themes and
|
|
115
|
+
languages at 100 and 150 %, and in Passive mode;
|
|
116
|
+
- `states`: no player, paused, no artist, unknown title (EN, FR), only
|
|
117
|
+
play/pause, no button, no cover (dark, light), the player gone;
|
|
118
|
+
- `covers`: a square cover, a wide one cropped to its centre, a new
|
|
119
|
+
track's cover, the cover option off and on (and the subscription it
|
|
120
|
+
makes);
|
|
121
|
+
- `marquee`: the pause at the start, the travel, the pause at the end, the
|
|
122
|
+
way back, the ellipsis with the option off, 150 %;
|
|
123
|
+
- `gesture`: Passive clicks call nothing; each button by click, Enter and
|
|
124
|
+
Space, with its player; refusals; a tooltip;
|
|
125
|
+
- `unavailable`: the failure's message (EN, FR), the new subscription
|
|
126
|
+
5 s later and the player it brings;
|
|
127
|
+
- `refused`, `read-only`.
|
|
128
|
+
|
|
129
|
+
## Cost
|
|
130
|
+
|
|
131
|
+
Measured on 2026-09-30 with OverCrow's release build, on an AMD Ryzen 7
|
|
132
|
+
5800X3D Linux workstation and in a Windows 11 virtual machine (2 vCPU),
|
|
133
|
+
at 320 px wide with a 256 px cover:
|
|
134
|
+
|
|
135
|
+
| | Linux | Windows (VM) |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| Widget start, warm, into the overlay's runtime | 6.9 ms (p50), 8.0 ms at most | VM ready in 14 ms (p50) |
|
|
138
|
+
| VM memory, cover included | 0.64 MiB private, 0.82 MiB PSS: the VM holds the cover's handle, not its pixels | 0.90 MiB private working set, 1.18 MiB private commit |
|
|
139
|
+
| Private memory in all (VM, sandbox helpers, overlay share with the cover) | under 7 MB | — |
|
|
140
|
+
| CPU, a track held | 0.03 % of one core, no frame | — |
|
|
141
|
+
| CPU, a long title scrolling | 0.15 % of one core: one frame per physical pixel it moves (18 a second at 100 %), 0.07 ms each, none during its pauses | — |
|
|
142
|
+
| CPU, a new track and cover every 2 s | 0.05 % of one core: one frame per track | — |
|
|
143
|
+
| VM CPU | one short turn per track; none while a title scrolls | — |
|
|
144
|
+
|
|
145
|
+
The egui widget it replaces repainted a scrolling title at 30 Hz (0.04 ms
|
|
146
|
+
each, painting only), inside the overlay's own process, and OverCrow polled
|
|
147
|
+
the players every second whatever the widgets showed.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "PlayerVox",
|
|
3
|
+
"spdxLicense": "MIT",
|
|
4
|
+
"sourceUrl": "https://github.com/Valhallab/playervox-overcrow-releases/tree/main/widgets/media",
|
|
5
|
+
"preview": "assets/preview.png",
|
|
6
|
+
"defaultLocale": "en",
|
|
7
|
+
"localizations": [
|
|
8
|
+
{
|
|
9
|
+
"locale": "en",
|
|
10
|
+
"name": "Media",
|
|
11
|
+
"description": "The track your music player is playing, with its cover, and previous, play/pause and next buttons in Interactive mode."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"locale": "fr",
|
|
15
|
+
"name": "Média",
|
|
16
|
+
"description": "Le morceau que joue votre lecteur de musique, avec sa pochette, et les boutons précédent, lecture/pause et suivant en mode interactif."
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"label": "Media",
|
|
3
|
+
"no-media": "No active media",
|
|
4
|
+
"unavailable": "Media is temporarily unavailable.",
|
|
5
|
+
"unknown-title": "Unknown title",
|
|
6
|
+
"cover": "Cover artwork",
|
|
7
|
+
"previous": "Previous",
|
|
8
|
+
"play": "Play",
|
|
9
|
+
"pause": "Pause",
|
|
10
|
+
"next": "Next"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"label": "Média",
|
|
3
|
+
"no-media": "Aucun média actif",
|
|
4
|
+
"unavailable": "Le média est temporairement indisponible.",
|
|
5
|
+
"unknown-title": "Titre inconnu",
|
|
6
|
+
"cover": "Pochette",
|
|
7
|
+
"previous": "Précédent",
|
|
8
|
+
"play": "Lire",
|
|
9
|
+
"pause": "Pause",
|
|
10
|
+
"next": "Suivant"
|
|
11
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Media: the track a desktop player is playing, its cover and, in
|
|
2
|
+
// Interactive mode, the player's previous / play-pause / next buttons. The
|
|
3
|
+
// host picks the player (MPRIS on Linux, the system's current session on
|
|
4
|
+
// Windows), decodes the cover into an `asset:` handle and scrolls an
|
|
5
|
+
// overflowing title (`text-overflow: marquee`): the widget only lays out.
|
|
6
|
+
import {
|
|
7
|
+
hasGrant,
|
|
8
|
+
host,
|
|
9
|
+
initState,
|
|
10
|
+
media,
|
|
11
|
+
onHost,
|
|
12
|
+
option,
|
|
13
|
+
t,
|
|
14
|
+
timers,
|
|
15
|
+
type Media,
|
|
16
|
+
type Subscription,
|
|
17
|
+
type Timer,
|
|
18
|
+
} from "@overcrow/sdk";
|
|
19
|
+
|
|
20
|
+
declare module "@overcrow/sdk" {
|
|
21
|
+
interface WidgetState {
|
|
22
|
+
/** The current player, or `null` without one. */
|
|
23
|
+
media: Media | null;
|
|
24
|
+
/** The host's media source failed; it retries on the next subscription. */
|
|
25
|
+
unavailable: boolean;
|
|
26
|
+
/** The buttons take input only in Interactive mode. */
|
|
27
|
+
interactive: boolean;
|
|
28
|
+
showCover: boolean;
|
|
29
|
+
scrollTitle: boolean;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* After the host ends the subscription with a failure (its media source
|
|
35
|
+
* failed), the widget subscribes again this much later; the host restarts
|
|
36
|
+
* its source for the new subscription.
|
|
37
|
+
*/
|
|
38
|
+
export const RETRY_MS = 5_000;
|
|
39
|
+
|
|
40
|
+
const canRead = hasGrant("media.read");
|
|
41
|
+
const canControl = hasGrant("media.control");
|
|
42
|
+
|
|
43
|
+
const state = initState({
|
|
44
|
+
media: null as Media | null,
|
|
45
|
+
unavailable: false,
|
|
46
|
+
interactive: host.mode === "interactive",
|
|
47
|
+
showCover: option("show-cover", true),
|
|
48
|
+
scrollTitle: option("scroll-title", true),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** Provider text on one line: line breaks never make a second row. */
|
|
52
|
+
export function oneLine(text: string): string {
|
|
53
|
+
return text.replace(/\s*[\r\n]+\s*/g, " ").trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The title, or "Unknown title" without one. */
|
|
57
|
+
export function title(current: Media): string {
|
|
58
|
+
const text = current.title === null ? "" : oneLine(current.title);
|
|
59
|
+
return text === "" ? t("unknown-title") : text;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The artists on one line, or nothing: the row is then left out. */
|
|
63
|
+
export function artists(current: Media): string {
|
|
64
|
+
return current.artists
|
|
65
|
+
.map(oneLine)
|
|
66
|
+
.filter((artist) => artist !== "")
|
|
67
|
+
.join(", ");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The cover's size class: as tall as the title and artist rows, or as one
|
|
72
|
+
* row without artists or without a player (the placeholder).
|
|
73
|
+
*/
|
|
74
|
+
export function coverClass(current: Media | null, cover: string | null): string {
|
|
75
|
+
const size = current !== null && artists(current) !== "" ? "cover tall" : "cover short";
|
|
76
|
+
return cover === null ? `${size} placeholder` : size;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The title scrolls when it overflows, or ends with an ellipsis. */
|
|
80
|
+
export function titleClass(scroll: boolean): string {
|
|
81
|
+
return scroll ? "title scrolling" : "title";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function rootClass(showCover: boolean): string {
|
|
85
|
+
return showCover ? "media with-cover" : "media";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** One transport button. */
|
|
89
|
+
export interface Control {
|
|
90
|
+
key: "previous" | "play-pause" | "next";
|
|
91
|
+
icon: string;
|
|
92
|
+
label: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The buttons the player supports, in the order previous, play/pause,
|
|
97
|
+
* next: an unsupported action has no button. None in Passive mode or
|
|
98
|
+
* without the control grant.
|
|
99
|
+
*/
|
|
100
|
+
export function controls(current: Media | null, interactive: boolean): Control[] {
|
|
101
|
+
if (!interactive || !canControl || current === null) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const list: Control[] = [];
|
|
105
|
+
if (current.canPrevious) {
|
|
106
|
+
list.push({ key: "previous", icon: "skip-back", label: t("previous") });
|
|
107
|
+
}
|
|
108
|
+
if (current.canPlayPause) {
|
|
109
|
+
list.push(
|
|
110
|
+
current.playing
|
|
111
|
+
? { key: "play-pause", icon: "pause", label: t("pause") }
|
|
112
|
+
: { key: "play-pause", icon: "play", label: t("play") },
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (current.canNext) {
|
|
116
|
+
list.push({ key: "next", icon: "skip-forward", label: t("next") });
|
|
117
|
+
}
|
|
118
|
+
return list;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Sends a button's action to the player shown. The host refuses it with
|
|
123
|
+
* `stale_context` when another player replaced it meanwhile and with
|
|
124
|
+
* `unsupported` when the player no longer offers the action; the next
|
|
125
|
+
* update shows the player's state either way, so a failure shows nothing.
|
|
126
|
+
*/
|
|
127
|
+
export function activate(key: Control["key"]): void {
|
|
128
|
+
const current = state.media;
|
|
129
|
+
if (current === null) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const params = { player: current.player };
|
|
133
|
+
const call =
|
|
134
|
+
key === "previous" ? media.previous(params) : key === "next" ? media.next(params) : media.playPause(params);
|
|
135
|
+
call.catch(() => {});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let subscription: Subscription | null = null;
|
|
139
|
+
let retry: Timer | null = null;
|
|
140
|
+
|
|
141
|
+
function subscribe(): void {
|
|
142
|
+
retry = null;
|
|
143
|
+
const current = media.subscribe({ cover: state.showCover }, (update) => {
|
|
144
|
+
if (update.ok) {
|
|
145
|
+
state.media = update.value;
|
|
146
|
+
state.unavailable = false;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// The subscription ended: the host's source failed. Show it and ask
|
|
150
|
+
// again later; hidden, the timer waits until the widget shows.
|
|
151
|
+
if (subscription === current) {
|
|
152
|
+
subscription = null;
|
|
153
|
+
state.media = null;
|
|
154
|
+
state.unavailable = true;
|
|
155
|
+
retry = timers.after(RETRY_MS, subscribe);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
subscription = current;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Subscribes again, for the cover option; the host sends the value at once. */
|
|
162
|
+
function resubscribe(): void {
|
|
163
|
+
retry?.cancel();
|
|
164
|
+
retry = null;
|
|
165
|
+
subscription?.cancel();
|
|
166
|
+
subscription = null;
|
|
167
|
+
subscribe();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
onHost((changed) => {
|
|
171
|
+
if (changed.includes("mode")) {
|
|
172
|
+
state.interactive = host.mode === "interactive";
|
|
173
|
+
}
|
|
174
|
+
if (changed.includes("options")) {
|
|
175
|
+
state.scrollTitle = option("scroll-title", true);
|
|
176
|
+
const showCover = option("show-cover", true);
|
|
177
|
+
if (showCover !== state.showCover) {
|
|
178
|
+
state.showCover = showCover;
|
|
179
|
+
// Without the cover the host loads none: the subscription says so.
|
|
180
|
+
if (canRead && !state.unavailable) {
|
|
181
|
+
resubscribe();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Without the grant the host would refuse the subscription: show that no
|
|
188
|
+
// media plays instead of asking.
|
|
189
|
+
if (canRead) {
|
|
190
|
+
subscribe();
|
|
191
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"apiVersion": 1,
|
|
4
|
+
"id": "com.playervox.overcrow.media",
|
|
5
|
+
"version": "1.0.2",
|
|
6
|
+
"name": {
|
|
7
|
+
"en": "Media",
|
|
8
|
+
"fr": "Média"
|
|
9
|
+
},
|
|
10
|
+
"sizing": {
|
|
11
|
+
"fit": "height",
|
|
12
|
+
"preferred": { "width": 320, "height": 160 },
|
|
13
|
+
"min": { "width": 160, "height": 24 },
|
|
14
|
+
"max": { "width": 900, "height": 240 }
|
|
15
|
+
},
|
|
16
|
+
"permissions": {
|
|
17
|
+
"capabilities": ["media.read", "media.control"]
|
|
18
|
+
},
|
|
19
|
+
"wrapper": {
|
|
20
|
+
"menu": [
|
|
21
|
+
{
|
|
22
|
+
"type": "toggle",
|
|
23
|
+
"id": "show-cover",
|
|
24
|
+
"label": { "en": "Show cover artwork", "fr": "Afficher la pochette" },
|
|
25
|
+
"default": true
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "toggle",
|
|
29
|
+
"id": "scroll-title",
|
|
30
|
+
"label": { "en": "Scroll long titles", "fr": "Faire défiler les titres longs" },
|
|
31
|
+
"default": true
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "overcrow-widget-media",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"private": true,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"check": "overcrow-widget check",
|
|
9
|
+
"package": "overcrow-widget package",
|
|
10
|
+
"test": "node --test tests/*.test.mjs"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@overcrow/sdk": "1.0.0",
|
|
14
|
+
"typescript": "6.0.3"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/* The cover on the left, as tall as the title and artist rows and centred
|
|
2
|
+
on them; the text column beside it, the buttons and the source message
|
|
3
|
+
under the text. The wrapper draws the panel and its 8 px margin; the
|
|
4
|
+
content adds 2 px on the sides (the built-in's 10 × 8 px margin), 8 px
|
|
5
|
+
beside the cover and 2 px between the rows. The width is the user's:
|
|
6
|
+
long text never widens the panel. */
|
|
7
|
+
.media {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: minmax(0px, 1fr);
|
|
10
|
+
column-gap: 8px;
|
|
11
|
+
row-gap: 2px;
|
|
12
|
+
padding: 0 2px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.with-cover {
|
|
16
|
+
grid-template-columns: auto minmax(0px, 1fr);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.with-cover .cover {
|
|
20
|
+
grid-column: 1;
|
|
21
|
+
grid-row: 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.with-cover .meta,
|
|
25
|
+
.with-cover .controls,
|
|
26
|
+
.with-cover .message {
|
|
27
|
+
grid-column: 2;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.with-cover .meta {
|
|
31
|
+
grid-row: 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Fixed line heights keep the cover exactly as tall as the rows beside
|
|
35
|
+
it: 18 px for the title, 2 px, 16 px for the artists. */
|
|
36
|
+
.cover {
|
|
37
|
+
align-self: center;
|
|
38
|
+
border-radius: var(--radius-sm);
|
|
39
|
+
object-fit: cover;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.cover.tall {
|
|
44
|
+
width: 36px;
|
|
45
|
+
height: 36px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.cover.short {
|
|
49
|
+
width: 18px;
|
|
50
|
+
height: 18px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* No cover: the music icon on a raised square, inset by 28 %. */
|
|
54
|
+
.placeholder {
|
|
55
|
+
background: var(--color-surface-raised);
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
color: var(--color-text-muted);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.cover-icon {
|
|
62
|
+
width: 44%;
|
|
63
|
+
height: 44%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.meta {
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
gap: 2px;
|
|
69
|
+
min-width: 0px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.title {
|
|
73
|
+
font-family: var(--font-ui);
|
|
74
|
+
font-size: var(--font-size-body);
|
|
75
|
+
font-weight: 700;
|
|
76
|
+
line-height: 18px;
|
|
77
|
+
color: var(--color-text);
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
text-overflow: ellipsis;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.title.scrolling {
|
|
84
|
+
text-overflow: marquee;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.artist {
|
|
88
|
+
font-family: var(--font-ui);
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
line-height: 16px;
|
|
91
|
+
color: var(--color-text-muted);
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
text-overflow: ellipsis;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.empty {
|
|
98
|
+
font-family: var(--font-ui);
|
|
99
|
+
font-size: var(--font-size-body);
|
|
100
|
+
line-height: 18px;
|
|
101
|
+
color: var(--color-text-muted);
|
|
102
|
+
white-space: nowrap;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
text-overflow: ellipsis;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.message {
|
|
108
|
+
font-family: var(--font-ui);
|
|
109
|
+
font-size: var(--font-size-small);
|
|
110
|
+
color: var(--color-text-muted);
|
|
111
|
+
white-space: nowrap;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
text-overflow: ellipsis;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.controls {
|
|
117
|
+
flex-direction: row;
|
|
118
|
+
gap: 3px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* The built-in's compact icon buttons: raised with a border, lighter under
|
|
122
|
+
the pointer or with the keyboard focus, the accent while pressed. The
|
|
123
|
+
icon (sized by `font-size`) fills the 12 px content box exactly and is
|
|
124
|
+
centred in it. */
|
|
125
|
+
.control {
|
|
126
|
+
width: var(--icon-button-size);
|
|
127
|
+
height: var(--icon-button-size);
|
|
128
|
+
padding: 4px;
|
|
129
|
+
border: 1px solid var(--color-border);
|
|
130
|
+
font-size: 12px;
|
|
131
|
+
justify-content: center;
|
|
132
|
+
align-items: center;
|
|
133
|
+
border-radius: var(--radius-sm);
|
|
134
|
+
background: var(--color-surface-raised);
|
|
135
|
+
color: var(--color-text-secondary);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.control:hover,
|
|
139
|
+
.control:focus {
|
|
140
|
+
background: var(--color-surface-hover);
|
|
141
|
+
color: var(--color-text);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.control:active {
|
|
145
|
+
background: var(--color-accent-soft);
|
|
146
|
+
border-color: var(--color-accent);
|
|
147
|
+
color: var(--color-accent-hover);
|
|
148
|
+
}
|