@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.2

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.
Files changed (391) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +38 -2
  3. package/dist/bundling/logic-exports.d.ts +34 -0
  4. package/dist/bundling/node-builtins.d.ts +21 -0
  5. package/dist/bundling/transpiler.d.ts +7 -0
  6. package/dist/cli.js +27404 -0
  7. package/dist/client/dev-server.d.ts +59 -0
  8. package/dist/client/index.d.ts +28 -0
  9. package/dist/client/stub-services.d.ts +32 -0
  10. package/dist/client/test-session.d.ts +195 -0
  11. package/dist/client.js +16920 -0
  12. package/dist/core/asset-reference-translator.d.ts +46 -0
  13. package/dist/core/clock.d.ts +57 -0
  14. package/dist/core/faults.d.ts +65 -0
  15. package/dist/core/game-settings.d.ts +65 -0
  16. package/dist/core/history.d.ts +89 -0
  17. package/dist/core/listen-with-fallback.d.ts +30 -0
  18. package/dist/core/mutation-queue.d.ts +61 -0
  19. package/dist/core/package-scenario.d.ts +89 -0
  20. package/dist/core/participant-registry.d.ts +109 -0
  21. package/dist/core/pending-changes.d.ts +14 -0
  22. package/dist/core/protocol.d.ts +83 -0
  23. package/dist/core/scenario-drafts.d.ts +32 -0
  24. package/dist/core/session-endpoints.d.ts +108 -0
  25. package/dist/core/session-gates.d.ts +75 -0
  26. package/dist/core/session-host.d.ts +94 -0
  27. package/dist/core/session-refusals.d.ts +56 -0
  28. package/dist/core/session-rewind.d.ts +32 -0
  29. package/dist/core/session-roster.d.ts +128 -0
  30. package/dist/core/session-surfaces.d.ts +32 -0
  31. package/dist/core/session-ticks.d.ts +18 -0
  32. package/dist/core/session-timers.d.ts +84 -0
  33. package/dist/core/session-writes.d.ts +40 -0
  34. package/dist/core/session.d.ts +385 -0
  35. package/dist/core/settings-locks.d.ts +22 -0
  36. package/dist/core/team-names.d.ts +52 -0
  37. package/dist/core/team-state-store.d.ts +218 -0
  38. package/dist/core/tick-timer.d.ts +75 -0
  39. package/dist/dev/blob-links.d.ts +10 -0
  40. package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
  41. package/dist/dev/console/index.html +12 -0
  42. package/dist/dev/console-path.d.ts +19 -0
  43. package/dist/dev/lockfile.d.ts +124 -0
  44. package/dist/dev/package-watcher.d.ts +21 -0
  45. package/dist/dev/routes.d.ts +67 -0
  46. package/dist/dev/shell.d.ts +260 -0
  47. package/dist/dev/surfaces.d.ts +41 -0
  48. package/dist/dev/tools-identity.d.ts +23 -0
  49. package/dist/dev/view/batch.d.ts +172 -0
  50. package/dist/dev/view/commands.d.ts +75 -0
  51. package/dist/dev/view/route.d.ts +58 -0
  52. package/dist/dev/view/view.d.ts +78 -0
  53. package/dist/dev/view/viewer.d.ts +32 -0
  54. package/dist/discovery/config.d.ts +9 -0
  55. package/dist/discovery/manifest-paths.d.ts +21 -0
  56. package/dist/discovery/removed-fields.d.ts +90 -0
  57. package/dist/docs/api/deck.md +386 -0
  58. package/dist/docs/api/index.md +67 -0
  59. package/dist/docs/api/sdk-commands.md +286 -0
  60. package/dist/docs/api/sdk-e2e.md +433 -0
  61. package/dist/docs/api/sdk-projection.md +207 -0
  62. package/dist/docs/api/sdk-testing.md +214 -0
  63. package/dist/docs/api/sdk-tools-client.md +560 -0
  64. package/dist/docs/api/sdk-ui-testing.md +231 -0
  65. package/dist/docs/api/sdk-ui.md +1269 -0
  66. package/dist/docs/api/sdk.md +2140 -0
  67. package/dist/docs/changelog-0.x.md +1156 -0
  68. package/dist/docs/changelog.md +241 -0
  69. package/dist/docs/cli-and-dev-server.md +217 -0
  70. package/dist/docs/cli-error-codes.md +156 -0
  71. package/dist/docs/cli-reference.md +2489 -0
  72. package/dist/docs/cli-simulation-testing.md +222 -0
  73. package/dist/docs/concepts/index.md +46 -0
  74. package/dist/docs/concepts/terminology.md +322 -0
  75. package/dist/docs/environment-variables.md +102 -0
  76. package/dist/docs/getting-started.md +145 -0
  77. package/dist/docs/guides/ai-facilitation.md +220 -0
  78. package/dist/docs/guides/capabilities.md +460 -0
  79. package/dist/docs/guides/endpoints.md +335 -0
  80. package/dist/docs/guides/engine-testing.md +477 -0
  81. package/dist/docs/guides/engine.md +527 -0
  82. package/dist/docs/guides/file-handling.md +405 -0
  83. package/dist/docs/guides/games.md +176 -0
  84. package/dist/docs/guides/global-settings.md +94 -0
  85. package/dist/docs/guides/guide.md +123 -0
  86. package/dist/docs/guides/hooks.md +183 -0
  87. package/dist/docs/guides/index.md +51 -0
  88. package/dist/docs/guides/internationalization.md +288 -0
  89. package/dist/docs/guides/llm-integration.md +635 -0
  90. package/dist/docs/guides/multi-team-games.md +586 -0
  91. package/dist/docs/guides/patterns.md +391 -0
  92. package/dist/docs/guides/presentation.md +196 -0
  93. package/dist/docs/guides/production-constraints.md +130 -0
  94. package/dist/docs/guides/scenario-editor.md +143 -0
  95. package/dist/docs/guides/scenarios.md +515 -0
  96. package/dist/docs/guides/session.md +972 -0
  97. package/dist/docs/guides/settings.md +458 -0
  98. package/dist/docs/guides/setup.md +193 -0
  99. package/dist/docs/guides/speech-generation.md +233 -0
  100. package/dist/docs/guides/starter-game.md +164 -0
  101. package/dist/docs/guides/testing.md +577 -0
  102. package/dist/docs/guides/tick-controller.md +569 -0
  103. package/dist/docs/guides/type-safety.md +516 -0
  104. package/dist/docs/guides/ui.md +1389 -0
  105. package/dist/docs/guides/upgrading.md +636 -0
  106. package/dist/docs/guides/validators.md +217 -0
  107. package/dist/docs/guides/waiting-room.md +244 -0
  108. package/dist/docs/index.md +41 -0
  109. package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
  110. package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
  111. package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
  112. package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
  113. package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
  114. package/dist/docs/recipes/create-capability.md +66 -0
  115. package/dist/docs/recipes/create-endpoint.md +127 -0
  116. package/dist/docs/recipes/create-engine.md +111 -0
  117. package/dist/docs/recipes/create-game.md +70 -0
  118. package/dist/docs/recipes/create-guide.md +75 -0
  119. package/dist/docs/recipes/create-hook.md +80 -0
  120. package/dist/docs/recipes/create-presentation.md +142 -0
  121. package/dist/docs/recipes/create-scenario-editor.md +102 -0
  122. package/dist/docs/recipes/create-scenario.md +98 -0
  123. package/dist/docs/recipes/create-session-ui.md +116 -0
  124. package/dist/docs/recipes/create-settings.md +88 -0
  125. package/dist/docs/recipes/create-setup.md +115 -0
  126. package/dist/docs/recipes/create-tick-controller.md +65 -0
  127. package/dist/docs/recipes/create-user-interface.md +96 -0
  128. package/dist/docs/recipes/create-validator.md +67 -0
  129. package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
  130. package/dist/docs/recipes/declare-api-uses.md +67 -0
  131. package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
  132. package/dist/docs/recipes/distribute-package.md +80 -0
  133. package/dist/docs/recipes/e2e-testing.md +116 -0
  134. package/dist/docs/recipes/end-game-on-condition.md +154 -0
  135. package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
  136. package/dist/docs/recipes/index.md +86 -0
  137. package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
  138. package/dist/docs/recipes/negotiate-across-teams.md +164 -0
  139. package/dist/docs/recipes/observe-other-teams-state.md +119 -0
  140. package/dist/docs/recipes/publish-a-package.md +139 -0
  141. package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
  142. package/dist/docs/recipes/review-a-past-day.md +73 -0
  143. package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
  144. package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
  145. package/dist/docs/recipes/show-all-teams-participants.md +109 -0
  146. package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
  147. package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
  148. package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
  149. package/dist/docs/recipes/test-user-interface.md +95 -0
  150. package/dist/docs/recipes/test-with-engine.md +115 -0
  151. package/dist/docs/recipes/upload-and-display-files.md +148 -0
  152. package/dist/docs/recipes/use-request-play.md +65 -0
  153. package/dist/docs/recipes/use-request-tick.md +75 -0
  154. package/dist/docs/troubleshooting.md +816 -0
  155. package/dist/execution/blob-inputs.d.ts +57 -0
  156. package/dist/execution/bundle-reload.d.ts +42 -0
  157. package/dist/execution/config-resolver.d.ts +127 -0
  158. package/dist/execution/configuration-builder.d.ts +66 -0
  159. package/dist/execution/context-composition.d.ts +161 -0
  160. package/dist/execution/endpoint-actions.d.ts +9 -0
  161. package/dist/execution/endpoint-payloads.d.ts +32 -0
  162. package/dist/execution/endpoint-runner.d.ts +129 -0
  163. package/dist/execution/engine-state.d.ts +40 -0
  164. package/dist/execution/executor-callbacks.d.ts +56 -0
  165. package/dist/execution/executor.d.ts +436 -0
  166. package/dist/execution/gates.d.ts +45 -0
  167. package/dist/execution/hook-chain.d.ts +25 -0
  168. package/dist/execution/logic-calls.d.ts +190 -0
  169. package/dist/execution/logic-failure.d.ts +84 -0
  170. package/dist/execution/logic-loader.d.ts +54 -0
  171. package/dist/execution/logic-result.d.ts +76 -0
  172. package/dist/execution/tick-controller-call.d.ts +32 -0
  173. package/dist/execution/types.d.ts +48 -0
  174. package/dist/generation/ts-type.d.ts +39 -0
  175. package/dist/guide/locale.d.ts +12 -0
  176. package/dist/guide/video-registry.d.ts +42 -0
  177. package/dist/help-examples.d.ts +125 -0
  178. package/dist/host/adapter.d.ts +67 -0
  179. package/dist/host/host-channel.d.ts +222 -0
  180. package/dist/host/index.d.ts +20 -0
  181. package/dist/host/protocol.d.ts +783 -0
  182. package/dist/host/surfaces.d.ts +42 -0
  183. package/dist/host/theme.d.ts +55 -0
  184. package/dist/host.js +583 -0
  185. package/dist/prompts/index.d.ts +7 -0
  186. package/dist/prompts/schema-builder.d.ts +29 -0
  187. package/dist/prompts/types.d.ts +108 -0
  188. package/dist/prompts/validators.d.ts +32 -0
  189. package/dist/runtime/capability-scope.d.ts +6 -0
  190. package/dist/runtime/context-merge.d.ts +29 -0
  191. package/dist/runtime/context-projection.d.ts +41 -0
  192. package/dist/runtime/guarded-callbacks.d.ts +15 -0
  193. package/dist/runtime/index.d.ts +27 -0
  194. package/dist/runtime/manifest-version.d.ts +55 -0
  195. package/dist/runtime/tick-validation.d.ts +25 -0
  196. package/dist/runtime.js +346 -0
  197. package/dist/schema.json +1546 -0
  198. package/dist/server/vite-plugin.d.ts +46 -0
  199. package/dist/services/tts/client.d.ts +51 -0
  200. package/dist/services/tts/elevenlabs.d.ts +24 -0
  201. package/dist/services/tts/types.d.ts +44 -0
  202. package/dist/storage/asset-resolver.d.ts +49 -0
  203. package/dist/storage/blob-store.d.ts +41 -0
  204. package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
  205. package/dist/templates/endpoint/logic.ts.liquid +25 -0
  206. package/dist/templates/engine/init.json.liquid +7 -0
  207. package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
  208. package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
  209. package/dist/templates/engine/logic.test.ts.liquid +61 -0
  210. package/dist/templates/engine/logic.ts.liquid +32 -0
  211. package/dist/templates/engine/run.json.liquid +7 -0
  212. package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
  213. package/dist/templates/guide/02-preparation.md.liquid +18 -0
  214. package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
  215. package/dist/templates/hook/logic.test.ts.liquid +46 -0
  216. package/dist/templates/hook/logic.ts.liquid +12 -0
  217. package/dist/templates/package/CLAUDE.md.template +338 -0
  218. package/dist/templates/package/DESIGN.md.liquid +144 -0
  219. package/dist/templates/package/README.md.liquid +143 -0
  220. package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
  221. package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
  222. package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
  223. package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
  224. package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
  225. package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
  226. package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
  227. package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
  228. package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
  229. package/dist/templates/package/claude/settings.json.template +16 -0
  230. package/dist/templates/package/gitignore.template +15 -0
  231. package/dist/templates/package/global-setup.ts.template +21 -0
  232. package/dist/templates/package/globals.d.ts.template +2 -0
  233. package/dist/templates/package/oxfmtrc.json.template +14 -0
  234. package/dist/templates/package/oxlintrc.json.template +238 -0
  235. package/dist/templates/package/package.json.liquid +65 -0
  236. package/dist/templates/package/playwright.config.ts.liquid +19 -0
  237. package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
  238. package/dist/templates/package/test-setup.ts.liquid +7 -0
  239. package/dist/templates/package/tsconfig.json.liquid +15 -0
  240. package/dist/templates/package/vite.config.ts.liquid +19 -0
  241. package/dist/templates/package/vitest.config.ts.liquid +25 -0
  242. package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
  243. package/dist/templates/presentation/index.html.liquid +20 -0
  244. package/dist/templates/presentation/src/App.tsx.liquid +49 -0
  245. package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
  246. package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
  247. package/dist/templates/presentation/src/main.tsx.liquid +14 -0
  248. package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
  249. package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
  250. package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
  251. package/dist/templates/session/src/App.test.tsx.liquid +60 -0
  252. package/dist/templates/session/src/App.tsx.liquid +128 -0
  253. package/dist/templates/session/src/i18n.ts.liquid +53 -0
  254. package/dist/templates/session/src/main.tsx.liquid +48 -0
  255. package/dist/templates/session/src/theme.ts.liquid +46 -0
  256. package/dist/templates/settings-editor/index.html.liquid +12 -0
  257. package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
  258. package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
  259. package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
  260. package/dist/templates/setup/src/App.tsx.liquid +102 -0
  261. package/dist/templates/setup/src/i18n.ts.liquid +47 -0
  262. package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
  263. package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
  264. package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
  265. package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
  266. package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
  267. package/dist/templates/starter-game/engine/defaults.ts +81 -0
  268. package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
  269. package/dist/templates/starter-game/engine/logic.test.ts +221 -0
  270. package/dist/templates/starter-game/engine/logic.ts +149 -0
  271. package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
  272. package/dist/templates/starter-game/engine/types.ts +139 -0
  273. package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
  274. package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
  275. package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
  276. package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
  277. package/dist/templates/starter-game/guide/guide.test.ts +108 -0
  278. package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
  279. package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
  280. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
  281. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
  282. package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
  283. package/dist/templates/starter-game/package.json +68 -0
  284. package/dist/templates/starter-game/playwright.config.ts +19 -0
  285. package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
  286. package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
  287. package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
  288. package/dist/templates/starter-game/session/defaults.ts +80 -0
  289. package/dist/templates/starter-game/session/index.html +12 -0
  290. package/dist/templates/starter-game/session/session.sdk.json +11 -0
  291. package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
  292. package/dist/templates/starter-game/session/src/App.tsx +309 -0
  293. package/dist/templates/starter-game/session/src/Section.tsx +37 -0
  294. package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
  295. package/dist/templates/starter-game/session/src/format.ts +8 -0
  296. package/dist/templates/starter-game/session/src/i18n.ts +51 -0
  297. package/dist/templates/starter-game/session/src/main.tsx +48 -0
  298. package/dist/templates/starter-game/session/src/theme.ts +45 -0
  299. package/dist/templates/starter-game/session/types.ts +135 -0
  300. package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
  301. package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
  302. package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
  303. package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
  304. package/dist/templates/starter-game/test-setup.ts +7 -0
  305. package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
  306. package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
  307. package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
  308. package/dist/templates/starter-game/tsconfig.json +19 -0
  309. package/dist/templates/starter-game/ui/defaults.ts +60 -0
  310. package/dist/templates/starter-game/ui/index.html +12 -0
  311. package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
  312. package/dist/templates/starter-game/ui/src/App.tsx +175 -0
  313. package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
  314. package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
  315. package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
  316. package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
  317. package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
  318. package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
  319. package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
  320. package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
  321. package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
  322. package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
  323. package/dist/templates/starter-game/ui/src/format.ts +12 -0
  324. package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
  325. package/dist/templates/starter-game/ui/src/main.tsx +48 -0
  326. package/dist/templates/starter-game/ui/src/menu.ts +18 -0
  327. package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
  328. package/dist/templates/starter-game/ui/src/theme.ts +45 -0
  329. package/dist/templates/starter-game/ui/src/writes.ts +43 -0
  330. package/dist/templates/starter-game/ui/types.ts +137 -0
  331. package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
  332. package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
  333. package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
  334. package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
  335. package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
  336. package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
  337. package/dist/templates/starter-game/vite.config.ts +19 -0
  338. package/dist/templates/starter-game/vitest.config.ts +15 -0
  339. package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
  340. package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
  341. package/dist/templates/starter-game/waiting_room/index.html +12 -0
  342. package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
  343. package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
  344. package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
  345. package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
  346. package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
  347. package/dist/templates/starter-game/waiting_room/types.ts +59 -0
  348. package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
  349. package/dist/templates/tick-controller/logic.ts.liquid +8 -0
  350. package/dist/templates/ui/index.html.liquid +12 -0
  351. package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
  352. package/dist/templates/ui/src/App.tsx.liquid +103 -0
  353. package/dist/templates/ui/src/i18n.ts.liquid +47 -0
  354. package/dist/templates/ui/src/main.tsx.liquid +48 -0
  355. package/dist/templates/ui/src/theme.ts.liquid +46 -0
  356. package/dist/templates/validator/logic.test.ts.liquid +42 -0
  357. package/dist/templates/validator/logic.ts.liquid +21 -0
  358. package/dist/templates/waiting-room/index.html.liquid +12 -0
  359. package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
  360. package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
  361. package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
  362. package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
  363. package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
  364. package/dist/templating/resource-metadata.d.ts +131 -0
  365. package/dist/types.d.ts +14 -0
  366. package/dist/utils/atomic-write.d.ts +40 -0
  367. package/dist/utils/cli-error.d.ts +64 -0
  368. package/dist/utils/document-parser-client.d.ts +20 -0
  369. package/dist/utils/env-loader.d.ts +40 -0
  370. package/dist/utils/error-codes.d.ts +339 -0
  371. package/dist/utils/errors.d.ts +19 -0
  372. package/dist/utils/global-settings.d.ts +65 -0
  373. package/dist/utils/llm-client.d.ts +34 -0
  374. package/dist/utils/logger.d.ts +36 -0
  375. package/dist/utils/output.d.ts +109 -0
  376. package/dist/utils/platform.d.ts +14 -0
  377. package/dist/utils/prompt.d.ts +36 -0
  378. package/dist/utils/real-path.d.ts +6 -0
  379. package/dist/utils/schema-properties.d.ts +35 -0
  380. package/dist/validation/ajv.d.ts +17 -0
  381. package/dist/validation/facilitator-guide-validator.d.ts +27 -0
  382. package/dist/validation/findings.d.ts +40 -0
  383. package/dist/validation/game-constraints.d.ts +10 -0
  384. package/dist/validation/game-validator.d.ts +34 -0
  385. package/dist/validation/package-validation.d.ts +41 -0
  386. package/dist/validation/package-validator.d.ts +19 -0
  387. package/dist/validation/resource-validator.d.ts +79 -0
  388. package/dist/validation/schema-defaults-validator.d.ts +29 -0
  389. package/dist/validation/schema-validation.d.ts +115 -0
  390. package/dist/validation/semver.d.ts +9 -0
  391. package/package.json +123 -3
