@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +93 -0
- package/README.md +36 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27368 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16884 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +209 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +575 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +335 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +122 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Box, Card, CardContent, Stack, Typography } from '@mui/material';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
title: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
action?: ReactNode;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** One card, one heading, one job. Every panel on this surface is built of these. */
|
|
12
|
+
export default function Section({ title, hint, action, children }: Props) {
|
|
13
|
+
return (
|
|
14
|
+
<Card variant="outlined">
|
|
15
|
+
<CardContent>
|
|
16
|
+
<Stack
|
|
17
|
+
direction="row"
|
|
18
|
+
spacing={2}
|
|
19
|
+
sx={{ alignItems: 'flex-start', justifyContent: 'space-between' }}
|
|
20
|
+
>
|
|
21
|
+
<Box>
|
|
22
|
+
<Typography variant="h6" component="h2">
|
|
23
|
+
{title}
|
|
24
|
+
</Typography>
|
|
25
|
+
{hint && (
|
|
26
|
+
<Typography variant="body2" color="text.secondary">
|
|
27
|
+
{hint}
|
|
28
|
+
</Typography>
|
|
29
|
+
)}
|
|
30
|
+
</Box>
|
|
31
|
+
{action}
|
|
32
|
+
</Stack>
|
|
33
|
+
<Box sx={{ mt: 2 }}>{children}</Box>
|
|
34
|
+
</CardContent>
|
|
35
|
+
</Card>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
|
|
2
|
+
import { useUserInterfaceCallbacks, useUserInterfaceThemePreference } from '@seriousgaming/sdk/ui';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
|
|
5
|
+
const OPTIONS = [
|
|
6
|
+
{ value: 'light', glyph: '☀' },
|
|
7
|
+
{ value: 'dark', glyph: '☾' },
|
|
8
|
+
{ value: 'system', glyph: 'A' },
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Light, dark, or whatever the device says.
|
|
13
|
+
*
|
|
14
|
+
* The preference is the host's, not this surface's: `setThemePreference` tells
|
|
15
|
+
* the host, the host decides, and the new palette comes back through
|
|
16
|
+
* `useUserInterfaceTheme` like any other state. So the whole session follows,
|
|
17
|
+
* including the facilitator's screen and the chrome around this frame, rather
|
|
18
|
+
* than one iframe going dark on its own.
|
|
19
|
+
*/
|
|
20
|
+
export default function ThemeToggle() {
|
|
21
|
+
const preference = useUserInterfaceThemePreference();
|
|
22
|
+
const { setThemePreference } = useUserInterfaceCallbacks();
|
|
23
|
+
const { t } = useTranslation();
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ToggleButtonGroup
|
|
27
|
+
size="small"
|
|
28
|
+
exclusive
|
|
29
|
+
value={preference}
|
|
30
|
+
aria-label={t('theme.label')}
|
|
31
|
+
onChange={(_, next) => {
|
|
32
|
+
// Null when the pressed button was already selected. Keeping the
|
|
33
|
+
// current value means the control never lands in no state at all.
|
|
34
|
+
if (next) setThemePreference(next);
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
{OPTIONS.map((option) => (
|
|
38
|
+
<ToggleButton
|
|
39
|
+
key={option.value}
|
|
40
|
+
value={option.value}
|
|
41
|
+
aria-label={t(`theme.${option.value}`)}
|
|
42
|
+
sx={{ px: 1.25, py: 0.25, lineHeight: 1 }}
|
|
43
|
+
>
|
|
44
|
+
{option.glyph}
|
|
45
|
+
</ToggleButton>
|
|
46
|
+
))}
|
|
47
|
+
</ToggleButtonGroup>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMockParticipant,
|
|
3
|
+
mergingSetData,
|
|
4
|
+
renderUserInterface,
|
|
5
|
+
} from '@seriousgaming/sdk/ui/testing';
|
|
6
|
+
import { fireEvent, screen } from '@testing-library/react';
|
|
7
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
8
|
+
|
|
9
|
+
import type { Context } from '../types';
|
|
10
|
+
import './i18n';
|
|
11
|
+
import { baseSettings, baseContext } from './test-helpers';
|
|
12
|
+
import TodayPanel from './TodayPanel';
|
|
13
|
+
|
|
14
|
+
const ada = createMockParticipant({ id: 'participant-1', name: 'Ada' });
|
|
15
|
+
|
|
16
|
+
const setSlider = (name: string, value: number) => {
|
|
17
|
+
const slider = screen.getByRole('slider', { name });
|
|
18
|
+
fireEvent.change(slider, { target: { value: String(value) } });
|
|
19
|
+
// The row writes on release, not on every frame of a drag.
|
|
20
|
+
fireEvent.mouseUp(slider);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('changing how many to make', () => {
|
|
24
|
+
it('sends only the item that changed', () => {
|
|
25
|
+
const setData = vi.fn();
|
|
26
|
+
renderUserInterface(<TodayPanel />, {
|
|
27
|
+
data: baseContext,
|
|
28
|
+
settings: baseSettings,
|
|
29
|
+
participant: ada,
|
|
30
|
+
setData,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
fireEvent.click(screen.getByRole('button', { name: 'Make more Iced tea' }));
|
|
34
|
+
|
|
35
|
+
// Only `icedtea`. Never the whole `Make` map: that would re-send the
|
|
36
|
+
// lemonade and the cookies from a snapshot that may already be stale.
|
|
37
|
+
expect(setData).toHaveBeenCalledWith({ plan: { Make: { icedtea: 25 } } });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('nudges by the configured step', () => {
|
|
41
|
+
const setData = vi.fn();
|
|
42
|
+
renderUserInterface(<TodayPanel />, {
|
|
43
|
+
data: baseContext,
|
|
44
|
+
settings: { ...baseSettings, ui: { ...baseSettings.ui, Step: 1 } },
|
|
45
|
+
participant: ada,
|
|
46
|
+
setData,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
fireEvent.click(screen.getByRole('button', { name: 'Make less Lemonade' }));
|
|
50
|
+
|
|
51
|
+
expect(setData).toHaveBeenCalledWith({ plan: { Make: { lemonade: 39 } } });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The whole reason the plan is a map keyed by item rather than three fields
|
|
56
|
+
* on one object. This harness merges the way the host does, so a payload
|
|
57
|
+
* that reached too wide would show up here as a team-mate's number being
|
|
58
|
+
* undone.
|
|
59
|
+
*/
|
|
60
|
+
it('leaves the items somebody else is looking after alone', () => {
|
|
61
|
+
let data: Context = baseContext;
|
|
62
|
+
const setData = mergingSetData<Context>((update) => {
|
|
63
|
+
data = update(data);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
renderUserInterface(<TodayPanel />, {
|
|
67
|
+
data,
|
|
68
|
+
settings: baseSettings,
|
|
69
|
+
participant: ada,
|
|
70
|
+
setData,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Grace has just taken the cookies to 30, and it has landed.
|
|
74
|
+
data = { ...data, plan: { Make: { ...data.plan.Make, cookies: 30 } } };
|
|
75
|
+
|
|
76
|
+
// Ada, who never saw that, changes her own item.
|
|
77
|
+
setSlider('Lemonade', 55);
|
|
78
|
+
|
|
79
|
+
expect(data.plan.Make).toEqual({ lemonade: 55, icedtea: 20, cookies: 30 });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('writes the number the slider was left on', () => {
|
|
83
|
+
const setData = vi.fn();
|
|
84
|
+
renderUserInterface(<TodayPanel />, {
|
|
85
|
+
data: baseContext,
|
|
86
|
+
settings: baseSettings,
|
|
87
|
+
participant: ada,
|
|
88
|
+
setData,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
setSlider('Lemonade', 70);
|
|
92
|
+
|
|
93
|
+
expect(setData).toHaveBeenLastCalledWith({ plan: { Make: { lemonade: 70 } } });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('the stall’s capacity', () => {
|
|
98
|
+
it('says how much room is left', () => {
|
|
99
|
+
renderUserInterface(<TodayPanel />, {
|
|
100
|
+
data: baseContext,
|
|
101
|
+
settings: baseSettings,
|
|
102
|
+
participant: ada,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(screen.getByTestId('capacity')).toHaveTextContent('70 of 120');
|
|
106
|
+
expect(screen.getByText(/Room for 50 more/)).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// A merged plan can exceed the stall even though no one write did, so the
|
|
110
|
+
// surface has to be able to render that state rather than assume it away.
|
|
111
|
+
it('says so when the plan is over what the stall can make', () => {
|
|
112
|
+
renderUserInterface(<TodayPanel />, {
|
|
113
|
+
data: {
|
|
114
|
+
...baseContext,
|
|
115
|
+
plan: { Make: { lemonade: 90, icedtea: 40, cookies: 20 } },
|
|
116
|
+
},
|
|
117
|
+
settings: baseSettings,
|
|
118
|
+
participant: ada,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(screen.getByTestId('capacity')).toHaveTextContent('150 of 120');
|
|
122
|
+
expect(screen.getByText(/30 more than the stall can make/)).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('the menu', () => {
|
|
127
|
+
it('lists in the order the game gave, not whatever order the keys arrived in', () => {
|
|
128
|
+
renderUserInterface(<TodayPanel />, {
|
|
129
|
+
data: baseContext,
|
|
130
|
+
settings: baseSettings,
|
|
131
|
+
participant: ada,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const names = screen
|
|
135
|
+
.getAllByRole('slider')
|
|
136
|
+
.map((slider) => slider.getAttribute('aria-label'));
|
|
137
|
+
expect(names).toEqual(['Lemonade', 'Iced tea', 'Cookies']);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('marks the item this participant looks after', () => {
|
|
141
|
+
renderUserInterface(<TodayPanel />, {
|
|
142
|
+
data: {
|
|
143
|
+
...baseContext,
|
|
144
|
+
roles: { LooksAfter: { 'participant-1': 'cookies' } },
|
|
145
|
+
},
|
|
146
|
+
settings: baseSettings,
|
|
147
|
+
participant: ada,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(screen.getByTestId('make-cookies')).toHaveTextContent('Yours');
|
|
151
|
+
expect(screen.getByTestId('make-lemonade')).not.toHaveTextContent('Yours');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('shows what each one earns and costs, and what today looks like', () => {
|
|
155
|
+
renderUserInterface(<TodayPanel />, {
|
|
156
|
+
data: baseContext,
|
|
157
|
+
settings: baseSettings,
|
|
158
|
+
participant: ada,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(screen.getByTestId('make-lemonade')).toHaveTextContent(
|
|
162
|
+
'£0.50 each, costs £0.15 to make. About 72 people will want one today.'
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('observing', () => {
|
|
168
|
+
it('says so, and offers nothing to change', () => {
|
|
169
|
+
renderUserInterface(<TodayPanel />, {
|
|
170
|
+
data: baseContext,
|
|
171
|
+
settings: baseSettings,
|
|
172
|
+
viewerRole: 'observer',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(screen.getByText(/watching this stall/)).toBeInTheDocument();
|
|
176
|
+
expect(screen.getByRole('slider', { name: 'Lemonade' })).toBeDisabled();
|
|
177
|
+
expect(screen.getByRole('button', { name: 'Make more Lemonade' })).toBeDisabled();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Alert, Chip, LinearProgress, Stack, Typography } from '@mui/material';
|
|
2
|
+
import { useUserInterfaceData, useUserInterfaceParticipant } from '@seriousgaming/sdk/ui';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
|
|
5
|
+
import type { Settings, Context } from '../types';
|
|
6
|
+
import MakeRow from './MakeRow';
|
|
7
|
+
import { menuOf, totalPlanned } from './menu';
|
|
8
|
+
import Section from './Section';
|
|
9
|
+
import { useWrite } from './writes';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* What the stall is making today.
|
|
13
|
+
*
|
|
14
|
+
* The only screen that writes, and it writes one key per line of the menu. Two
|
|
15
|
+
* people changing two different things write two different keys, and the host's
|
|
16
|
+
* merge keeps both: no locks, no turn-taking, and nothing to explain to the
|
|
17
|
+
* room. The capacity they share is the one thing that can still go wrong, and
|
|
18
|
+
* `validators/capacity` is what catches it.
|
|
19
|
+
*/
|
|
20
|
+
export default function TodayPanel() {
|
|
21
|
+
const { data, settings, setData } = useUserInterfaceData<Context, Settings>();
|
|
22
|
+
const participant = useUserInterfaceParticipant();
|
|
23
|
+
const { run, pending } = useWrite();
|
|
24
|
+
const { t } = useTranslation();
|
|
25
|
+
|
|
26
|
+
const menu = menuOf(settings.menu);
|
|
27
|
+
const planned = totalPlanned(data.plan.Make);
|
|
28
|
+
const room = settings.menu.Capacity - planned;
|
|
29
|
+
const yours = participant ? data.roles.LooksAfter[participant.id] : undefined;
|
|
30
|
+
|
|
31
|
+
const setCount = (id: string, next: number) => {
|
|
32
|
+
// Only this item's key. Never `{ ...data.plan.Make, [id]: next }`: that
|
|
33
|
+
// re-sends every other item from a snapshot that may already be stale,
|
|
34
|
+
// and would undo whatever a team-mate changed a moment ago.
|
|
35
|
+
void run(() => setData({ plan: { Make: { [id]: next } } }), `make:${id}`);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Section
|
|
40
|
+
title={t('today.title')}
|
|
41
|
+
hint={t(`weather.${data.weather.Today}`)}
|
|
42
|
+
action={
|
|
43
|
+
<Chip
|
|
44
|
+
color={room < 0 ? 'error' : room === 0 ? 'success' : 'default'}
|
|
45
|
+
label={t('today.capacity', { planned, capacity: settings.menu.Capacity })}
|
|
46
|
+
data-testid="capacity"
|
|
47
|
+
/>
|
|
48
|
+
}
|
|
49
|
+
>
|
|
50
|
+
<LinearProgress
|
|
51
|
+
variant="determinate"
|
|
52
|
+
color={room < 0 ? 'error' : 'primary'}
|
|
53
|
+
value={Math.min(100, (planned / Math.max(1, settings.menu.Capacity)) * 100)}
|
|
54
|
+
sx={{ mb: 3 }}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
{!participant && (
|
|
58
|
+
<Alert severity="info" sx={{ mb: 3 }}>
|
|
59
|
+
{t('today.observing')}
|
|
60
|
+
</Alert>
|
|
61
|
+
)}
|
|
62
|
+
|
|
63
|
+
<Stack spacing={3}>
|
|
64
|
+
{menu.map(({ id, item }) => (
|
|
65
|
+
<MakeRow
|
|
66
|
+
key={id}
|
|
67
|
+
id={id}
|
|
68
|
+
item={item}
|
|
69
|
+
value={data.plan.Make[id] ?? 0}
|
|
70
|
+
expected={data.weather.Expected[id] ?? 0}
|
|
71
|
+
// One item can fill the stall on its own, so the cap on
|
|
72
|
+
// a single row is the stall's. The rule about the total
|
|
73
|
+
// is the validator's, because only the merged plan
|
|
74
|
+
// shows it.
|
|
75
|
+
max={settings.menu.Capacity}
|
|
76
|
+
step={settings.ui.Step}
|
|
77
|
+
currencySymbol={settings.ui.CurrencySymbol}
|
|
78
|
+
yours={yours === id}
|
|
79
|
+
disabled={!participant || pending}
|
|
80
|
+
onChange={(next) => setCount(id, next)}
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
</Stack>
|
|
84
|
+
|
|
85
|
+
<Typography variant="body2" color="text.secondary" sx={{ mt: 3 }}>
|
|
86
|
+
{room < 0
|
|
87
|
+
? t('today.over', { over: -room })
|
|
88
|
+
: t('today.room', { room, capacity: settings.menu.Capacity })}
|
|
89
|
+
</Typography>
|
|
90
|
+
</Section>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Alert,
|
|
3
|
+
Box,
|
|
4
|
+
Stack,
|
|
5
|
+
Table,
|
|
6
|
+
TableBody,
|
|
7
|
+
TableCell,
|
|
8
|
+
TableHead,
|
|
9
|
+
TableRow,
|
|
10
|
+
Typography,
|
|
11
|
+
} from '@mui/material';
|
|
12
|
+
import { useUserInterfaceData } from '@seriousgaming/sdk/ui';
|
|
13
|
+
import { useTranslation } from 'react-i18next';
|
|
14
|
+
|
|
15
|
+
import type { Settings, Context } from '../types';
|
|
16
|
+
import { count, money } from './format';
|
|
17
|
+
import { menuOf } from './menu';
|
|
18
|
+
import Section from './Section';
|
|
19
|
+
|
|
20
|
+
function Stat({
|
|
21
|
+
id,
|
|
22
|
+
label,
|
|
23
|
+
value,
|
|
24
|
+
tone,
|
|
25
|
+
}: {
|
|
26
|
+
id: string;
|
|
27
|
+
label: string;
|
|
28
|
+
value: string;
|
|
29
|
+
tone?: 'good' | 'bad';
|
|
30
|
+
}) {
|
|
31
|
+
return (
|
|
32
|
+
<Box sx={{ minWidth: 110 }} data-testid={`stat-${id}`}>
|
|
33
|
+
<Typography variant="overline" color="text.secondary" component="p">
|
|
34
|
+
{label}
|
|
35
|
+
</Typography>
|
|
36
|
+
<Typography
|
|
37
|
+
variant="h6"
|
|
38
|
+
component="p"
|
|
39
|
+
sx={{ fontVariantNumeric: 'tabular-nums' }}
|
|
40
|
+
color={tone === 'good' ? 'success.main' : tone === 'bad' ? 'error.main' : undefined}
|
|
41
|
+
>
|
|
42
|
+
{value}
|
|
43
|
+
</Typography>
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** How the last day actually went, item by item. */
|
|
49
|
+
export default function YesterdayPanel() {
|
|
50
|
+
const { data, settings } = useUserInterfaceData<Context, Settings>();
|
|
51
|
+
const { t } = useTranslation();
|
|
52
|
+
const symbol = settings.ui.CurrencySymbol;
|
|
53
|
+
const results = data.results;
|
|
54
|
+
|
|
55
|
+
if (results.Day === 0) {
|
|
56
|
+
return (
|
|
57
|
+
<Section title={t('yesterday.title')} hint={t('yesterday.none')}>
|
|
58
|
+
<Alert severity="info">{t('yesterday.firstDay')}</Alert>
|
|
59
|
+
</Section>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const pouredAway = Object.values(results.Lines).reduce((sum, l) => sum + l.PouredAway, 0);
|
|
64
|
+
const turnedAway = Object.values(results.Lines).reduce((sum, l) => sum + l.TurnedAway, 0);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Section
|
|
68
|
+
title={t('yesterday.title')}
|
|
69
|
+
hint={t('yesterday.on', {
|
|
70
|
+
day: results.Day,
|
|
71
|
+
weather: t(`weather.${results.Weather}Short`),
|
|
72
|
+
})}
|
|
73
|
+
>
|
|
74
|
+
<Stack direction="row" spacing={3} useFlexGap sx={{ flexWrap: 'wrap', mb: 3 }}>
|
|
75
|
+
<Stat
|
|
76
|
+
id="takings"
|
|
77
|
+
label={t('yesterday.takings')}
|
|
78
|
+
value={money(symbol, results.Takings)}
|
|
79
|
+
/>
|
|
80
|
+
<Stat
|
|
81
|
+
id="spent"
|
|
82
|
+
label={t('yesterday.spent')}
|
|
83
|
+
value={money(symbol, results.Spent)}
|
|
84
|
+
/>
|
|
85
|
+
<Stat
|
|
86
|
+
id="profit"
|
|
87
|
+
label={t('yesterday.profit')}
|
|
88
|
+
value={money(symbol, results.Profit)}
|
|
89
|
+
tone={results.Profit >= 0 ? 'good' : 'bad'}
|
|
90
|
+
/>
|
|
91
|
+
<Stat
|
|
92
|
+
id="purse"
|
|
93
|
+
label={t('yesterday.purse')}
|
|
94
|
+
value={money(symbol, results.Purse)}
|
|
95
|
+
/>
|
|
96
|
+
<Stat
|
|
97
|
+
id="pouredaway"
|
|
98
|
+
label={t('yesterday.pouredAway')}
|
|
99
|
+
value={count(pouredAway)}
|
|
100
|
+
tone={pouredAway > 0 ? 'bad' : undefined}
|
|
101
|
+
/>
|
|
102
|
+
<Stat
|
|
103
|
+
id="turnedaway"
|
|
104
|
+
label={t('yesterday.turnedAway')}
|
|
105
|
+
value={count(turnedAway)}
|
|
106
|
+
tone={turnedAway > 0 ? 'bad' : undefined}
|
|
107
|
+
/>
|
|
108
|
+
</Stack>
|
|
109
|
+
|
|
110
|
+
<Box sx={{ overflowX: 'auto' }}>
|
|
111
|
+
<Table size="small">
|
|
112
|
+
<TableHead>
|
|
113
|
+
<TableRow>
|
|
114
|
+
<TableCell>{t('yesterday.item')}</TableCell>
|
|
115
|
+
<TableCell align="right">{t('yesterday.made')}</TableCell>
|
|
116
|
+
<TableCell align="right">{t('yesterday.wanted')}</TableCell>
|
|
117
|
+
<TableCell align="right">{t('yesterday.sold')}</TableCell>
|
|
118
|
+
<TableCell align="right">{t('yesterday.pouredAway')}</TableCell>
|
|
119
|
+
<TableCell align="right">{t('yesterday.turnedAway')}</TableCell>
|
|
120
|
+
</TableRow>
|
|
121
|
+
</TableHead>
|
|
122
|
+
<TableBody>
|
|
123
|
+
{menuOf(settings.menu).map(({ id, item }) => {
|
|
124
|
+
const line = results.Lines[id];
|
|
125
|
+
if (!line) return null;
|
|
126
|
+
return (
|
|
127
|
+
<TableRow key={id} data-testid={`line-${id}`}>
|
|
128
|
+
<TableCell>{item.Name}</TableCell>
|
|
129
|
+
<TableCell align="right">{count(line.Made)}</TableCell>
|
|
130
|
+
<TableCell align="right">{count(line.Wanted)}</TableCell>
|
|
131
|
+
<TableCell align="right">{count(line.Sold)}</TableCell>
|
|
132
|
+
<TableCell align="right">{count(line.PouredAway)}</TableCell>
|
|
133
|
+
<TableCell align="right">{count(line.TurnedAway)}</TableCell>
|
|
134
|
+
</TableRow>
|
|
135
|
+
);
|
|
136
|
+
})}
|
|
137
|
+
</TableBody>
|
|
138
|
+
</Table>
|
|
139
|
+
</Box>
|
|
140
|
+
</Section>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display helpers shared by the panels.
|
|
3
|
+
*
|
|
4
|
+
* Money is held in pence everywhere, so the sums stay in whole numbers and
|
|
5
|
+
* nothing accumulates a rounding error over ten days. It becomes pounds here,
|
|
6
|
+
* at the edge, and nowhere else.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const money = (symbol: string, pence: number) =>
|
|
10
|
+
`${pence < 0 ? '-' : ''}${symbol}${(Math.abs(pence) / 100).toFixed(2)}`;
|
|
11
|
+
|
|
12
|
+
export const count = (value: number) => Math.round(value).toLocaleString();
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
import { initReactI18next } from 'react-i18next';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Every string on the surface lives here, including the ones nobody expects to
|
|
6
|
+
* translate. A game that reaches a second locale by hunting for hard-coded
|
|
7
|
+
* strings finds about two thirds of them.
|
|
8
|
+
*
|
|
9
|
+
* The host tells the surface which locale the session is in, and `main.tsx`
|
|
10
|
+
* hands that to i18next. Adding a locale is a key in `resources` plus an entry
|
|
11
|
+
* in `supportedLocales` in the manifest.
|
|
12
|
+
*/
|
|
13
|
+
const resources = {
|
|
14
|
+
en: {
|
|
15
|
+
translation: {
|
|
16
|
+
shell: {
|
|
17
|
+
untitledTeam: 'Your stall',
|
|
18
|
+
looksAfter: 'You look after the {{item}}',
|
|
19
|
+
day: 'Day {{day}}',
|
|
20
|
+
processing: 'The day is being played out',
|
|
21
|
+
briefing: 'Notice',
|
|
22
|
+
writeFailed: 'That did not reach the stall. Try again.',
|
|
23
|
+
weekOver: 'The week is over. Nothing left to open.',
|
|
24
|
+
countedNextDay: 'The day turned as you changed that, so it counts for day {{day}}.',
|
|
25
|
+
},
|
|
26
|
+
weather: {
|
|
27
|
+
sunny: 'Sunny today. It is going to be busy.',
|
|
28
|
+
cloudy: 'Cloudy today. An ordinary sort of day.',
|
|
29
|
+
rainy: 'Rain today. Not many people about.',
|
|
30
|
+
sunnyShort: 'Sunny',
|
|
31
|
+
cloudyShort: 'Cloudy',
|
|
32
|
+
rainyShort: 'Rainy',
|
|
33
|
+
},
|
|
34
|
+
day: {
|
|
35
|
+
making: 'Making {{planned}} of a possible {{capacity}}',
|
|
36
|
+
ready: 'Open up, and day {{day}} plays out',
|
|
37
|
+
tooMany: 'Take something off before you open up',
|
|
38
|
+
nothing: 'Decide what to make first',
|
|
39
|
+
open: 'Open the stall',
|
|
40
|
+
},
|
|
41
|
+
today: {
|
|
42
|
+
title: 'What shall we make today?',
|
|
43
|
+
capacity: '{{planned}} of {{capacity}}',
|
|
44
|
+
room: 'Room for {{room}} more. The stall can make {{capacity}} things in a day.',
|
|
45
|
+
over: 'That is {{over}} more than the stall can make. Somebody needs to take some off.',
|
|
46
|
+
observing: 'You are watching this stall rather than working on it.',
|
|
47
|
+
},
|
|
48
|
+
make: {
|
|
49
|
+
yours: 'Yours',
|
|
50
|
+
line: '{{price}} each, costs {{cost}} to make. About {{expected}} people will want one today.',
|
|
51
|
+
more: 'Make more {{name}}',
|
|
52
|
+
fewer: 'Make less {{name}}',
|
|
53
|
+
},
|
|
54
|
+
yesterday: {
|
|
55
|
+
title: 'How yesterday went',
|
|
56
|
+
on: 'Day {{day}}, {{weather}}',
|
|
57
|
+
none: 'Nothing sold yet',
|
|
58
|
+
firstDay: 'Set your numbers, and the stall opens when the facilitator says so.',
|
|
59
|
+
item: 'Item',
|
|
60
|
+
made: 'Made',
|
|
61
|
+
wanted: 'Wanted',
|
|
62
|
+
sold: 'Sold',
|
|
63
|
+
pouredAway: 'Poured away',
|
|
64
|
+
turnedAway: 'Turned away',
|
|
65
|
+
takings: 'Took',
|
|
66
|
+
spent: 'Spent',
|
|
67
|
+
profit: 'Made',
|
|
68
|
+
purse: 'In the jar',
|
|
69
|
+
},
|
|
70
|
+
history: {
|
|
71
|
+
title: 'Every day so far',
|
|
72
|
+
hint: 'What you made, what people wanted, and what was left.',
|
|
73
|
+
day: 'Day',
|
|
74
|
+
weather: 'Weather',
|
|
75
|
+
},
|
|
76
|
+
theme: {
|
|
77
|
+
label: 'Light or dark',
|
|
78
|
+
light: 'Light',
|
|
79
|
+
dark: 'Dark',
|
|
80
|
+
system: 'Match my device',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
i18n.use(initReactI18next).init({
|
|
87
|
+
resources,
|
|
88
|
+
lng: 'en',
|
|
89
|
+
fallbackLng: 'en',
|
|
90
|
+
interpolation: {
|
|
91
|
+
escapeValue: false,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export default i18n;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import createCache from '@emotion/cache';
|
|
2
|
+
import { CacheProvider } from '@emotion/react';
|
|
3
|
+
import '@fontsource-variable/roboto-mono';
|
|
4
|
+
import '@fontsource-variable/work-sans';
|
|
5
|
+
import rtlPlugin from '@mui/stylis-plugin-rtl';
|
|
6
|
+
import { UserInterfaceWrapper, useUserInterfaceLocale } from '@seriousgaming/sdk/ui';
|
|
7
|
+
|
|
8
|
+
import './i18n';
|
|
9
|
+
import i18n from 'i18next';
|
|
10
|
+
import React, { StrictMode, useEffect } from 'react';
|
|
11
|
+
import { createRoot } from 'react-dom/client';
|
|
12
|
+
import { prefixer } from 'stylis';
|
|
13
|
+
|
|
14
|
+
import App from './App';
|
|
15
|
+
|
|
16
|
+
// Create RTL cache
|
|
17
|
+
const cacheRtl = createCache({
|
|
18
|
+
key: 'muirtl',
|
|
19
|
+
stylisPlugins: [prefixer, rtlPlugin],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const cacheLtr = createCache({
|
|
23
|
+
key: 'muiltr',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Wrapper component to sync locale with i18next and setup RTL
|
|
27
|
+
function RootWrapper({ children }: { children: React.ReactNode }) {
|
|
28
|
+
const { locale } = useUserInterfaceLocale();
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const i18nextLocale = locale.split('-')[0];
|
|
32
|
+
i18n.changeLanguage(i18nextLocale);
|
|
33
|
+
}, [locale]);
|
|
34
|
+
|
|
35
|
+
const isRtl = i18n.dir(locale.split('-')[0]) === 'rtl';
|
|
36
|
+
|
|
37
|
+
return <CacheProvider value={isRtl ? cacheRtl : cacheLtr}>{children}</CacheProvider>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
createRoot(document.getElementById('root')!).render(
|
|
41
|
+
<StrictMode>
|
|
42
|
+
<UserInterfaceWrapper>
|
|
43
|
+
<RootWrapper>
|
|
44
|
+
<App />
|
|
45
|
+
</RootWrapper>
|
|
46
|
+
</UserInterfaceWrapper>
|
|
47
|
+
</StrictMode>
|
|
48
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Context, MenuItem, Settings } from '../types';
|
|
2
|
+
|
|
3
|
+
export type MenuRow = { id: string; item: MenuItem };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The menu, in the order the game gave it.
|
|
7
|
+
*
|
|
8
|
+
* Every screen lists the items the same way round. Sorting on a declared
|
|
9
|
+
* `Order` rather than trusting object key order means a menu that arrives from
|
|
10
|
+
* a scenario, or through JSON, still reads the same.
|
|
11
|
+
*/
|
|
12
|
+
export const menuOf = (menu: Settings['menu']): MenuRow[] =>
|
|
13
|
+
Object.entries(menu.Menu)
|
|
14
|
+
.map(([id, item]) => ({ id, item }))
|
|
15
|
+
.sort((a, b) => a.item.Order - b.item.Order);
|
|
16
|
+
|
|
17
|
+
export const totalPlanned = (make: Context['plan']['Make']) =>
|
|
18
|
+
Object.values(make).reduce((sum, value) => sum + value, 0);
|