@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,391 @@
|
|
|
1
|
+
# Common patterns
|
|
2
|
+
|
|
3
|
+
Three patterns for an engine's state and logic, each built up from the CLI commands that create its resources: a stock level that never goes negative, revenue computed in a second step, and a production loop split into one step per concern. The second builds on the first; the third stands alone.
|
|
4
|
+
|
|
5
|
+
For the shape of a whole game, start from the starter game `new package` gives you: [A tour of the starter game](./starter-game.md) walks through it, and [Design state for concurrent writes](../recipes/design-state-for-concurrent-writes.md) states the rules it follows on their own.
|
|
6
|
+
|
|
7
|
+
## Table of contents
|
|
8
|
+
|
|
9
|
+
- [Basic inventory system](#basic-inventory-system)
|
|
10
|
+
- [Pricing and revenue](#pricing-and-revenue)
|
|
11
|
+
- [Complex state management](#complex-state-management)
|
|
12
|
+
|
|
13
|
+
## Basic inventory system
|
|
14
|
+
|
|
15
|
+
A simple system that tracks stock and prevents negative inventory.
|
|
16
|
+
|
|
17
|
+
**Capability:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm run sdk new capability '{
|
|
21
|
+
"definition": {
|
|
22
|
+
"identifier": "inventory",
|
|
23
|
+
"variablesSchema": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"Stock": { "type": "number", "default": 100, "minimum": 0 },
|
|
27
|
+
"ReorderPoint": { "type": "number", "default": 20 }
|
|
28
|
+
},
|
|
29
|
+
"required": ["Stock", "ReorderPoint"],
|
|
30
|
+
"additionalProperties": false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Engine:**
|
|
37
|
+
|
|
38
|
+
A game holds one engine, in `engine/`, so it carries no identifier of its own. Its parameters go in a settings resource it lists in `settings`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm run sdk new settings '{
|
|
42
|
+
"definition": {
|
|
43
|
+
"identifier": "engine",
|
|
44
|
+
"settingsSchema": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"DailySales": { "type": "number", "default": 5 }
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}'
|
|
53
|
+
|
|
54
|
+
pnpm run sdk new engine '{
|
|
55
|
+
"definition": {
|
|
56
|
+
"writes": ["inventory"],
|
|
57
|
+
"settings": ["engine"]
|
|
58
|
+
},
|
|
59
|
+
"logic": {
|
|
60
|
+
"directory": ".",
|
|
61
|
+
"entrypoint": "logic.ts"
|
|
62
|
+
},
|
|
63
|
+
"rootDirectory": "."
|
|
64
|
+
}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Logic** (`engine/logic.ts`)**:**
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { RunFunction } from '@seriousgaming/sdk';
|
|
71
|
+
import type { Context, Settings } from './types';
|
|
72
|
+
|
|
73
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, settings }) => {
|
|
74
|
+
let { Stock } = context.inventory;
|
|
75
|
+
|
|
76
|
+
// Reduce stock
|
|
77
|
+
Stock -= settings.engine.DailySales;
|
|
78
|
+
|
|
79
|
+
// Prevent negative stock
|
|
80
|
+
if (Stock < 0) {
|
|
81
|
+
Stock = 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
capabilities: {
|
|
86
|
+
...context,
|
|
87
|
+
inventory: {
|
|
88
|
+
...context.inventory,
|
|
89
|
+
Stock,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Pricing and revenue
|
|
97
|
+
|
|
98
|
+
Calculate revenue based on inventory and pricing.
|
|
99
|
+
|
|
100
|
+
**Capabilities:**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pnpm run sdk new capability '{
|
|
104
|
+
"definition": {
|
|
105
|
+
"identifier": "pricing",
|
|
106
|
+
"variablesSchema": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"properties": {
|
|
109
|
+
"Price": { "type": "number", "default": 0 },
|
|
110
|
+
"PriceHistory": { "type": "array", "default": [] }
|
|
111
|
+
},
|
|
112
|
+
"required": ["Price", "PriceHistory"],
|
|
113
|
+
"additionalProperties": false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}'
|
|
117
|
+
|
|
118
|
+
pnpm run sdk new capability '{
|
|
119
|
+
"definition": {
|
|
120
|
+
"identifier": "revenue",
|
|
121
|
+
"variablesSchema": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"properties": {
|
|
124
|
+
"TotalRevenue": { "type": "number", "default": 0 },
|
|
125
|
+
"ThisTickRevenue": { "type": "number", "default": 0 }
|
|
126
|
+
},
|
|
127
|
+
"required": ["TotalRevenue", "ThisTickRevenue"],
|
|
128
|
+
"additionalProperties": false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}'
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Engine:**
|
|
135
|
+
|
|
136
|
+
There is no second engine to create: revenue is another step of the one engine the game already holds. Add the conversion rate to its settings, and update its declarations so it reads the new capabilities and writes `revenue`:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
pnpm run sdk update settings engine '{
|
|
140
|
+
"definition": {
|
|
141
|
+
"identifier": "engine",
|
|
142
|
+
"settingsSchema": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"properties": {
|
|
145
|
+
"DailySales": { "type": "number", "default": 5 },
|
|
146
|
+
"ConversionRate": { "type": "number", "default": 0.8 }
|
|
147
|
+
},
|
|
148
|
+
"additionalProperties": false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}'
|
|
152
|
+
|
|
153
|
+
pnpm run sdk update engine '{
|
|
154
|
+
"definition": {
|
|
155
|
+
"reads": ["pricing"],
|
|
156
|
+
"writes": ["inventory", "revenue"],
|
|
157
|
+
"settings": ["engine"]
|
|
158
|
+
},
|
|
159
|
+
"logic": {
|
|
160
|
+
"directory": ".",
|
|
161
|
+
"entrypoint": "logic.ts"
|
|
162
|
+
},
|
|
163
|
+
"rootDirectory": "."
|
|
164
|
+
}'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Logic:** move the stock calculation into a step file of its own, add the revenue step beside it, and let `logic.ts` call them in order.
|
|
168
|
+
|
|
169
|
+
`engine/inventory.ts`:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import type { StepFunction } from '@seriousgaming/sdk';
|
|
173
|
+
import type { Context, Settings } from './types';
|
|
174
|
+
|
|
175
|
+
export const runInventory: StepFunction<Context, Settings> = async ({ context, settings }) => {
|
|
176
|
+
const Stock = Math.max(0, context.inventory.Stock - settings.engine.DailySales);
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
...context,
|
|
180
|
+
inventory: {
|
|
181
|
+
...context.inventory,
|
|
182
|
+
Stock,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`engine/sales.ts`:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import type { StepFunction } from '@seriousgaming/sdk';
|
|
192
|
+
import type { Context, Settings } from './types';
|
|
193
|
+
|
|
194
|
+
export const runSales: StepFunction<Context, Settings> = async ({ context, settings }) => {
|
|
195
|
+
const { Stock } = context.inventory;
|
|
196
|
+
const { Price } = context.pricing;
|
|
197
|
+
const { TotalRevenue } = context.revenue;
|
|
198
|
+
|
|
199
|
+
// Calculate potential sales
|
|
200
|
+
const potentialSales = Stock * Price;
|
|
201
|
+
|
|
202
|
+
// Apply conversion rate
|
|
203
|
+
const actualRevenue = potentialSales * settings.engine.ConversionRate;
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
...context,
|
|
207
|
+
revenue: {
|
|
208
|
+
...context.revenue,
|
|
209
|
+
TotalRevenue: TotalRevenue + actualRevenue,
|
|
210
|
+
ThisTickRevenue: actualRevenue,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`engine/logic.ts`:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { RunFunction } from '@seriousgaming/sdk';
|
|
220
|
+
import { runInventory } from './inventory';
|
|
221
|
+
import { runSales } from './sales';
|
|
222
|
+
import type { Context, Settings } from './types';
|
|
223
|
+
|
|
224
|
+
export const Run: RunFunction<Context, Settings> = async (args) => {
|
|
225
|
+
let context = args.context;
|
|
226
|
+
context = await runInventory({ ...args, context });
|
|
227
|
+
context = await runSales({ ...args, context });
|
|
228
|
+
return { capabilities: context };
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Each step is a `StepFunction`: it takes the context the previous one returned and returns the next one, flat, so the order of the calls is the order the simulation runs in, visible in one place. Only the exported `Run` wraps the final context in `{ capabilities }`.
|
|
233
|
+
|
|
234
|
+
## Complex state management
|
|
235
|
+
|
|
236
|
+
Managing state across several steps of one engine.
|
|
237
|
+
|
|
238
|
+
**Scenario:** a production system that tracks demand, production, and costs.
|
|
239
|
+
|
|
240
|
+
**Capabilities:**
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Demand capability
|
|
244
|
+
pnpm run sdk new capability '{
|
|
245
|
+
"definition": {
|
|
246
|
+
"identifier": "demand",
|
|
247
|
+
"variablesSchema": {
|
|
248
|
+
"type": "object",
|
|
249
|
+
"properties": {
|
|
250
|
+
"CurrentDemand": { "type": "number", "default": 0 },
|
|
251
|
+
"DemandHistory": { "type": "array", "default": [] }
|
|
252
|
+
},
|
|
253
|
+
"required": ["CurrentDemand", "DemandHistory"],
|
|
254
|
+
"additionalProperties": false
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}'
|
|
258
|
+
|
|
259
|
+
# Production capability
|
|
260
|
+
pnpm run sdk new capability '{
|
|
261
|
+
"definition": {
|
|
262
|
+
"identifier": "production",
|
|
263
|
+
"variablesSchema": {
|
|
264
|
+
"type": "object",
|
|
265
|
+
"properties": {
|
|
266
|
+
"ProductionRate": { "type": "number", "default": 10 },
|
|
267
|
+
"TotalProduced": { "type": "number", "default": 0 }
|
|
268
|
+
},
|
|
269
|
+
"required": ["ProductionRate", "TotalProduced"],
|
|
270
|
+
"additionalProperties": false
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}'
|
|
274
|
+
|
|
275
|
+
# Costs capability
|
|
276
|
+
pnpm run sdk new capability '{
|
|
277
|
+
"definition": {
|
|
278
|
+
"identifier": "costs",
|
|
279
|
+
"variablesSchema": {
|
|
280
|
+
"type": "object",
|
|
281
|
+
"properties": {
|
|
282
|
+
"ProductionCost": { "type": "number", "default": 5 },
|
|
283
|
+
"TotalCosts": { "type": "number", "default": 0 }
|
|
284
|
+
},
|
|
285
|
+
"required": ["ProductionCost", "TotalCosts"],
|
|
286
|
+
"additionalProperties": false
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}'
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The steps below read `BaseDemand`, `DemandVariance`, and `ProductionCost` from `settings/engine/`.
|
|
293
|
+
|
|
294
|
+
**Demand step** (`engine/demand.ts`)**:**
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
export const runDemand: StepFunction<Context, Settings> = async ({ context, settings }) => {
|
|
298
|
+
let demand = settings.engine.BaseDemand;
|
|
299
|
+
|
|
300
|
+
// Demand varies by sine wave for realistic pattern
|
|
301
|
+
const tick = context.demand.DemandHistory.length;
|
|
302
|
+
demand += Math.sin(tick * 0.5) * settings.engine.DemandVariance;
|
|
303
|
+
demand = Math.max(0, demand);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
...context,
|
|
307
|
+
demand: {
|
|
308
|
+
...context.demand,
|
|
309
|
+
CurrentDemand: demand,
|
|
310
|
+
DemandHistory: [...context.demand.DemandHistory, demand],
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Production step** (`engine/production.ts`)**:**
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
export const runProduction: StepFunction<Context, Settings> = async ({ context, settings }) => {
|
|
320
|
+
const { CurrentDemand } = context.demand;
|
|
321
|
+
const { ProductionRate, TotalProduced } = context.production;
|
|
322
|
+
|
|
323
|
+
// Adjust production based on demand
|
|
324
|
+
let adjustedProduction = ProductionRate;
|
|
325
|
+
if (CurrentDemand > ProductionRate) {
|
|
326
|
+
adjustedProduction = ProductionRate * 1.1; // Increase by 10%
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
...context,
|
|
331
|
+
production: {
|
|
332
|
+
...context.production,
|
|
333
|
+
ProductionRate: adjustedProduction,
|
|
334
|
+
TotalProduced: TotalProduced + adjustedProduction,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
};
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Costs step** (`engine/costs.ts`)**:**
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
export const runCosts: StepFunction<Context, Settings> = async ({ context, settings }) => {
|
|
344
|
+
const { ProductionRate } = context.production;
|
|
345
|
+
const { TotalCosts } = context.costs;
|
|
346
|
+
|
|
347
|
+
const costThisTick = ProductionRate * settings.engine.ProductionCost;
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
...context,
|
|
351
|
+
costs: {
|
|
352
|
+
...context.costs,
|
|
353
|
+
ProductionCost: settings.engine.ProductionCost,
|
|
354
|
+
TotalCosts: TotalCosts + costThisTick,
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Ordering** (`engine/logic.ts`)**:**
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { RunFunction } from '@seriousgaming/sdk';
|
|
364
|
+
import { runCosts } from './costs';
|
|
365
|
+
import { runDemand } from './demand';
|
|
366
|
+
import { runProduction } from './production';
|
|
367
|
+
import type { Context, Settings } from './types';
|
|
368
|
+
|
|
369
|
+
export const Run: RunFunction<Context, Settings> = async (args) => {
|
|
370
|
+
let context = args.context;
|
|
371
|
+
context = await runDemand({ ...args, context });
|
|
372
|
+
context = await runProduction({ ...args, context });
|
|
373
|
+
context = await runCosts({ ...args, context });
|
|
374
|
+
return { capabilities: context };
|
|
375
|
+
};
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Best practices summary
|
|
379
|
+
|
|
380
|
+
1. **Separate concerns**: one step per responsibility
|
|
381
|
+
2. **Clear dependencies**: declare what you need explicitly
|
|
382
|
+
3. **Immutable updates**: always use spread operators
|
|
383
|
+
4. **Type safety**: use generated types
|
|
384
|
+
5. **Test logic**: write unit tests
|
|
385
|
+
6. **Document code**: comments for non-obvious logic
|
|
386
|
+
|
|
387
|
+
## Next steps
|
|
388
|
+
|
|
389
|
+
- [Creating engines](./engine.md): the reference for `Init`, `Run`, steps and the engine's private state
|
|
390
|
+
- [Creating capabilities](./capabilities.md): scopes, lifetimes and typing a capability's variables
|
|
391
|
+
- [Engine testing](./engine-testing.md): play a few ticks of a pattern in-process before building a UI on it
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Building presentations
|
|
2
|
+
|
|
3
|
+
Ship the facilitator's slide deck as part of the game: an introduction, the briefing, a divider before each block of rounds, the debriefs and the results, localised, aware of the scenario, live with the session's results, and exportable for anyone who wants to take it away and edit it.
|
|
4
|
+
|
|
5
|
+
**Keywords:** presentation, slides, slide deck, deck, briefing, debrief, results, PowerPoint, PDF, PNG, export, usePresentation, PresentationWrapper, @seriousgaming/deck, Deck, Slide, Step
|
|
6
|
+
|
|
7
|
+
## What you'll learn
|
|
8
|
+
|
|
9
|
+
- What a presentation is, and what a runtime does with it
|
|
10
|
+
- How a deck reads the game and the session, and why `session` can be `null`
|
|
11
|
+
- How to build slides with `Deck`, `Slide`, `Step` and the building blocks
|
|
12
|
+
- How a deck remembers where the facilitator was
|
|
13
|
+
- What export produces, and the habits that keep an export faithful
|
|
14
|
+
|
|
15
|
+
## What is a presentation?
|
|
16
|
+
|
|
17
|
+
A presentation is the slides a facilitator shows the room. A game holds at most one, in the `presentation/` slot. It is an ordinary UI bundle of the game's own, so the runtime renders it without knowing what a slide is: navigation, animation, layout and export all happen inside the frame.
|
|
18
|
+
|
|
19
|
+
Every runtime offers it, so a game can rely on its deck being there for the briefing and the debrief. Three things set it apart from the other surfaces:
|
|
20
|
+
|
|
21
|
+
- **Read-only.** It declares `reads` and `settings`, and no `writes`, `uses` or `endpoints`. The host refuses anything it sends except a read of past rounds
|
|
22
|
+
- **Facilitator only.** It is never shown on a participant's or an observer's device. The room sees it because the facilitator shares or projects their screen
|
|
23
|
+
- **Shown with or without a session.** It is the one surface a runtime shows with no session at all: from a game's page in a library, while a facilitator rehearses, or to export it before the day
|
|
24
|
+
|
|
25
|
+
## Creating one
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm run sdk new presentation '{
|
|
29
|
+
"definition": {
|
|
30
|
+
"reads": ["results"],
|
|
31
|
+
"settings": ["engine"]
|
|
32
|
+
},
|
|
33
|
+
"rootDirectory": ".",
|
|
34
|
+
"ui": {
|
|
35
|
+
"entrypoint": "index.html"
|
|
36
|
+
}
|
|
37
|
+
}'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The slot is the presentation's identifier, so `update presentation` and `delete presentation` take none. The scaffold depends on `@seriousgaming/deck`, the SDK's deck library, which a newly scaffolded package already lists. See [Create a presentation](../recipes/create-presentation.md) for the step-by-step version.
|
|
41
|
+
|
|
42
|
+
The deck library is optional. A presentation is a UI bundle like any other, rendered under `PresentationWrapper`, so a deck written with another library, or none, works the same way. The rest of this guide assumes the SDK's own.
|
|
43
|
+
|
|
44
|
+
## Reading the game and the session
|
|
45
|
+
|
|
46
|
+
`usePresentation()` gives a deck everything it can see:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { usePresentation } from '@seriousgaming/sdk/ui';
|
|
50
|
+
|
|
51
|
+
import type { Settings, SharedContext, TeamContext } from '../types';
|
|
52
|
+
|
|
53
|
+
const { game, session, settings, theme, locale, getPastState, getAssetUrl } = usePresentation<
|
|
54
|
+
Settings,
|
|
55
|
+
TeamContext,
|
|
56
|
+
SharedContext
|
|
57
|
+
>();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Field | What it holds |
|
|
61
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `game` | `name` in the current locale, `maxTicks` (or `null` when the engine or tick controller decides), `mode`, and `scenarioName` (or `null`). Present with or without a session |
|
|
63
|
+
| `session` | `null` with no session. Otherwise its `id`, `name`, `tick` (the highest any team has reached), `data` (the shared store and every team, narrowed to `reads`) and `teams` |
|
|
64
|
+
| `settings` | The settings resources the presentation declares, resolved for the scenario, and for the session when there is one |
|
|
65
|
+
| `theme` | The host's theme, to map onto the deck's colours |
|
|
66
|
+
| `locale` | The locale to render in |
|
|
67
|
+
| `getPastState` | Reads past rounds, as the session UI does, with `teams` defaulting to every team. Rejects when there is no session |
|
|
68
|
+
| `getAssetUrl` | Turns an `asset:`, `shared:` or `blob:` reference into a URL |
|
|
69
|
+
|
|
70
|
+
A presentation sees every team: it is the facilitator's view of the room.
|
|
71
|
+
|
|
72
|
+
The shared hooks work in a deck too: `useAssetUrl` and `useAsset` for assets, and `usePastState` in place of calling `getPastState` from an effect (it rejects into `error` when there is no session). `PresentationWrapper` sets the document's `lang`, `dir` and `color-scheme` from the locale and theme, as every wrapper does.
|
|
73
|
+
|
|
74
|
+
### When `session` is `null`
|
|
75
|
+
|
|
76
|
+
A deck must render with `session: null`. Wherever a slide would show results, show sample or placeholder figures, and label them as such on the slide so nobody mistakes them for the room's own. One hook that returns either the session's figures or a sample set flagged `live: false` keeps the branch in one place, and each results slide adds a note when `live` is false:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
export function useResults(): Results {
|
|
80
|
+
const { session } = usePresentation<Settings, TeamContext>();
|
|
81
|
+
if (!session || session.completedTicks < 1) return SAMPLE; // { live: false, ... }
|
|
82
|
+
return {
|
|
83
|
+
live: true,
|
|
84
|
+
teams: session.teams.map((team) => ({
|
|
85
|
+
name: team.name,
|
|
86
|
+
profit: session.data.teams[team.teamId]?.context.profit?.Profit ?? 0,
|
|
87
|
+
})),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Varying by scenario
|
|
93
|
+
|
|
94
|
+
Branch on settings, scenario assets and `game.maxTicks`, never on `game.scenarioName`. A facilitator who saves their own scenario from one of yours gets a new identity, so a deck that branched on the name would show the wrong slides to exactly the facilitators who customised. `scenarioName` is for display.
|
|
95
|
+
|
|
96
|
+
## Slides
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { Bullets, Deck, Heading, Slide, Step, Text } from '@seriousgaming/deck';
|
|
100
|
+
|
|
101
|
+
<Deck
|
|
102
|
+
title={game.name}
|
|
103
|
+
lang={locale.locale}
|
|
104
|
+
rememberAs={session ? `session:${session.id}` : 'rehearsal'}
|
|
105
|
+
>
|
|
106
|
+
<Slide key="title">
|
|
107
|
+
<Heading level={1}>{game.name}</Heading>
|
|
108
|
+
</Slide>
|
|
109
|
+
<Slide key="agenda" transition="slide" notes="Reveal each point in turn.">
|
|
110
|
+
<Heading>{t('agendaHeading')}</Heading>
|
|
111
|
+
<Bullets stepped items={[t('intro'), t('play'), t('review')]} />
|
|
112
|
+
<Step at={4} animation="zoom">
|
|
113
|
+
<Text>{t('letsBegin')}</Text>
|
|
114
|
+
</Step>
|
|
115
|
+
</Slide>
|
|
116
|
+
</Deck>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- **`Deck`** shows one slide at a time, scaled to fit. Every direct child is one slide (arrays and fragments are flattened), so a component of your own counts as a slide only if it renders a single `Slide`
|
|
120
|
+
- **`Slide`** lays its content out on a fixed 1920 by 1080 canvas. Position things however you like inside it. It takes `transition` (`none`, `fade`, `slide`, `zoom` or `rise`; the deck's own `transition` by default), `notes`, `background` (any CSS background) and `id`
|
|
121
|
+
- **`Step`** is content revealed by a click: `at={1}` for the first, `at={2}` for the second, and steps may share a number. A hidden step keeps its space, so a slide never reflows as it builds. Its `animation` is `fade`, `fade-up` (the default), `fade-left`, `zoom`, `blur` or `none`
|
|
122
|
+
- **`useStepVisible(at)`** builds a custom animation with clicks without wrapping it in a `Step`
|
|
123
|
+
|
|
124
|
+
The facilitator moves with the keyboard, a clicker, a click or a swipe. Right, down, space, page down and enter advance; left, up, page up and backspace go back; home and end jump; F toggles fullscreen and O the overview. In a right-to-left deck the arrows swap. Going back lands on the previous slide fully built.
|
|
125
|
+
|
|
126
|
+
`Deck` also takes `dir`, `lang`, `brand` (the deck's colours and fonts), `controls` and `clickToAdvance` (both on by default), `exportFormats`, and `onSlideChange`.
|
|
127
|
+
|
|
128
|
+
### Building blocks
|
|
129
|
+
|
|
130
|
+
| Block | Use it for | In PowerPoint |
|
|
131
|
+
| --------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
132
|
+
| `Heading` | A slide title, `level` 1 to 3 | Editable text |
|
|
133
|
+
| `Text` | Body copy, with `variant` `body`, `lead` or `caption` | Editable text |
|
|
134
|
+
| `Bullets` | A list, `ordered` or not, revealed one item per click with `stepped` | An editable list |
|
|
135
|
+
| `Table` | `columns` and `rows`, with an `align` per column | An editable table |
|
|
136
|
+
| `Image` | A picture. `fit="contain"` (the default) or `fit="cover"` to fill and crop | A movable picture with `contain`; part of the slide's picture with `cover` |
|
|
137
|
+
| `Chart` | A `bar` or `line` chart from `categories` and `series`, drawn in the deck's colours | Part of the slide's picture |
|
|
138
|
+
|
|
139
|
+
Everything else on a slide, your own components included, exports as part of the slide's picture. To make your own element export as editable text, spread `nativeText` onto it: `<div {...nativeText}>...</div>`.
|
|
140
|
+
|
|
141
|
+
### Colours
|
|
142
|
+
|
|
143
|
+
The deck draws in its own `brand`, which a game maps from the host's theme in a line or two, so the slides pick up the same primary and secondary as every other surface:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const brand = {
|
|
147
|
+
colors: {
|
|
148
|
+
...defaultBrand.colors,
|
|
149
|
+
primary: theme.light.primary,
|
|
150
|
+
secondary: theme.light.secondary,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Use the light palette whatever mode the facilitator's screen is in: slides are made to be projected, and a projector washes out a dark slide. `useBrand()` gives a custom component the resolved colours, and the CSS variables `--deck-primary`, `--deck-secondary` and `--deck-muted` reach anything inside a slide.
|
|
156
|
+
|
|
157
|
+
## Coming back to where you were
|
|
158
|
+
|
|
159
|
+
`rememberAs` keeps the facilitator's place in the browser's own storage, so a reload, a closed laptop lid or a break comes back to the same slide and step:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
<Deck rememberAs={session ? `session:${session.id}` : 'rehearsal'} startAt="title">
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- Key it by session, so each session keeps its own place. `session.id` is stable for the session's life and unique to it
|
|
166
|
+
- The deck remembers a slide by its name (its `id`, or its React `key`) and falls back to its number, so a slide that a setting adds or removes does not shift the facilitator onto the wrong one. Give every slide a key
|
|
167
|
+
- `startAt` is where to open when nothing is remembered: a slide's name or number. Compute it from where the room has got to, for instance `startAt={session && session.completedTicks > 0 ? 'results' : 'title'}`
|
|
168
|
+
|
|
169
|
+
The position lives in one browser on one device. A facilitator who moves to another laptop starts at `startAt`.
|
|
170
|
+
|
|
171
|
+
## Export
|
|
172
|
+
|
|
173
|
+
The controls offer the whole deck as a PDF (through the browser's print dialogue), as a PowerPoint file, and the current slide as a PNG. The browser's own print shortcut gives the same PDF. Every export renders each slide still and fully built.
|
|
174
|
+
|
|
175
|
+
In PowerPoint, **words are editable and visuals are pictures**. Headings, text, bullets and tables become native objects a facilitator can edit, and `notes` land in the notes pane. Charts, custom components and anything else become part of the slide's picture, so the export always matches the screen. An export is the facilitator's own file once it is downloaded: nothing about the game changes, and the game itself is never edited in place.
|
|
176
|
+
|
|
177
|
+
## Habits that keep an export faithful
|
|
178
|
+
|
|
179
|
+
- **Use the building blocks for anything a facilitator might edit.** Text drawn by your own component exports as a picture
|
|
180
|
+
- **Render with `session === null`.** Every slide makes sense with no session, and labels sample figures as samples
|
|
181
|
+
- **Branch on settings, not the scenario name.** A facilitator's own scenario has a different name and the same settings
|
|
182
|
+
- **Show finished visuals in static mode.** Print, export and the overview render each slide still. A custom animation reads `useSlide().mode` and draws its end state when it is `'static'`, as in `const shown = useStepVisible(1); const on = mode === 'static' || shown;`
|
|
183
|
+
- **Route every word through i18n.** A deck is shown in the session's language. Set `lang` and `dir` on the `Deck` from `locale`
|
|
184
|
+
- **One element per slide.** Every element in the array is a slide; a wrapper around several is one slide
|
|
185
|
+
|
|
186
|
+
## Testing
|
|
187
|
+
|
|
188
|
+
To see your deck, run `pnpm run sdk dev` and open `/presentation`, or `/presentation?preview` for the deck with no session.
|
|
189
|
+
|
|
190
|
+
## Related
|
|
191
|
+
|
|
192
|
+
- [Create a presentation](../recipes/create-presentation.md): the step-by-step version
|
|
193
|
+
- [Building session UIs](./session.md): the facilitator's other surface
|
|
194
|
+
- [Facilitator guides](./guide.md): the run sheet beside the deck
|
|
195
|
+
- [Internationalisation (i18n)](./internationalization.md): translating the deck's words
|
|
196
|
+
- [Scenarios](./scenarios.md): what a scenario can change, and so what a deck can vary on
|