@@ -0,0 +1,30 @@
1
+ {% raw %}import { renderSettingsEditor } from '@seriousgaming/sdk/ui/testing';
2
+ import { screen, fireEvent } from '@testing-library/react';
3
+ import { describe, it, expect } from 'vitest';
4
+
5
+ import { defaultSettings } from '../defaults';
6
+ import './i18n';
7
+ import App from './App';
8
+
9
+ // renderSettingsEditor supplies the SDK context providers App needs, in place
10
+ // of the iframe channel the real screen uses: no mocking needed.
11
+ const renderApp = () => renderSettingsEditor(<App />, { settings: defaultSettings });
12
+
13
+ describe('App', () => {
14
+ it('renders the welcome message', () => {
15
+ renderApp();
16
+ expect(screen.getByText(/welcome/i)).toBeInTheDocument();
17
+ });
18
+
19
+ it('increments the counter on button click', () => {
20
+ renderApp();
21
+
22
+ expect(screen.getByText(/current value: 0/i)).toBeInTheDocument();
23
+
24
+ // The counter uses local state, so fireEvent.click is appropriate here.
25
+ // For controlled inputs that call updateSettings, use fireEvent.change.
26
+ fireEvent.click(screen.getByRole('button', { name: /increment/i }));
27
+
28
+ expect(screen.getByText(/current value: 1/i)).toBeInTheDocument();
29
+ });
30
+ });{% endraw %}
@@ -0,0 +1,95 @@
1
+ import { Box, Button, CssBaseline, GlobalStyles, ThemeProvider, Typography } from '@mui/material';
2
+ import { useSettingsEditor } from '@seriousgaming/sdk/ui';
3
+ import React, { useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { Settings } from '../types';
7
+ import { useAppTheme } from './theme';
8
+
9
+ /*
10
+ This UI requires the following dependencies:
11
+ - @mui/material
12
+ - @emotion/react
13
+ - @emotion/styled
14
+ - stylis
15
+ - @mui/stylis-plugin-rtl
16
+ - @fontsource-variable/roboto-mono
17
+ - @fontsource-variable/work-sans
18
+ - i18next
19
+ - react-i18next
20
+
21
+ Make sure to install them in your project, e.g.:
22
+ pnpm add @mui/material @emotion/react @emotion/styled stylis @mui/stylis-plugin-rtl @fontsource-variable/roboto-mono @fontsource-variable/work-sans i18next react-i18next
23
+ */
24
+
25
+ /*
26
+ This is the game's scenario editor: where a facilitator or author builds a new
27
+ scenario for the game, at any time and with no session running. The host saves
28
+ what it edits as a scenario; the screen itself has no save button to provide.
29
+ It includes an example counter that uses local component state. To connect it
30
+ to the game's settings:
31
+
32
+ 1. In scenario_editor/scenario_editor.sdk.json, list the settings resources
33
+ this screen edits in `configures`, e.g. "configures": ["demand"]. The
34
+ settings it reads and writes are keyed by those identifiers.
35
+
36
+ 2. In this file (App.tsx), uncomment the lines related to `useSettingsEditor`.
37
+ */
38
+
39
+ /**
40
+ * This surface is embedded in the host's page, so it leaves the ground to the
41
+ * host: a surface painting its own background reads as a panel bolted on rather
42
+ * than part of the page, and never quite matches. Paint cards and panels, which
43
+ * are meant to look raised, not the page.
44
+ */
45
+ const TRANSPARENT_BODY = { body: { backgroundColor: 'transparent' } };
46
+
47
+ function App() {
48
+ // const { settings, updateSettings } = useSettingsEditor<Settings>();
49
+ const { t } = useTranslation();
50
+ const theme = useAppTheme();
51
+ const [localCounter, setLocalCounter] = useState(0);
52
+
53
+ // This is a local state example.
54
+ // To connect to the settings, comment out the local state above and
55
+ // uncomment the `useSettingsEditor` hook and the `counterValue` line below.
56
+ const counterValue = localCounter;
57
+ // const counterValue = settings.demand?.someField;
58
+
59
+ const handleIncrement = () => {
60
+ // This is a local state update example.
61
+ setLocalCounter((c) => c + 1);
62
+
63
+ // To change a setting, uncomment the `updateSettings` call below. Only
64
+ // what changed reaches the host.
65
+ // void updateSettings({
66
+ // demand: {
67
+ // ...settings.demand,
68
+ // someField: (settings.demand?.someField ?? 0) + 1,
69
+ // },
70
+ // });
71
+ };
72
+
73
+ return (
74
+ <ThemeProvider theme={theme}>
75
+ <CssBaseline enableColorScheme />
76
+ <GlobalStyles styles={TRANSPARENT_BODY} />
77
+ <Box>
78
+ <Typography variant="body1">{t('welcomeMessage')}</Typography>
79
+ <Typography variant="body2" color="text.secondary">
80
+ {t('dataExample')}
81
+ </Typography>
82
+ <Typography variant="h6" component="p">
83
+ {t('currentValue', { value: counterValue })}
84
+ </Typography>
85
+ <Box>
86
+ <Button variant="contained" onClick={handleIncrement}>
87
+ {t('updateButton')}
88
+ </Button>
89
+ </Box>
90
+ </Box>
91
+ </ThemeProvider>
92
+ );
93
+ }
94
+
95
+ export default App;
@@ -0,0 +1,44 @@
1
+ import i18n from 'i18next';
2
+ import { initReactI18next } from 'react-i18next';
3
+
4
+ // Translation resources
5
+ const resources = {
6
+ en: {
7
+ translation: {
8
+ greeting: 'Hello, World!',
9
+ welcomeMessage: 'Welcome to the scenario editor for this game.',
10
+ dataExample: 'Choose the settings this scenario plays with.',
11
+ currentValue: 'Current value: {% raw %}{{value}}{% endraw %}',
12
+ updateButton: 'Increment Value',
13
+ },
14
+ },
15
+ it: {
16
+ translation: {
17
+ greeting: 'Ciao, mondo!',
18
+ welcomeMessage: 'Benvenuto nell\'editor di scenari di questo gioco.',
19
+ dataExample: 'Scegli le impostazioni di questo scenario.',
20
+ currentValue: 'Valore corrente: {% raw %}{{value}}{% endraw %}',
21
+ updateButton: 'Incrementa valore',
22
+ },
23
+ },
24
+ ar: {
25
+ translation: {
26
+ greeting: 'أهلاً بالعالم!',
27
+ welcomeMessage: 'مرحبًا بك في محرر السيناريوهات لهذه اللعبة.',
28
+ dataExample: 'اختر إعدادات هذا السيناريو.',
29
+ currentValue: 'القيمة الحالية: {% raw %}{{value}}{% endraw %}',
30
+ updateButton: 'زيادة القيمة',
31
+ },
32
+ },
33
+ };
34
+
35
+ i18n.use(initReactI18next).init({
36
+ resources,
37
+ lng: 'en', // default language
38
+ fallbackLng: 'en',
39
+ interpolation: {
40
+ escapeValue: false, // React already escapes
41
+ },
42
+ });
43
+
44
+ export default i18n;
@@ -0,0 +1,60 @@
1
+ {% raw %}import {
2
+ createMockSharedState,
3
+ createMockTeamState,
4
+ renderSessionUserInterface,
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 { SessionData, SharedContext, TeamContext } from '../types';
10
+ import './i18n';
11
+ import App from './App';
12
+
13
+ const mockTimer = {
14
+ enabled: false,
15
+ paused: false,
16
+ nextTickAt: null,
17
+ intervalMs: null,
18
+ pausedAt: null,
19
+ pausedMs: 0,
20
+ scheduleStartedAt: null,
21
+ extendedMs: 0,
22
+ };
23
+
24
+ const teams = [
25
+ { teamId: 'team-1', index: 0, name: 'Team 1' },
26
+ { teamId: 'team-2', index: 1, name: 'Team 2' },
27
+ ];
28
+
29
+ // `createMockTeamState` and `createMockSharedState` fill in the fields a test
30
+ // rarely cares about, `revision` included. Typed with the generated contexts,
31
+ // a team fixture can hold only that team's own capabilities: one carrying a
32
+ // shared capability fails to compile, as the surface would never see it there.
33
+ const sessionData: SessionData = {
34
+ shared: createMockSharedState<SharedContext>(),
35
+ teams: {
36
+ 'team-1': createMockTeamState<TeamContext>({ completedTicks: 3, timer: mockTimer }),
37
+ 'team-2': createMockTeamState<TeamContext>({ completedTicks: 5, timer: mockTimer }),
38
+ },
39
+ };
40
+
41
+ describe('App', () => {
42
+ it('lists every team with its tick', () => {
43
+ renderSessionUserInterface<SessionData>(<App />, { sessionData, teams });
44
+ expect(screen.getByText('Team 1')).toBeInTheDocument();
45
+ expect(screen.getByText('Team 2')).toBeInTheDocument();
46
+ expect(screen.getByText('3')).toBeInTheDocument();
47
+ expect(screen.getByText('5')).toBeInTheDocument();
48
+ });
49
+
50
+ it('drives the whole session with the control buttons', () => {
51
+ const callbacks = { requestTick: vi.fn(), undoTick: vi.fn(), resetGame: vi.fn() };
52
+ renderSessionUserInterface<SessionData>(<App />, { sessionData, teams, callbacks });
53
+ fireEvent.click(screen.getByRole('button', { name: /next tick/i }));
54
+ fireEvent.click(screen.getByRole('button', { name: /undo/i }));
55
+ fireEvent.click(screen.getByRole('button', { name: /reset/i }));
56
+ expect(callbacks.requestTick).toHaveBeenCalled();
57
+ expect(callbacks.undoTick).toHaveBeenCalled();
58
+ expect(callbacks.resetGame).toHaveBeenCalled();
59
+ });
60
+ });{% endraw %}
@@ -0,0 +1,128 @@
1
+ {% raw %}import {
2
+ useSessionUserInterfaceCallbacks,
3
+ useSessionUserInterfaceData,
4
+ useSessionUserInterfaceTeams,
5
+ type TeamInfo,
6
+ } from '@seriousgaming/sdk/ui';
7
+ import {
8
+ Box,
9
+ Button,
10
+ Card,
11
+ Chip,
12
+ CssBaseline,
13
+ GlobalStyles,
14
+ Stack,
15
+ Table,
16
+ TableBody,
17
+ TableCell,
18
+ TableHead,
19
+ TableRow,
20
+ ThemeProvider,
21
+ Typography,
22
+ } from '@mui/material';
23
+ import { useTranslation } from 'react-i18next';
24
+
25
+ import type { Settings, SessionData, SharedWrites, TeamWrites } from '../types';
26
+ import { useAppTheme } from './theme';
27
+
28
+ // Requires the dependencies that `package new` installs (@mui/material, i18next,
29
+ // and friends). See the user-interfaces guide if any are missing.
30
+
31
+ // Manage-session user interfaces run in the facilitator view and see every
32
+ // team in the session. Declare the capabilities to read in this UI's `reads`
33
+ // array and run `pnpm run sdk generate`: the generated `SessionData` then types
34
+ // `sessionData.teams[teamId].context` with each team's own capabilities, and
35
+ // `sessionData.shared.context` with the shared ones, which arrive once for the
36
+ // session rather than inside every team. `SharedWrites` and `TeamWrites` type
37
+ // the two forms of `setData` against what this UI declares in `writes`.
38
+
39
+ /**
40
+ * This surface is embedded in the host's page, so it leaves the ground to the
41
+ * host: a surface painting its own background reads as a panel bolted on rather
42
+ * than part of the page, and never quite matches. Paint cards and panels, which
43
+ * are meant to look raised, not the page.
44
+ */
45
+ const TRANSPARENT_BODY = { body: { backgroundColor: 'transparent' } };
46
+
47
+ function App() {
48
+ const { sessionData } = useSessionUserInterfaceData<
49
+ SessionData,
50
+ Settings,
51
+ SharedWrites,
52
+ TeamWrites
53
+ >();
54
+ const teams = useSessionUserInterfaceTeams();
55
+ const { requestTick, undoTick, resetGame } = useSessionUserInterfaceCallbacks();
56
+ const { t } = useTranslation();
57
+ const theme = useAppTheme();
58
+
59
+ return (
60
+ <ThemeProvider theme={theme}>
61
+ <CssBaseline enableColorScheme />
62
+ <GlobalStyles styles={TRANSPARENT_BODY} />
63
+ <Box
64
+ sx={{
65
+ minHeight: '100vh',
66
+ '@supports (height: 100dvh)': {
67
+ minHeight: '100dvh',
68
+ },
69
+ display: 'flex',
70
+ alignItems: 'center',
71
+ justifyContent: 'center',
72
+ p: 2,
73
+ }}
74
+ >
75
+ <Card
76
+ variant="outlined"
77
+ sx={{ width: '100%', maxWidth: 560, p: 4, textAlign: 'center' }}
78
+ >
79
+ <Typography variant="h5" component="h1" gutterBottom>
80
+ {t('title')}
81
+ </Typography>
82
+ <Chip label={t('role')} color="info" size="small" sx={{ mb: 2 }} />
83
+ <Table size="small" sx={{ mb: 3 }}>
84
+ <TableHead>
85
+ <TableRow>
86
+ <TableCell>{t('teamHeader')}</TableCell>
87
+ <TableCell align="right">{t('tickHeader')}</TableCell>
88
+ </TableRow>
89
+ </TableHead>
90
+ <TableBody>
91
+ {teams.length === 0 ? (
92
+ <TableRow>
93
+ <TableCell colSpan={2} sx={{ color: 'text.secondary' }}>
94
+ {t('noTeams')}
95
+ </TableCell>
96
+ </TableRow>
97
+ ) : (
98
+ teams.map((team: TeamInfo) => (
99
+ <TableRow key={team.teamId}>
100
+ <TableCell component="th" scope="row">
101
+ {team.name}
102
+ </TableCell>
103
+ <TableCell align="right">
104
+ {sessionData.teams[team.teamId]?.completedTicks ?? '—'}
105
+ </TableCell>
106
+ </TableRow>
107
+ ))
108
+ )}
109
+ </TableBody>
110
+ </Table>
111
+ <Stack direction="row" spacing={1} sx={{ justifyContent: 'center' }}>
112
+ <Button variant="contained" onClick={() => requestTick()}>
113
+ {t('nextTickAll')}
114
+ </Button>
115
+ <Button variant="outlined" onClick={() => undoTick()}>
116
+ {t('undoAll')}
117
+ </Button>
118
+ <Button variant="outlined" color="error" onClick={() => resetGame()}>
119
+ {t('resetAll')}
120
+ </Button>
121
+ </Stack>
122
+ </Card>
123
+ </Box>
124
+ </ThemeProvider>
125
+ );
126
+ }
127
+
128
+ export default App;{% endraw %}
@@ -0,0 +1,53 @@
1
+ import i18n from 'i18next';
2
+ import { initReactI18next } from 'react-i18next';
3
+
4
+ // Translation resources
5
+ const resources = {
6
+ en: {
7
+ translation: {
8
+ title: '{{ name }}',
9
+ role: 'Facilitator · session',
10
+ teamHeader: 'Team',
11
+ tickHeader: 'Tick',
12
+ noTeams: 'No teams yet. Start the game to see team data.',
13
+ nextTickAll: 'Next tick (all)',
14
+ undoAll: 'Undo (all)',
15
+ resetAll: 'Reset (all)',
16
+ },
17
+ },
18
+ it: {
19
+ translation: {
20
+ title: '{{ name }}',
21
+ role: 'Facilitatore · sessione',
22
+ teamHeader: 'Squadra',
23
+ tickHeader: 'Tick',
24
+ noTeams: 'Nessuna squadra. Avvia la partita per vedere i dati delle squadre.',
25
+ nextTickAll: 'Prossimo tick (tutte)',
26
+ undoAll: 'Annulla (tutte)',
27
+ resetAll: 'Reimposta (tutte)',
28
+ },
29
+ },
30
+ ar: {
31
+ translation: {
32
+ title: '{{ name }}',
33
+ role: 'الميسّر · الجلسة',
34
+ teamHeader: 'الفريق',
35
+ tickHeader: 'الدورة',
36
+ noTeams: 'لا توجد فرق بعد. ابدأ اللعبة لرؤية بيانات الفرق.',
37
+ nextTickAll: 'الدورة التالية (الكل)',
38
+ undoAll: 'تراجع (الكل)',
39
+ resetAll: 'إعادة تعيين (الكل)',
40
+ },
41
+ },
42
+ };
43
+
44
+ i18n.use(initReactI18next).init({
45
+ resources,
46
+ lng: 'en', // default language
47
+ fallbackLng: 'en',
48
+ interpolation: {
49
+ escapeValue: false, // React already escapes
50
+ },
51
+ });
52
+
53
+ 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 { SessionUserInterfaceWrapper, useSessionUserInterfaceLocale } 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 } = useSessionUserInterfaceLocale();
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
+ <SessionUserInterfaceWrapper>
43
+ <RootWrapper>
44
+ <App />
45
+ </RootWrapper>
46
+ </SessionUserInterfaceWrapper>
47
+ </StrictMode>
48
+ );
@@ -0,0 +1,46 @@
1
+ {% raw %}import { createTheme } from '@mui/material/styles';
2
+ import { useSessionUserInterfaceTheme } from '@seriousgaming/sdk/ui';
3
+ import { useMemo } from 'react';
4
+
5
+ /**
6
+ * The host owns the palette, the game owns the typography and the component
7
+ * shapes. Reading `theme.mode` rather than the browser's preference is what
8
+ * keeps a participant's screen and the facilitator's looking like one product.
9
+ */
10
+ export const useAppTheme = () => {
11
+ const theme = useSessionUserInterfaceTheme();
12
+
13
+ return useMemo(() => {
14
+ const colors = theme.mode === 'light' ? theme.light : theme.dark;
15
+ return createTheme({
16
+ palette: {
17
+ mode: theme.mode,
18
+ primary: { main: colors.primary, contrastText: colors.contrastText.primary },
19
+ secondary: { main: colors.secondary, contrastText: colors.contrastText.secondary },
20
+ success: { main: colors.success, contrastText: colors.contrastText.success },
21
+ error: { main: colors.error, contrastText: colors.contrastText.error },
22
+ warning: { main: colors.warning, contrastText: colors.contrastText.warning },
23
+ info: { main: colors.info, contrastText: colors.contrastText.info },
24
+ },
25
+ shape: { borderRadius: 10 },
26
+ components: {
27
+ MuiButton: { styleOverrides: { root: { textTransform: 'none' } } },
28
+ MuiTab: {
29
+ styleOverrides: { root: { textTransform: 'none', fontSize: '0.95rem' } },
30
+ },
31
+ MuiToggleButton: { styleOverrides: { root: { textTransform: 'none' } } },
32
+ MuiTableCell: {
33
+ styleOverrides: {
34
+ head: {
35
+ fontSize: '0.75rem',
36
+ fontWeight: 700,
37
+ letterSpacing: '0.07em',
38
+ textTransform: 'uppercase',
39
+ },
40
+ },
41
+ },
42
+ },
43
+ });
44
+ }, [theme]);
45
+ };
46
+ {% endraw %}
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{ name }}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="./src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -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 { SettingsEditorWrapper, useSettingsEditorLocale } 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 } = useSettingsEditorLocale();
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
+ <SettingsEditorWrapper>
43
+ <RootWrapper>
44
+ <App />
45
+ </RootWrapper>
46
+ </SettingsEditorWrapper>
47
+ </StrictMode>
48
+ );
@@ -0,0 +1,24 @@
1
+ import { createTheme } from '@mui/material/styles';
2
+ import { useSettingsEditorTheme } from '@seriousgaming/sdk/ui';
3
+ import { useMemo } from 'react';
4
+
5
+ export const useAppTheme = () => {
6
+ const theme = useSettingsEditorTheme();
7
+
8
+ const appTheme = useMemo(() => {
9
+ const themeColors = theme.mode === 'light' ? theme.light : theme.dark;
10
+ return createTheme({
11
+ palette: {
12
+ mode: theme.mode,
13
+ primary: { main: themeColors.primary, contrastText: themeColors.contrastText.primary },
14
+ secondary: { main: themeColors.secondary, contrastText: themeColors.contrastText.secondary },
15
+ success: { main: themeColors.success, contrastText: themeColors.contrastText.success },
16
+ error: { main: themeColors.error, contrastText: themeColors.contrastText.error },
17
+ warning: { main: themeColors.warning, contrastText: themeColors.contrastText.warning },
18
+ info: { main: themeColors.info, contrastText: themeColors.contrastText.info },
19
+ },
20
+ });
21
+ }, [theme]);
22
+
23
+ return appTheme;
24
+ };
@@ -0,0 +1,39 @@
1
+ {% raw %}import { renderSettingsEditor } from '@seriousgaming/sdk/ui/testing';
2
+ import { screen, fireEvent } from '@testing-library/react';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+
5
+ import { defaultSettings } from '../defaults';
6
+ import './i18n';
7
+ import App from './App';
8
+
9
+ // renderSettingsEditor supplies the SDK context providers App needs, in place
10
+ // of the iframe channel the real screen uses: no mocking needed.
11
+ const renderApp = () => renderSettingsEditor(<App />, { settings: defaultSettings });
12
+
13
+ describe('App', () => {
14
+ it('renders the welcome message', () => {
15
+ renderApp();
16
+ expect(screen.getByText(/welcome/i)).toBeInTheDocument();
17
+ });
18
+
19
+ it('increments the counter on button click', () => {
20
+ renderApp();
21
+
22
+ expect(screen.getByText(/current value: 0/i)).toBeInTheDocument();
23
+
24
+ // The counter uses local state, so fireEvent.click is appropriate here.
25
+ // For controlled inputs that call updateSettings, use fireEvent.change.
26
+ fireEvent.click(screen.getByRole('button', { name: /increment/i }));
27
+
28
+ expect(screen.getByText(/current value: 1/i)).toBeInTheDocument();
29
+ });
30
+
31
+ it('asks the host to start the session', () => {
32
+ const requestPlay = vi.fn();
33
+ renderSettingsEditor(<App />, { settings: defaultSettings, callbacks: { requestPlay } });
34
+
35
+ fireEvent.click(screen.getByRole('button', { name: /start/i }));
36
+
37
+ expect(requestPlay).toHaveBeenCalledOnce();
38
+ });
39
+ });{% endraw %}