@dxos/plugin-game 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/PLUGIN.mdl +62 -10
  2. package/dist/lib/Game.mjs +4 -5
  3. package/dist/lib/Game.mjs.map +1 -1
  4. package/dist/lib/GameCapabilities.mjs.map +1 -1
  5. package/dist/lib/GameUtil.mjs +2 -7
  6. package/dist/lib/GameUtil.mjs.map +1 -1
  7. package/dist/lib/capabilities.browser.mjs +28 -0
  8. package/dist/lib/capabilities.browser.mjs.map +1 -0
  9. package/dist/lib/capabilities.mjs +7 -5
  10. package/dist/lib/capabilities.mjs.map +1 -1
  11. package/dist/lib/capabilities.node.mjs +5 -1
  12. package/dist/lib/capabilities.node.mjs.map +1 -1
  13. package/dist/lib/capabilities.tauri.mjs +28 -0
  14. package/dist/lib/capabilities.tauri.mjs.map +1 -0
  15. package/dist/lib/capabilities.workerd.mjs.map +1 -1
  16. package/dist/lib/chunk-GameArticle.mjs +5 -5
  17. package/dist/lib/chunk-GameArticle.mjs.map +1 -1
  18. package/dist/lib/chunk-GameCard.mjs +3 -3
  19. package/dist/lib/chunk-GameCard.mjs.map +1 -1
  20. package/dist/lib/chunk-PLUGIN.mjs +6 -0
  21. package/dist/lib/chunk-PLUGIN.mjs.map +1 -0
  22. package/dist/lib/chunk-create-object.mjs +3 -3
  23. package/dist/lib/chunk-create-object.mjs.map +1 -1
  24. package/dist/lib/chunk-react-surface.mjs +3 -3
  25. package/dist/lib/chunk-react-surface.mjs.map +1 -1
  26. package/dist/lib/components.mjs +50 -30
  27. package/dist/lib/components.mjs.map +1 -1
  28. package/dist/lib/containers.mjs.map +1 -1
  29. package/dist/lib/index.mjs +2 -3
  30. package/dist/lib/meta.mjs.map +1 -1
  31. package/dist/lib/ns/GameUtil.mjs +11 -0
  32. package/dist/lib/ns/GameUtil.mjs.map +1 -0
  33. package/dist/lib/translations.mjs +2 -1
  34. package/dist/lib/translations.mjs.map +1 -1
  35. package/dist/lib/util.mjs +2 -2
  36. package/dist/types/src/GameUtil.d.ts +2 -0
  37. package/dist/types/src/GameUtil.d.ts.map +1 -0
  38. package/dist/types/src/capabilities/gen/browser.d.ts +6 -0
  39. package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/gen/node.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/gen/tauri.d.ts +6 -0
  42. package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
  43. package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -1
  44. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  45. package/dist/types/src/components/CreateGamePanel.d.ts +4 -3
  46. package/dist/types/src/components/CreateGamePanel.d.ts.map +1 -1
  47. package/dist/types/src/components/CreateGamePanel.stories.d.ts +2 -0
  48. package/dist/types/src/components/CreateGamePanel.stories.d.ts.map +1 -1
  49. package/dist/types/src/components/index.d.ts +1 -1
  50. package/dist/types/src/components/index.d.ts.map +1 -1
  51. package/dist/types/src/containers/GameArticle/GameArticle.d.ts +1 -1
  52. package/dist/types/src/containers/GameArticle/GameArticle.d.ts.map +1 -1
  53. package/dist/types/src/containers/GameArticle/index.d.ts +1 -1
  54. package/dist/types/src/containers/GameArticle/index.d.ts.map +1 -1
  55. package/dist/types/src/containers/GameCard/GameCard.d.ts +1 -1
  56. package/dist/types/src/containers/GameCard/GameCard.d.ts.map +1 -1
  57. package/dist/types/src/containers/GameCard/index.d.ts +1 -1
  58. package/dist/types/src/containers/GameCard/index.d.ts.map +1 -1
  59. package/dist/types/src/containers/index.d.ts.map +1 -1
  60. package/dist/types/src/index.d.ts +2 -3
  61. package/dist/types/src/index.d.ts.map +1 -1
  62. package/dist/types/src/translations.d.ts +2 -0
  63. package/dist/types/src/translations.d.ts.map +1 -1
  64. package/dist/types/src/types/Game.d.ts.map +1 -1
  65. package/dist/types/src/types/GameCapabilities.d.ts +1 -1
  66. package/dist/types/src/types/GameCapabilities.d.ts.map +1 -1
  67. package/dist/types/src/types/index.d.ts +3 -3
  68. package/dist/types/src/types/index.d.ts.map +1 -1
  69. package/dist/types/src/util/index.d.ts +1 -2
  70. package/dist/types/src/util/index.d.ts.map +1 -1
  71. package/dist/types/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +32 -26
  73. package/src/GameUtil.ts +7 -0
  74. package/src/capabilities/create-object.ts +5 -1
  75. package/src/capabilities/gen/browser.ts +36 -0
  76. package/src/capabilities/gen/node.ts +2 -2
  77. package/src/capabilities/gen/tauri.ts +36 -0
  78. package/src/capabilities/gen/workerd.ts +1 -1
  79. package/src/capabilities/index.ts +5 -5
  80. package/src/capabilities/react-surface.ts +2 -2
  81. package/src/components/CreateGamePanel.stories.tsx +21 -23
  82. package/src/components/CreateGamePanel.tsx +63 -42
  83. package/src/components/index.ts +1 -1
  84. package/src/containers/GameArticle/GameArticle.tsx +5 -5
  85. package/src/containers/GameArticle/index.ts +1 -1
  86. package/src/containers/GameCard/GameCard.tsx +3 -3
  87. package/src/containers/GameCard/index.ts +1 -1
  88. package/src/containers/index.ts +2 -2
  89. package/src/index.ts +2 -3
  90. package/src/meta.ts +1 -1
  91. package/src/translations.ts +3 -2
  92. package/src/types/Game.ts +4 -4
  93. package/src/types/GameCapabilities.ts +1 -1
  94. package/src/types/index.ts +3 -3
  95. package/src/util/index.ts +1 -2
package/PLUGIN.mdl CHANGED
@@ -18,9 +18,10 @@ Each extension is defined in the Appendix or resolved via its URI.
18
18
  |-------------|--------------------------------|
19
19
  | `type` | `org.dxos.mdl.type@1.0` |
20
20
  | `feat` | `org.dxos.mdl.feat@1.0` |
21
- | `test` | `org.dxos.mdl.test@1.0` |
21
+ | `scenario` | `org.dxos.mdl.scenario@1.0` |
22
22
  | `component` | `org.dxos.mdl.component@1.0` |
23
23
  | `op` | `org.dxos.mdl.op@1.0` |
24
+ | `test` | `org.dxos.mdl.test@1.0` |
24
25
 
25
26
  ## Types
26
27
 
@@ -206,7 +207,7 @@ feat F-3: Game Article / Card Rendering
206
207
  ## Acceptance
207
208
 
208
209
  ```mdl
209
- test T-1: Variant picker shows all contributions
210
+ scenario T-1: Variant picker shows all contributions
210
211
  given: two variant plugins have contributed GameVariants
211
212
  when: the user opens the create-game panel
212
213
  then:
@@ -215,7 +216,7 @@ test T-1: Variant picker shows all contributions
215
216
  ```
216
217
 
217
218
  ```mdl
218
- test T-2: Create game without form
219
+ scenario T-2: Create game without form
219
220
  given: a variant with no inputSchema is selected
220
221
  when: user clicks the variant item
221
222
  then:
@@ -224,7 +225,7 @@ test T-2: Create game without form
224
225
  ```
225
226
 
226
227
  ```mdl
227
- test T-3: Create game with form
228
+ scenario T-3: Create game with form
228
229
  given: a variant with an inputSchema is selected
229
230
  when: user fills and submits the form
230
231
  then:
@@ -232,7 +233,7 @@ test T-3: Create game with form
232
233
  ```
233
234
 
234
235
  ```mdl
235
- test T-4: Atomic game creation
236
+ scenario T-4: Atomic game creation
236
237
  given: a valid variantId and target space
237
238
  when: op:createGame is invoked
238
239
  then:
@@ -242,7 +243,7 @@ test T-4: Atomic game creation
242
243
  ```
243
244
 
244
245
  ```mdl
245
- test T-5: Rollback on second write failure
246
+ scenario T-5: Rollback on second write failure
246
247
  given: variant state is successfully written
247
248
  when: the second write (Game object) fails
248
249
  then:
@@ -251,14 +252,14 @@ test T-5: Rollback on second write failure
251
252
  ```
252
253
 
253
254
  ```mdl
254
- test T-6: Unknown variant rejected
255
+ scenario T-6: Unknown variant rejected
255
256
  given: op:createGame is invoked with an unregistered variantId
256
257
  when: the handler runs
257
258
  then: UnknownVariant error is returned; no ECHO writes occur
258
259
  ```
259
260
 
260
261
  ```mdl
261
- test T-7: Unsupported variant fallback
262
+ scenario T-7: Unsupported variant fallback
262
263
  given: a Game whose variant typename has no registered GameVariant
263
264
  when: GameArticle renders
264
265
  then: "Unsupported game variant" message is shown instead of crashing
@@ -266,6 +267,57 @@ test T-7: Unsupported variant fallback
266
267
 
267
268
  ---
268
269
 
270
+ ## QA
271
+
272
+ Executable tests. Each step carries a human instruction (`do`) and, where the step maps to an
273
+ invocable operation, an agent binding (`invoke`). Report failures by test and step number.
274
+
275
+ ```mdl
276
+ test QA-1: Create a game through the variant picker and render it
277
+ desc: |
278
+ The generic host round-trip — pick a registered variant, create the Game wrapper plus its
279
+ hidden variant state, and confirm dispatch renders the variant's article. Uses
280
+ plugin-chess's variant since it is the only one shipped and enabled by default; the test
281
+ tests plugin-game's dispatch, not chess's own gameplay (see plugin-chess's QA for that).
282
+ actors: human
283
+ covers: [F-1.2, F-2.1, F-2.4, F-3.1, T-2, T-4]
284
+ status: unverified
285
+ given:
286
+ - an identity exists
287
+ - space: a space is open
288
+ - collection: a collection in that space, to create into
289
+ - plugin-chess (or another GameVariant contributor) is enabled, so the picker lists at least one variant
290
+ - no game named "QA Game" exists — ABORT rather than deleting one this test did not create
291
+ - game: (bound during the run, not beforehand) the Game object the "Select the Chess variant" step creates — recorded by the tester, since no invoke captures it
292
+ steps:
293
+ - name: Open the create-game panel
294
+ do: In the navtree, click + on a collection and choose Game.
295
+ expect: CreateGamePanel opens showing a searchable list including a "Chess" row
296
+ note: |
297
+ NO `invoke` — `createGame` is registered as a `CreateObjectEntry.customPanel`, not an
298
+ `Operation.make` with a runtime key, so there is no key to invoke through the operation
299
+ invoker; opening the panel is itself the create-object gesture (see `code-style`/`operations`
300
+ conventions: only `Operation.make` instances get a DXN, and this factory is not one).
301
+ - name: Select the Chess variant
302
+ do: Click the "Chess" row (it has no inputSchema, so no form stage follows).
303
+ expect: a Game object appears under the collection, named after the default, with hidden chess variant state referenced by `game.variant`
304
+ note: |
305
+ NO `invoke` for the same reason as the previous step — this whole creation gesture is
306
+ human-only until `createGame` (or an equivalent) is exposed as an invocable operation.
307
+ Because nothing here is scriptable, there is no captured result to thread forward; the
308
+ tester notes the new Game's id manually for the `after` stage below.
309
+ - name: Open the game and confirm dispatch
310
+ do: Click the Game row in the navtree.
311
+ expect: GameArticle resolves `game.variant` to the chess state object and delegates to chess's own article — a starting chess position renders, not the "Unsupported game variant" fallback
312
+ after:
313
+ - name: Delete the game
314
+ do: Right-click the Game row in the navtree and choose Delete.
315
+ invoke: org.dxos.operation.space.removeObjects { objects: [$given.game], target: $given.collection }
316
+ expect: the Game row leaves the navtree, its plank closes if open, and the hidden variant state object is also removed
317
+ ```
318
+
319
+ ---
320
+
269
321
  ## Appendix: Extension Definitions
270
322
 
271
323
  Extension block types used in this document are defined below using
@@ -293,8 +345,8 @@ ext feat
293
345
  ```
294
346
 
295
347
  ```mdl
296
- ext test
297
- uri: org.dxos.mdl.test@1.0
348
+ ext scenario
349
+ uri: org.dxos.mdl.scenario@1.0
298
350
  desc: An acceptance scenario expressed as given / when / then steps.
299
351
  fields:
300
352
  given?: Step | Step[]
package/dist/lib/Game.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
2
  import { Annotation, DXN, Obj, Ref, Type } from "@dxos/echo";
3
3
  import * as Schema from "effect/Schema";
4
- import { FormInputAnnotation, LabelAnnotation } from "@dxos/echo/Annotation";
5
4
  import { CardAnnotation } from "@dxos/schema";
6
5
  //#region src/types/Game.ts
7
6
  var Game_exports = /* @__PURE__ */ __exportAll({
@@ -28,12 +27,12 @@ var Player = Schema.Struct({
28
27
  */
29
28
  var Game = class extends Type.makeObject(DXN.make("org.dxos.type.game", "0.1.0"))(Schema.Struct({
30
29
  name: Schema.optional(Schema.String),
31
- players: Schema.mutable(Schema.Array(Player)).annotate({ description: "Players in the game." }).pipe(FormInputAnnotation.set(false), Schema.optional),
32
- variant: Ref.Ref(Obj.Unknown).annotate({ description: "Reference to variant-specific state object." }).pipe(FormInputAnnotation.set(false))
33
- }).pipe(LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
30
+ players: Schema.mutable(Schema.Array(Player)).annotate({ description: "Players in the game." }).pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),
31
+ variant: Ref.Ref(Obj.Unknown).annotate({ description: "Reference to variant-specific state object." }).pipe(Annotation.FormInputAnnotation.set(false))
32
+ }).pipe(Annotation.LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
34
33
  icon: "ph--sword--regular",
35
34
  hue: "indigo"
36
- }), CardAnnotation.set(true), Annotation.IconFromRefAnnotation.set("variant"))) {};
35
+ }), CardAnnotation.set(true), Annotation.IconFromRefAnnotation.set("variant"), Annotation.UserType.set())) {};
37
36
  var GameRef = (_variantType) => Ref.Ref(Game);
38
37
  /**
39
38
  * Build a base `Game` object referencing the given variant-state ECHO object.
@@ -1 +1 @@
1
- {"version":3,"file":"Game.mjs","names":[],"sources":["../../src/types/Game.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Obj, Ref, Type } from '@dxos/echo';\nimport { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/Annotation';\nimport { CardAnnotation } from '@dxos/schema';\n\n// @import-as-namespace\n\n/**\n * A player in a game.\n * `role` is variant-defined (e.g. 'white' | 'black' for chess, 'x' | 'o' for tictactoe).\n * `identity` is the optional DID of a human player; absent for AI or empty seats.\n * `name` is an optional display label (useful for AI/guest players).\n */\nexport const Player = Schema.Struct({\n role: Schema.String.annotate({\n description: 'Variant-defined player role (e.g. \"white\", \"x\").',\n }),\n identity: Schema.optional(\n Schema.String.annotate({\n description: 'DID of the player; absent for AI or empty seats.',\n }),\n ),\n name: Schema.optional(\n Schema.String.annotate({\n description: 'Optional display name for the player.',\n }),\n ),\n});\n\nexport type Player = Schema.Schema.Type<typeof Player>;\n\n/**\n * Base Game object. Variant-specific state lives in a separate referenced object.\n * Unifies all game variants (chess, tic-tac-toe, ...) under one typename so they share\n * graph node, create flow, and shared surface scaffolding.\n */\nexport class Game extends Type.makeObject<Game>(DXN.make('org.dxos.type.game', '0.1.0'))(\n Schema.Struct({\n name: Schema.optional(Schema.String),\n players: Schema.mutable(Schema.Array(Player))\n .annotate({ description: 'Players in the game.' })\n .pipe(FormInputAnnotation.set(false), Schema.optional),\n variant: Ref.Ref(Obj.Unknown)\n .annotate({ description: 'Reference to variant-specific state object.' })\n .pipe(FormInputAnnotation.set(false)),\n }).pipe(\n LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--sword--regular', hue: 'indigo' }),\n CardAnnotation.set(true),\n // Delegate the graph-node icon to the referenced variant state's schema. Falls back to\n // the static `ph--sword--regular` above while the variant ref is still loading.\n Annotation.IconFromRefAnnotation.set('variant'),\n ),\n) {}\n\n/**\n * Variant-narrowed reference to a Game.\n *\n * Encodes — at the type level — that the variant ref points to a specific state type.\n * Runtime narrowing is performed by `GameUtil.loadGame(ref, variantType)` which validates and\n * returns the resolved Game together with its typed variant state.\n *\n * @example\n * ```ts\n * input: Schema.Struct({\n * game: GameRef(Chess.State),\n * }),\n * // handler:\n * const { game, variant } = yield* GameUtil.loadGame(input.game, Chess.State);\n * // `variant` is typed as Chess.State.\n * ```\n */\nexport type GameRef<_V> = Ref.Ref<Game>;\n\nexport const GameRef = <S extends Type.AnyObj>(_variantType: S) =>\n Ref.Ref(Game) as unknown as Schema.Codec<GameRef<Type.InstanceType<S>>, any>;\n\n/**\n * Build a base `Game` object referencing the given variant-state ECHO object.\n *\n * The variant is stored as a `Ref` so the underlying state (e.g. `Chess.State`,\n * `TicTacToe.State`) lives as its own ECHO object alongside the Game.\n *\n * @param name Optional display name shown in the graph node and Properties form.\n * @param variant Variant-specific state object (e.g. `Chess.State`, `TicTacToe.State`).\n * @param players Optional initial players; copied into a mutable array on the Game.\n * @returns A new `Game` object with `variant` stored as a Ref.\n */\nexport const make = ({\n name,\n players,\n variant,\n}: {\n name?: string;\n players?: ReadonlyArray<Player>;\n variant: Obj.Unknown;\n}): Game => {\n return Obj.make(Game, {\n name,\n players: players ? [...players] : [],\n variant: Ref.make(variant),\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,IAAa,SAAS,OAAO,OAAO;CAClC,MAAM,OAAO,OAAO,SAAS,EAC3B,aAAa,uDACf,CAAC;CACD,UAAU,OAAO,SACf,OAAO,OAAO,SAAS,EACrB,aAAa,mDACf,CAAC,CACH;CACA,MAAM,OAAO,SACX,OAAO,OAAO,SAAS,EACrB,aAAa,wCACf,CAAC,CACH;AACF,CAAC;;;;;;AASD,IAAa,OAAb,cAA0B,KAAK,WAAiB,IAAI,KAAK,sBAAsB,OAAO,CAAC,CAAC,CACtF,OAAO,OAAO;CACZ,MAAM,OAAO,SAAS,OAAO,MAAM;CACnC,SAAS,OAAO,QAAQ,OAAO,MAAM,MAAM,CAAC,CAAA,CACzC,SAAS,EAAE,aAAa,uBAAuB,CAAC,CAAA,CAChD,KAAK,oBAAoB,IAAI,KAAK,GAAG,OAAO,QAAQ;CACvD,SAAS,IAAI,IAAI,IAAI,OAAO,CAAA,CACzB,SAAS,EAAE,aAAa,8CAA8C,CAAC,CAAA,CACvE,KAAK,oBAAoB,IAAI,KAAK,CAAC;AACxC,CAAC,CAAC,CAAC,KACD,gBAAgB,IAAI,CAAC,MAAM,CAAC,GAC5B,WAAW,eAAe,IAAI;CAAE,MAAM;CAAsB,KAAK;AAAS,CAAC,GAC3E,eAAe,IAAI,IAAI,GAGvB,WAAW,sBAAsB,IAAI,SAAS,CAChD,CACF,CAAC,CAAC,CAAC;AAqBH,IAAa,WAAkC,iBAC7C,IAAI,IAAI,IAAI;;;;;;;;;;;;AAad,IAAa,QAAQ,EACnB,MACA,SACA,cAKU;CACV,OAAO,IAAI,KAAK,MAAM;EACpB;EACA,SAAS,UAAU,CAAC,GAAG,OAAO,IAAI,CAAC;EACnC,SAAS,IAAI,KAAK,OAAO;CAC3B,CAAC;AACH"}
1
+ {"version":3,"file":"Game.mjs","names":[],"sources":["../../src/types/Game.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Obj, Ref, Type } from '@dxos/echo';\nimport { CardAnnotation } from '@dxos/schema';\n\n// @import-as-namespace\n\n/**\n * A player in a game.\n * `role` is variant-defined (e.g. 'white' | 'black' for chess, 'x' | 'o' for tictactoe).\n * `identity` is the optional DID of a human player; absent for AI or empty seats.\n * `name` is an optional display label (useful for AI/guest players).\n */\nexport const Player = Schema.Struct({\n role: Schema.String.annotate({\n description: 'Variant-defined player role (e.g. \"white\", \"x\").',\n }),\n identity: Schema.optional(\n Schema.String.annotate({\n description: 'DID of the player; absent for AI or empty seats.',\n }),\n ),\n name: Schema.optional(\n Schema.String.annotate({\n description: 'Optional display name for the player.',\n }),\n ),\n});\n\nexport type Player = Schema.Schema.Type<typeof Player>;\n\n/**\n * Base Game object. Variant-specific state lives in a separate referenced object.\n * Unifies all game variants (chess, tic-tac-toe, ...) under one typename so they share\n * graph node, create flow, and shared surface scaffolding.\n */\nexport class Game extends Type.makeObject<Game>(DXN.make('org.dxos.type.game', '0.1.0'))(\n Schema.Struct({\n name: Schema.optional(Schema.String),\n players: Schema.mutable(Schema.Array(Player))\n .annotate({ description: 'Players in the game.' })\n .pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),\n variant: Ref.Ref(Obj.Unknown)\n .annotate({ description: 'Reference to variant-specific state object.' })\n .pipe(Annotation.FormInputAnnotation.set(false)),\n }).pipe(\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--sword--regular', hue: 'indigo' }),\n CardAnnotation.set(true),\n // Delegate the graph-node icon to the referenced variant state's schema. Falls back to\n // the static `ph--sword--regular` above while the variant ref is still loading.\n Annotation.IconFromRefAnnotation.set('variant'),\n Annotation.UserType.set(),\n ),\n) {}\n\n/**\n * Variant-narrowed reference to a Game.\n *\n * Encodes — at the type level — that the variant ref points to a specific state type.\n * Runtime narrowing is performed by `GameUtil.loadGame(ref, variantType)` which validates and\n * returns the resolved Game together with its typed variant state.\n *\n * @example\n * ```ts\n * input: Schema.Struct({\n * game: GameRef(Chess.State),\n * }),\n * // handler:\n * const { game, variant } = yield* GameUtil.loadGame(input.game, Chess.State);\n * // `variant` is typed as Chess.State.\n * ```\n */\nexport type GameRef<_V> = Ref.Ref<Game>;\n\nexport const GameRef = <S extends Type.AnyObj>(_variantType: S) =>\n Ref.Ref(Game) as unknown as Schema.Codec<GameRef<Type.InstanceType<S>>, any>;\n\n/**\n * Build a base `Game` object referencing the given variant-state ECHO object.\n *\n * The variant is stored as a `Ref` so the underlying state (e.g. `Chess.State`,\n * `TicTacToe.State`) lives as its own ECHO object alongside the Game.\n *\n * @param name Optional display name shown in the graph node and Properties form.\n * @param variant Variant-specific state object (e.g. `Chess.State`, `TicTacToe.State`).\n * @param players Optional initial players; copied into a mutable array on the Game.\n * @returns A new `Game` object with `variant` stored as a Ref.\n */\nexport const make = ({\n name,\n players,\n variant,\n}: {\n name?: string;\n players?: ReadonlyArray<Player>;\n variant: Obj.Unknown;\n}): Game => {\n return Obj.make(Game, {\n name,\n players: players ? [...players] : [],\n variant: Ref.make(variant),\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,IAAa,SAAS,OAAO,OAAO;CAClC,MAAM,OAAO,OAAO,SAAS,EAC3B,aAAa,uDACf,CAAC;CACD,UAAU,OAAO,SACf,OAAO,OAAO,SAAS,EACrB,aAAa,mDACf,CAAC,CACH;CACA,MAAM,OAAO,SACX,OAAO,OAAO,SAAS,EACrB,aAAa,wCACf,CAAC,CACH;AACF,CAAC;;;;;;AASD,IAAa,OAAb,cAA0B,KAAK,WAAiB,IAAI,KAAK,sBAAsB,OAAO,CAAC,CAAC,CACtF,OAAO,OAAO;CACZ,MAAM,OAAO,SAAS,OAAO,MAAM;CACnC,SAAS,OAAO,QAAQ,OAAO,MAAM,MAAM,CAAC,CAAA,CACzC,SAAS,EAAE,aAAa,uBAAuB,CAAC,CAAA,CAChD,KAAK,WAAW,oBAAoB,IAAI,KAAK,GAAG,OAAO,QAAQ;CAClE,SAAS,IAAI,IAAI,IAAI,OAAO,CAAA,CACzB,SAAS,EAAE,aAAa,8CAA8C,CAAC,CAAA,CACvE,KAAK,WAAW,oBAAoB,IAAI,KAAK,CAAC;AACnD,CAAC,CAAC,CAAC,KACD,WAAW,gBAAgB,IAAI,CAAC,MAAM,CAAC,GACvC,WAAW,eAAe,IAAI;CAAE,MAAM;CAAsB,KAAK;AAAS,CAAC,GAC3E,eAAe,IAAI,IAAI,GAGvB,WAAW,sBAAsB,IAAI,SAAS,GAC9C,WAAW,SAAS,IAAI,CAC1B,CACF,CAAC,CAAC,CAAC;AAqBH,IAAa,WAAkC,iBAC7C,IAAI,IAAI,IAAI;;;;;;;;;;;;AAad,IAAa,QAAQ,EACnB,MACA,SACA,cAKU;CACV,OAAO,IAAI,KAAK,MAAM;EACpB;EACA,SAAS,UAAU,CAAC,GAAG,OAAO,IAAI,CAAC;EACnC,SAAS,IAAI,KAAK,OAAO;CAC3B,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"GameCapabilities.mjs","names":[],"sources":["../../src/types/GameCapabilities.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as Effect from 'effect/Effect';\nimport type * as Schema from 'effect/Schema';\nimport type { ComponentType } from 'react';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport type { Database, Obj, Type } from '@dxos/echo';\n\nimport { meta } from '#meta';\n\nimport * as Game from './Game';\n\n/**\n * A game variant contribution. Each variant plugin (chess, tic-tac-toe, ...)\n * contributes one of these via `Capability.contribute(GameCapabilities.VariantProvider, variant)`.\n * Multi capability: more than one variant plugin provides it. Consumers iterate via\n * `Capability.getAll(GameCapabilities.VariantProvider)` (Effect) or\n * `useCapabilities(GameCapabilities.VariantProvider)` (React).\n */\nexport const VariantProvider = Capability.make<GameVariant>()(`${meta.profile.key}.capability.variant`);\n\n/**\n * Contribution from a variant plugin (e.g. plugin-chess).\n * Defines how a game variant is created, rendered, and the roles its players take.\n */\nexport type GameVariant = {\n /** Stable id, typically the variant state typename (e.g. 'org.dxos.type.chess.state'). */\n id: string;\n /** Human-readable variant name (e.g. 'Chess'). */\n label: string;\n /** Optional Phosphor icon name (e.g. 'ph--shield-chevron--regular'). */\n icon?: string;\n /** ECHO Type entity of the variant state object referenced by `Game.variant`. */\n variantType: Type.AnyObj;\n /**\n * Optional Effect Schema rendered as a form after the user picks the variant.\n * To use a `Type.Type` entity, extract its schema first via `Type.getSchema(...)`.\n */\n inputSchema?: Schema.Codec<any, any>;\n /** Roles a player may take in this variant (e.g. ['white', 'black']). */\n roles: readonly string[];\n /**\n * Build the variant state object from the user's form input. May allocate ECHO\n * objects, run effects, etc. Returned object is added to the database alongside the Game.\n */\n createVariant: (input: Record<string, any>) => Effect.Effect<Obj.Any, Error, Database.Service>;\n /** Optional Card surface component for this variant. */\n card?: ComponentType<GameVariantSurfaceProps>;\n /** Optional Article/Section surface component for this variant. */\n article?: ComponentType<GameVariantSurfaceProps>;\n};\n\nexport type GameVariantSurfaceProps = {\n /** The base Game object (may be a snapshot from useObject/Surface). */\n game: Obj.Snapshot<Game.Game> | Game.Game;\n /**\n * The resolved variant state object, live rather than a snapshot so variants can write to it.\n * Reads must subscribe via `useObject(variant, prop)` — the host re-renders only when the ref\n * resolves, not on every mutation.\n */\n variant: Obj.Unknown;\n /** Surface role passed through from the host. */\n role?: string;\n /** Attendable id passed through from the host. */\n attendableId?: string;\n};\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAa,kBAAkB,WAAW,KAAkB,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,oBAAoB"}
1
+ {"version":3,"file":"GameCapabilities.mjs","names":[],"sources":["../../src/types/GameCapabilities.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as Effect from 'effect/Effect';\nimport type * as Schema from 'effect/Schema';\nimport type { ComponentType } from 'react';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport type { Database, Obj, Type } from '@dxos/echo';\n\nimport { meta } from '#meta';\n\nimport * as Game from './Game.ts';\n\n/**\n * A game variant contribution. Each variant plugin (chess, tic-tac-toe, ...)\n * contributes one of these via `Capability.contribute(GameCapabilities.VariantProvider, variant)`.\n * Multi capability: more than one variant plugin provides it. Consumers iterate via\n * `Capability.getAll(GameCapabilities.VariantProvider)` (Effect) or\n * `useCapabilities(GameCapabilities.VariantProvider)` (React).\n */\nexport const VariantProvider = Capability.make<GameVariant>()(`${meta.profile.key}.capability.variant`);\n\n/**\n * Contribution from a variant plugin (e.g. plugin-chess).\n * Defines how a game variant is created, rendered, and the roles its players take.\n */\nexport type GameVariant = {\n /** Stable id, typically the variant state typename (e.g. 'org.dxos.type.chess.state'). */\n id: string;\n /** Human-readable variant name (e.g. 'Chess'). */\n label: string;\n /** Optional Phosphor icon name (e.g. 'ph--shield-chevron--regular'). */\n icon?: string;\n /** ECHO Type entity of the variant state object referenced by `Game.variant`. */\n variantType: Type.AnyObj;\n /**\n * Optional Effect Schema rendered as a form after the user picks the variant.\n * To use a `Type.Type` entity, extract its schema first via `Type.getSchema(...)`.\n */\n inputSchema?: Schema.Codec<any, any>;\n /** Roles a player may take in this variant (e.g. ['white', 'black']). */\n roles: readonly string[];\n /**\n * Build the variant state object from the user's form input. May allocate ECHO\n * objects, run effects, etc. Returned object is added to the database alongside the Game.\n */\n createVariant: (input: Record<string, any>) => Effect.Effect<Obj.Any, Error, Database.Service>;\n /** Optional Card surface component for this variant. */\n card?: ComponentType<GameVariantSurfaceProps>;\n /** Optional Article/Section surface component for this variant. */\n article?: ComponentType<GameVariantSurfaceProps>;\n};\n\nexport type GameVariantSurfaceProps = {\n /** The base Game object (may be a snapshot from useObject/Surface). */\n game: Obj.Snapshot<Game.Game> | Game.Game;\n /**\n * The resolved variant state object, live rather than a snapshot so variants can write to it.\n * Reads must subscribe via `useObject(variant, prop)` — the host re-renders only when the ref\n * resolves, not on every mutation.\n */\n variant: Obj.Unknown;\n /** Surface role passed through from the host. */\n role?: string;\n /** Attendable id passed through from the host. */\n attendableId?: string;\n};\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAa,kBAAkB,WAAW,KAAkB,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,oBAAoB"}
@@ -1,11 +1,6 @@
1
- import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
- import { Database, Obj, Type } from "@dxos/echo";
3
1
  import * as Effect from "effect/Effect";
2
+ import { Database, Obj, Type } from "@dxos/echo";
4
3
  //#region src/util/load-game.ts
5
- var load_game_exports = /* @__PURE__ */ __exportAll({
6
- GameVariantMismatchError: () => GameVariantMismatchError,
7
- loadGame: () => loadGame
8
- });
9
4
  var GameVariantMismatchError = class extends Error {
10
5
  gameId;
11
6
  expectedTypename;
@@ -38,6 +33,6 @@ var loadGame = (ref, variantType) => Effect.gen(function* () {
38
33
  };
39
34
  });
40
35
  //#endregion
41
- export { GameVariantMismatchError, loadGame, load_game_exports as t };
36
+ export { GameVariantMismatchError, loadGame };
42
37
 
43
38
  //# sourceMappingURL=GameUtil.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"GameUtil.mjs","names":[],"sources":["../../src/util/load-game.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Database, Obj, type Ref, Type } from '@dxos/echo';\n\nimport { Game } from '#types';\n\nexport class GameVariantMismatchError extends Error {\n readonly _tag = 'GameVariantMismatchError';\n constructor(\n readonly gameId: string,\n readonly expectedTypename: string,\n readonly actualTypename: string,\n ) {\n super(`Game ${gameId} variant typename mismatch: expected ${expectedTypename}, got ${actualTypename}.`);\n }\n}\n\n/**\n * Loads a Game from a Ref and resolves its variant state, asserting the variant matches\n * the expected type. Returns the typed pair.\n *\n * Use in operation handlers that declare `input.game: GameRef<MyVariantState>`.\n */\nexport const loadGame = <S extends Type.AnyObj>(\n ref: Ref.Ref<Game.Game>,\n variantType: S,\n): Effect.Effect<{ game: Game.Game; variant: Type.InstanceType<S> }, Error, Database.Service> =>\n Effect.gen(function* () {\n const game = yield* Database.load(ref);\n const variant = yield* Database.load(game.variant);\n if (!Obj.instanceOf(variantType, variant)) {\n const expected = Type.getTypename(variantType) ?? 'unknown';\n const actual = Obj.getTypename(variant as Obj.Any) ?? 'unknown';\n return yield* Effect.fail(new GameVariantMismatchError(game.id, expected, actual));\n }\n return { game, variant: variant as Type.InstanceType<S> };\n });\n"],"mappings":";;;;;;;;AAUA,IAAa,2BAAb,cAA8C,MAAM;CAGvC;CACA;CACA;CAJX,OAAgB;CAChB,YACE,QACA,kBACA,gBACA;EACA,MAAM,QAAQ,OAAO,uCAAuC,iBAAiB,QAAQ,eAAe,EAAE;EAJ7F,KAAA,SAAA;EACA,KAAA,mBAAA;EACA,KAAA,iBAAA;CAGX;AACF;;;;;;;AAQA,IAAa,YACX,KACA,gBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,SAAS,KAAK,GAAG;CACrC,MAAM,UAAU,OAAO,SAAS,KAAK,KAAK,OAAO;CACjD,IAAI,CAAC,IAAI,WAAW,aAAa,OAAO,GAAG;EACzC,MAAM,WAAW,KAAK,YAAY,WAAW,KAAK;EAClD,MAAM,SAAS,IAAI,YAAY,OAAkB,KAAK;EACtD,OAAO,OAAO,OAAO,KAAK,IAAI,yBAAyB,KAAK,IAAI,UAAU,MAAM,CAAC;CACnF;CACA,OAAO;EAAE;EAAe;CAAgC;AAC1D,CAAC"}
1
+ {"version":3,"file":"GameUtil.mjs","names":[],"sources":["../../src/util/load-game.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Database, Obj, type Ref, Type } from '@dxos/echo';\n\nimport { Game } from '#types';\n\nexport class GameVariantMismatchError extends Error {\n readonly _tag = 'GameVariantMismatchError';\n constructor(\n readonly gameId: string,\n readonly expectedTypename: string,\n readonly actualTypename: string,\n ) {\n super(`Game ${gameId} variant typename mismatch: expected ${expectedTypename}, got ${actualTypename}.`);\n }\n}\n\n/**\n * Loads a Game from a Ref and resolves its variant state, asserting the variant matches\n * the expected type. Returns the typed pair.\n *\n * Use in operation handlers that declare `input.game: GameRef<MyVariantState>`.\n */\nexport const loadGame = <S extends Type.AnyObj>(\n ref: Ref.Ref<Game.Game>,\n variantType: S,\n): Effect.Effect<{ game: Game.Game; variant: Type.InstanceType<S> }, Error, Database.Service> =>\n Effect.gen(function* () {\n const game = yield* Database.load(ref);\n const variant = yield* Database.load(game.variant);\n if (!Obj.instanceOf(variantType, variant)) {\n const expected = Type.getTypename(variantType) ?? 'unknown';\n const actual = Obj.getTypename(variant as Obj.Any) ?? 'unknown';\n return yield* Effect.fail(new GameVariantMismatchError(game.id, expected, actual));\n }\n return { game, variant: variant as Type.InstanceType<S> };\n });\n"],"mappings":";;;AAUA,IAAa,2BAAb,cAA8C,MAAM;CAGvC;CACA;CACA;CAJX,OAAgB;CAChB,YACE,QACA,kBACA,gBACA;EACA,MAAM,QAAQ,OAAO,uCAAuC,iBAAiB,QAAQ,eAAe,EAAE;EAJ7F,KAAA,SAAA;EACA,KAAA,mBAAA;EACA,KAAA,iBAAA;CAGX;AACF;;;;;;;AAQA,IAAa,YACX,KACA,gBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,SAAS,KAAK,GAAG;CACrC,MAAM,UAAU,OAAO,SAAS,KAAK,KAAK,OAAO;CACjD,IAAI,CAAC,IAAI,WAAW,aAAa,OAAO,GAAG;EACzC,MAAM,WAAW,KAAK,YAAY,WAAW,KAAK;EAClD,MAAM,SAAS,IAAI,YAAY,OAAkB,KAAK;EACtD,OAAO,OAAO,OAAO,KAAK,IAAI,yBAAyB,KAAK,IAAI,UAAU,MAAM,CAAC;CACnF;CACA,OAAO;EAAE;EAAe;CAAgC;AAC1D,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
2
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
+ import { translations } from "#translations";
4
+ import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
5
+ import { meta } from "#meta";
6
+ //#region src/capabilities/gen/browser.ts
7
+ var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"), { environments: ["browser", "tauri"] });
8
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: [
9
+ "org.dxos.role.article",
10
+ "org.dxos.role.cardContent",
11
+ "org.dxos.role.section"
12
+ ] });
13
+ var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
14
+ var PluginAsset = AppCapability.pluginAsset({
15
+ pluginId: meta.profile.key,
16
+ path: "PLUGIN.mdl",
17
+ content: PLUGIN_default,
18
+ mimeType: "application/x-mdl"
19
+ });
20
+ var Translations = AppCapability.translations(translations, { environments: [
21
+ "browser",
22
+ "node",
23
+ "tauri"
24
+ ] });
25
+ //#endregion
26
+ export { CreateObject, PluginAsset, ReactSurface, Schema, Translations };
27
+
28
+ //# sourceMappingURL=capabilities.browser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.browser.mjs","names":[],"sources":["../../src/capabilities/gen/browser.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'browser' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport pluginSpec from '../../../PLUGIN.mdl?raw';\n\n// Browser-only: the entry supplies `CreateGamePanel`, the React form that picks the variant and\n// collects its input.\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object.ts'), {\n environments: ['browser', 'tauri'],\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.cardContent', 'org.dxos.role.section'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\n\nexport const Translations = AppCapability.translations(translations, {\n environments: ['browser', 'node', 'tauri'],\n});\n"],"mappings":";;;;;;AAgBA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,8BAAwB,EAC5F,cAAc,CAAC,WAAW,OAAO,EACnC,CAAC;AAED,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO;CAAC;CAAyB;CAA6B;AAAuB,EACvF,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AAED,IAAa,eAAe,cAAc,aAAa,cAAc,EACnE,cAAc;CAAC;CAAW;CAAQ;AAAO,EAC3C,CAAC"}
@@ -1,12 +1,10 @@
1
+ import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
1
2
  import * as AppCapability from "@dxos/app-toolkit/AppCapability";
2
3
  import { translations } from "#translations";
3
4
  import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
4
5
  import { meta } from "#meta";
5
- //#region PLUGIN.mdl?raw
6
- var PLUGIN_default = "---\nid: org.dxos.plugin.game\nname: GamePlugin\nversion: 0.1.0\n---\n\nA generic game host plugin for `DXOS` Composer that provides shared infrastructure for\nmulti-player, local-first turn-based games. Variant plugins (e.g. plugin-chess,\nplugin-tictactoe) contribute a `GameVariant` capability and game-specific surfaces;\nplugin-game supplies the `Game` ECHO type, create flow, and dispatch scaffolding.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype Player\n fields:\n role: string # variant-defined (e.g. \"white\", \"x\"); required\n identity?: string # DID of a human player; absent for AI / empty seats\n name?: string # display label for AI or guest players\n```\n\n```mdl\ntype Game\n fields:\n name?: string\n players?: Player[] # populated during or after creation\n variant: Ref<Unknown> # reference to variant-specific state object (e.g. Chess.State)\n typename: org.dxos.type.game\n version: 0.1.0\n```\n\n```mdl\ntype GameVariant\n desc: |\n Contribution interface from a variant plugin. Registered via\n `Capability.contributes(GameCapabilities.VariantProvider, variant)`.\n fields:\n id: string # stable variant id; matches the variant state typename\n label: string # human-readable name shown in the picker\n icon?: string # Phosphor icon name\n variantSchema: Schema # Effect/Schema for the ECHO state object\n inputSchema?: Schema # optional form schema shown after variant is selected\n roles: string[] # player roles (e.g. [\"white\", \"black\"])\n createVariant: Effect # factory that builds the variant ECHO state object\n card?: ComponentType # optional Card surface component\n article?: ComponentType # optional Article/Section surface component\n```\n\n## Components\n\n```mdl\ncomponent CreateGamePanel\n desc: |\n Two-stage creation panel. Stage 1 shows a searchable variant picker.\n Stage 2 (optional) shows a form rendered from the variant's inputSchema.\n props:\n target: Space | Obj\n onCreateObject: (args: { variantId: string; input?: Record }) => void\n variants?: GameVariant[] # override; defaults to GameCapabilities.VariantProvider\n state:\n selectedId?: string # id of the variant chosen in stage 1\n actions:\n selectVariant(id: string)\n submitForm(input: Record)\n layout: |\n ┌─────────────────────────┐\n │ Stage 1: Variant Picker │\n │ ┌───────────────────┐ │\n │ │ Search input │ │\n │ ├───────────────────┤ │\n │ │ variant row │ │\n │ │ variant row │ │\n │ └───────────────────┘ │\n ├─────────────────────────┤\n │ Stage 2: Input Form │\n │ (rendered from schema) │\n │ [Submit] │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent GameArticle\n desc: |\n Article / Section surface for Game objects. Resolves the variant via\n `game.variant` ref, finds the matching `GameVariant` by typename, and\n delegates rendering to `variant.article`.\n props:\n game: Game\n role?: string\n attendableId?: string\n state:\n variant: Unknown # live variant state object from the Ref\n match?: GameVariant # resolved GameVariant from capabilities\n layout: |\n ┌─────────────────────────┐\n │ [variant.article] │\n │ (fallback: unsupported │\n │ variant message) │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent GameCard\n desc: |\n Card surface for Game objects. Follows the same variant-resolution\n logic as GameArticle and delegates to `variant.card`.\n props:\n game: Game\n role?: string\n state:\n variant: Unknown\n match?: GameVariant\n```\n\n## Operations\n\n```mdl\nop createGame\n desc: |\n Creates a variant state object via `variant.createVariant`, adds it\n to the space as a hidden object, then wraps it in a top-level Game.\n If the second write fails the variant state object is rolled back.\n input:\n variantId: string\n input?: Record # variant-specific form values\n target: Space | Obj\n targetNodeId?: string\n output: Game\n errors:\n UnknownVariant: no registered GameVariant matches variantId\n effects: [echo:write]\n requires: [GameCapabilities.VariantProvider, SpaceOperation.AddObject, SpaceOperation.RemoveObjects]\n note: |\n Invoked by CreateObjectEntry.createObject.\n Variant state is added with `hidden: true` so it does not appear\n as a top-level item in the user's space graph.\n```\n\n## Features\n\n```mdl\nfeat F-1: Variant Registration\n\n req F-1.1:\n when: a variant plugin loads\n then: it contributes a GameVariant via `Capability.contributes(GameCapabilities.VariantProvider, …)`\n\n req F-1.2:\n when: GameCapabilities.VariantProvider contributions are queried\n then: all registered variants are returned as an ordered list\n```\n\n```mdl\nfeat F-2: Game Creation\n\n req F-2.1:\n when: user opens the create-object flow for type `org.dxos.type.game`\n then: CreateGamePanel is shown with a searchable list of available variants\n\n req F-2.2:\n when: user selects a variant with no inputSchema\n then: op:createGame is invoked immediately with empty input\n\n req F-2.3:\n when: user selects a variant that has an inputSchema\n then: a form rendered from the schema is shown; op:createGame is invoked on submit\n\n req F-2.4:\n when: op:createGame completes\n then: a Game object appears in the space graph; variant state is hidden\n```\n\n```mdl\nfeat F-3: Game Article / Card Rendering\n\n req F-3.1:\n when: Composer navigates to a Game object\n then: GameArticle resolves `game.variant` and delegates to the matching `variant.article`\n\n req F-3.2:\n when: a Game object is shown in a Card layout\n then: GameCard resolves `game.variant` and delegates to `variant.card`\n\n req F-3.3:\n when: no matching variant is found for the loaded state object\n then: GameArticle renders an \"Unsupported game variant\" fallback message\n\n req F-3.4:\n when: the variant ref is not yet loaded\n then: neither GameArticle nor GameCard renders (returns null)\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Variant picker shows all contributions\n given: two variant plugins have contributed GameVariants\n when: the user opens the create-game panel\n then:\n - CreateGamePanel lists both variants\n - variants are sorted alphabetically\n```\n\n```mdl\ntest T-2: Create game without form\n given: a variant with no inputSchema is selected\n when: user clicks the variant item\n then:\n - onCreateObject is called immediately with { variantId }\n - no form is shown\n```\n\n```mdl\ntest T-3: Create game with form\n given: a variant with an inputSchema is selected\n when: user fills and submits the form\n then:\n - onCreateObject is called with { variantId, input: <form values> }\n```\n\n```mdl\ntest T-4: Atomic game creation\n given: a valid variantId and target space\n when: op:createGame is invoked\n then:\n - variant state object added to space (hidden)\n - Game object added with game.variant pointing to the state object\n - both writes are visible to other peers via ECHO replication\n```\n\n```mdl\ntest T-5: Rollback on second write failure\n given: variant state is successfully written\n when: the second write (Game object) fails\n then:\n - RemoveObjects is invoked for the variant state\n - no orphaned state object remains in the space\n```\n\n```mdl\ntest T-6: Unknown variant rejected\n given: op:createGame is invoked with an unregistered variantId\n when: the handler runs\n then: UnknownVariant error is returned; no ECHO writes occur\n```\n\n```mdl\ntest T-7: Unsupported variant fallback\n given: a Game whose variant typename has no registered GameVariant\n when: GameArticle renders\n then: \"Unsupported game variant\" message is shown instead of crashing\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n";
7
- //#endregion
8
6
  //#region src/capabilities/index.ts
9
- var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"), { environments: [] });
7
+ var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"), { environments: ["browser", "tauri"] });
10
8
  var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: [
11
9
  "org.dxos.role.article",
12
10
  "org.dxos.role.cardContent",
@@ -19,7 +17,11 @@ var PluginAsset = AppCapability.pluginAsset({
19
17
  content: PLUGIN_default,
20
18
  mimeType: "application/x-mdl"
21
19
  });
22
- var Translations = AppCapability.translations(translations, { environments: ["node"] });
20
+ var Translations = AppCapability.translations(translations, { environments: [
21
+ "browser",
22
+ "node",
23
+ "tauri"
24
+ ] });
23
25
  //#endregion
24
26
  export { CreateObject, PluginAsset, ReactSurface, Schema, Translations };
25
27
 
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../../PLUGIN.mdl?raw';\n\n// Browser-only: the entry supplies `CreateGamePanel`, the React form that picks the variant and\n// collects its input.\nexport const CreateObject = SpaceCapability.createObject(() => import('./create-object'), {\n environments: [],\n});\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.cardContent', 'org.dxos.role.section'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema'));\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\nexport const Translations = AppCapability.translations(translations, {\n environments: ['node'],\n});\n"],"mappings":";;;;;;;;AAeA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,8BAAoB,EACxF,cAAc,CAAC,EACjB,CAAC;AACD,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAoB,EACjF,OAAO;CAAC;CAAyB;CAA6B;AAAuB,EACvF,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAW;AACnE,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AACD,IAAa,eAAe,cAAc,aAAa,cAAc,EACnE,cAAc,CAAC,MAAM,EACvB,CAAC"}
1
+ {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../../PLUGIN.mdl?raw';\n\n// Browser-only: the entry supplies `CreateGamePanel`, the React form that picks the variant and\n// collects its input.\nexport const CreateObject = SpaceCapability.createObject(() => import('./create-object.ts'), {\n environments: ['browser', 'tauri'],\n});\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface.ts'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.cardContent', 'org.dxos.role.section'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema.ts'));\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\nexport const Translations = AppCapability.translations(translations, {\n environments: ['browser', 'node', 'tauri'],\n});\n"],"mappings":";;;;;;AAeA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,8BAAuB,EAC3F,cAAc,CAAC,WAAW,OAAO,EACnC,CAAC;AACD,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAuB,EACpF,OAAO;CAAC;CAAyB;CAA6B;AAAuB,EACvF,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAc;AACtE,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AACD,IAAa,eAAe,cAAc,aAAa,cAAc,EACnE,cAAc;CAAC;CAAW;CAAQ;AAAO,EAC3C,CAAC"}
@@ -2,7 +2,11 @@ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
2
2
  import { translations } from "#translations";
3
3
  //#region src/capabilities/gen/node.ts
4
4
  var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
5
- var Translations = AppCapability.translations(translations, { environments: ["node"] });
5
+ var Translations = AppCapability.translations(translations, { environments: [
6
+ "browser",
7
+ "node",
8
+ "tauri"
9
+ ] });
6
10
  var CreateObject = void 0;
7
11
  var PluginAsset = void 0;
8
12
  var ReactSurface = void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations(translations, {\n environments: ['node'],\n});\n\nexport const CreateObject = undefined;\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAY;AAEpE,IAAa,eAAe,cAAc,aAAa,cAAc,EACnE,cAAc,CAAC,MAAM,EACvB,CAAC;AAED,IAAa,eAAe,KAAA;AAC5B,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
1
+ {"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations, {\n environments: ['browser', 'node', 'tauri'],\n});\n\nexport const CreateObject = undefined;\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,cAAc,EACnE,cAAc;CAAC;CAAW;CAAQ;AAAO,EAC3C,CAAC;AAED,IAAa,eAAe,KAAA;AAC5B,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
@@ -0,0 +1,28 @@
1
+ import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
2
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
+ import { translations } from "#translations";
4
+ import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
5
+ import { meta } from "#meta";
6
+ //#region src/capabilities/gen/tauri.ts
7
+ var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"), { environments: ["browser", "tauri"] });
8
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: [
9
+ "org.dxos.role.article",
10
+ "org.dxos.role.cardContent",
11
+ "org.dxos.role.section"
12
+ ] });
13
+ var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
14
+ var PluginAsset = AppCapability.pluginAsset({
15
+ pluginId: meta.profile.key,
16
+ path: "PLUGIN.mdl",
17
+ content: PLUGIN_default,
18
+ mimeType: "application/x-mdl"
19
+ });
20
+ var Translations = AppCapability.translations(translations, { environments: [
21
+ "browser",
22
+ "node",
23
+ "tauri"
24
+ ] });
25
+ //#endregion
26
+ export { CreateObject, PluginAsset, ReactSurface, Schema, Translations };
27
+
28
+ //# sourceMappingURL=capabilities.tauri.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.tauri.mjs","names":[],"sources":["../../src/capabilities/gen/tauri.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'tauri' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport pluginSpec from '../../../PLUGIN.mdl?raw';\n\n// Browser-only: the entry supplies `CreateGamePanel`, the React form that picks the variant and\n// collects its input.\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object.ts'), {\n environments: ['browser', 'tauri'],\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.cardContent', 'org.dxos.role.section'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\n\nexport const Translations = AppCapability.translations(translations, {\n environments: ['browser', 'node', 'tauri'],\n});\n"],"mappings":";;;;;;AAgBA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,8BAAwB,EAC5F,cAAc,CAAC,WAAW,OAAO,EACnC,CAAC;AAED,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO;CAAC;CAAyB;CAA6B;AAAuB,EACvF,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AAED,IAAa,eAAe,cAAc,aAAa,cAAc,EACnE,cAAc;CAAC;CAAW;CAAQ;AAAO,EAC3C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const CreateObject = undefined;\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\nexport const Translations = undefined;\n"],"mappings":";;AAQA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAY;AAEpE,IAAa,eAAe,KAAA;AAC5B,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA;AAC5B,IAAa,eAAe,KAAA"}
1
+ {"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const CreateObject = undefined;\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\nexport const Translations = undefined;\n"],"mappings":";;AAQA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,KAAA;AAC5B,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA;AAC5B,IAAa,eAAe,KAAA"}
@@ -1,15 +1,15 @@
1
1
  import { meta } from "#meta";
2
+ import { Obj } from "@dxos/echo";
2
3
  import { GameCapabilities } from "#types";
3
4
  import "react";
4
- import { useCapabilities } from "@dxos/app-framework/ui";
5
- import { Obj } from "@dxos/echo";
6
- import { useTranslation } from "@dxos/react-ui";
5
+ import * as Hooks from "@dxos/app-framework/Hooks";
6
+ import * as UiHooks from "@dxos/react-ui/Hooks";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { useResolveRef } from "@dxos/echo-react";
9
9
  //#region src/containers/GameArticle/GameArticle.tsx
10
10
  var GameArticle = ({ role, attendableId, subject: game }) => {
11
- const { t } = useTranslation(meta.profile.key);
12
- const variants = useCapabilities(GameCapabilities.VariantProvider);
11
+ const { t } = UiHooks.useTranslation(meta.profile.key);
12
+ const variants = Hooks.useCapabilities(GameCapabilities.VariantProvider);
13
13
  const variant = useResolveRef(game.variant);
14
14
  if (!variant) return null;
15
15
  const variantTypename = Obj.getTypename(variant);
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-GameArticle.mjs","names":[],"sources":["../../src/containers/GameArticle/GameArticle.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { useCapabilities } from '@dxos/app-framework/ui';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { Obj } from '@dxos/echo';\nimport { useResolveRef } from '@dxos/echo-react';\nimport { useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\nimport { Game, GameCapabilities } from '#types';\n\nexport type GameArticleProps = AppSurface.ObjectArticleProps<Game.Game>;\n\nexport const GameArticle = ({ role, attendableId, subject: game }: GameArticleProps) => {\n const { t } = useTranslation(meta.profile.key);\n const variants = useCapabilities(GameCapabilities.VariantProvider);\n // Resolved live rather than as a snapshot: variants mutate their state, and a snapshot is frozen.\n const variant = useResolveRef(game.variant);\n\n if (!variant) {\n return null;\n }\n\n const variantTypename = Obj.getTypename(variant);\n const match = variants.find((v) => v.id === variantTypename);\n if (!match?.article) {\n return (\n <div className='p-4 text-sm'>{t('unsupported-variant.label', { defaultValue: 'Unsupported game variant' })}</div>\n );\n }\n\n const Component = match.article;\n return <Component game={game} variant={variant} role={role} attendableId={attendableId} />;\n};\n\nGameArticle.displayName = 'GameArticle';\n"],"mappings":";;;;;;;;;AAiBA,IAAa,eAAe,EAAE,MAAM,cAAc,SAAS,WAA6B;CACtF,MAAM,EAAE,MAAM,eAAe,KAAK,QAAQ,GAAG;CAC7C,MAAM,WAAW,gBAAgB,iBAAiB,eAAe;CAEjE,MAAM,UAAU,cAAc,KAAK,OAAO;CAE1C,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,kBAAkB,IAAI,YAAY,OAAO;CAC/C,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,OAAO,eAAe;CAC3D,IAAI,CAAC,OAAO,SACV,OACE,oBAAC,OAAD;EAAK,WAAU;EAAe,UAAA,EAAE,6BAA6B,EAAE,cAAc,2BAA2B,CAAC;CAAO,CAAA;CAIpH,MAAM,YAAY,MAAM;CACxB,OAAO,oBAAC,WAAD;EAAiB;EAAe;EAAe;EAAoB;CAAe,CAAA;AAC3F;AAEA,YAAY,cAAc"}
1
+ {"version":3,"file":"chunk-GameArticle.mjs","names":[],"sources":["../../src/containers/GameArticle/GameArticle.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { Obj } from '@dxos/echo';\nimport { useResolveRef } from '@dxos/echo-react';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\n\nimport { meta } from '#meta';\nimport { Game, GameCapabilities } from '#types';\n\nexport type GameArticleProps = AppSurface.ObjectArticleProps<Game.Game>;\n\nexport const GameArticle = ({ role, attendableId, subject: game }: GameArticleProps) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n const variants = Hooks.useCapabilities(GameCapabilities.VariantProvider);\n // Resolved live rather than as a snapshot: variants mutate their state, and a snapshot is frozen.\n const variant = useResolveRef(game.variant);\n\n if (!variant) {\n return null;\n }\n\n const variantTypename = Obj.getTypename(variant);\n const match = variants.find((v) => v.id === variantTypename);\n if (!match?.article) {\n return (\n <div className='p-4 text-sm'>{t('unsupported-variant.label', { defaultValue: 'Unsupported game variant' })}</div>\n );\n }\n\n const Component = match.article;\n return <Component game={game} variant={variant} role={role} attendableId={attendableId} />;\n};\n\nGameArticle.displayName = 'GameArticle';\n"],"mappings":";;;;;;;;;AAiBA,IAAa,eAAe,EAAE,MAAM,cAAc,SAAS,WAA6B;CACtF,MAAM,EAAE,MAAM,QAAQ,eAAe,KAAK,QAAQ,GAAG;CACrD,MAAM,WAAW,MAAM,gBAAgB,iBAAiB,eAAe;CAEvE,MAAM,UAAU,cAAc,KAAK,OAAO;CAE1C,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,kBAAkB,IAAI,YAAY,OAAO;CAC/C,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,OAAO,eAAe;CAC3D,IAAI,CAAC,OAAO,SACV,OACE,oBAAC,OAAD;EAAK,WAAU;EAAe,UAAA,EAAE,6BAA6B,EAAE,cAAc,2BAA2B,CAAC;CAAO,CAAA;CAIpH,MAAM,YAAY,MAAM;CACxB,OAAO,oBAAC,WAAD;EAAiB;EAAe;EAAe;EAAoB;CAAe,CAAA;AAC3F;AAEA,YAAY,cAAc"}
@@ -1,12 +1,12 @@
1
+ import { Obj } from "@dxos/echo";
1
2
  import { GameCapabilities } from "#types";
2
3
  import "react";
3
- import { useCapabilities } from "@dxos/app-framework/ui";
4
- import { Obj } from "@dxos/echo";
4
+ import * as Hooks from "@dxos/app-framework/Hooks";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { useResolveRef } from "@dxos/echo-react";
7
7
  //#region src/containers/GameCard/GameCard.tsx
8
8
  var GameCard = ({ role, subject: game }) => {
9
- const variants = useCapabilities(GameCapabilities.VariantProvider);
9
+ const variants = Hooks.useCapabilities(GameCapabilities.VariantProvider);
10
10
  const variant = useResolveRef(game.variant);
11
11
  if (!variant) return null;
12
12
  const variantTypename = Obj.getTypename(variant);
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-GameCard.mjs","names":[],"sources":["../../src/containers/GameCard/GameCard.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { useCapabilities } from '@dxos/app-framework/ui';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { Obj } from '@dxos/echo';\nimport { useResolveRef } from '@dxos/echo-react';\n\nimport { Game, GameCapabilities } from '#types';\n\nexport type GameCardProps = AppSurface.ObjectCardProps<Game.Game>;\n\nexport const GameCard = ({ role, subject: game }: GameCardProps) => {\n const variants = useCapabilities(GameCapabilities.VariantProvider);\n // Resolved live rather than as a snapshot: variants mutate their state, and a snapshot is frozen.\n const variant = useResolveRef(game.variant);\n\n if (!variant) {\n return null;\n }\n\n const variantTypename = Obj.getTypename(variant);\n const match = variants.find((v) => v.id === variantTypename);\n if (!match?.card) {\n return null;\n }\n\n const Component = match.card;\n return <Component game={game} variant={variant} role={role} />;\n};\n\nGameCard.displayName = 'GameCard';\n"],"mappings":";;;;;;;AAeA,IAAa,YAAY,EAAE,MAAM,SAAS,WAA0B;CAClE,MAAM,WAAW,gBAAgB,iBAAiB,eAAe;CAEjE,MAAM,UAAU,cAAc,KAAK,OAAO;CAE1C,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,kBAAkB,IAAI,YAAY,OAAO;CAC/C,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,OAAO,eAAe;CAC3D,IAAI,CAAC,OAAO,MACV,OAAO;CAGT,MAAM,YAAY,MAAM;CACxB,OAAO,oBAAC,WAAD;EAAiB;EAAe;EAAe;CAAO,CAAA;AAC/D;AAEA,SAAS,cAAc"}
1
+ {"version":3,"file":"chunk-GameCard.mjs","names":[],"sources":["../../src/containers/GameCard/GameCard.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { Obj } from '@dxos/echo';\nimport { useResolveRef } from '@dxos/echo-react';\n\nimport { Game, GameCapabilities } from '#types';\n\nexport type GameCardProps = AppSurface.ObjectCardProps<Game.Game>;\n\nexport const GameCard = ({ role, subject: game }: GameCardProps) => {\n const variants = Hooks.useCapabilities(GameCapabilities.VariantProvider);\n // Resolved live rather than as a snapshot: variants mutate their state, and a snapshot is frozen.\n const variant = useResolveRef(game.variant);\n\n if (!variant) {\n return null;\n }\n\n const variantTypename = Obj.getTypename(variant);\n const match = variants.find((v) => v.id === variantTypename);\n if (!match?.card) {\n return null;\n }\n\n const Component = match.card;\n return <Component game={game} variant={variant} role={role} />;\n};\n\nGameCard.displayName = 'GameCard';\n"],"mappings":";;;;;;;AAeA,IAAa,YAAY,EAAE,MAAM,SAAS,WAA0B;CAClE,MAAM,WAAW,MAAM,gBAAgB,iBAAiB,eAAe;CAEvE,MAAM,UAAU,cAAc,KAAK,OAAO;CAE1C,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,kBAAkB,IAAI,YAAY,OAAO;CAC/C,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,OAAO,eAAe;CAC3D,IAAI,CAAC,OAAO,MACV,OAAO;CAGT,MAAM,YAAY,MAAM;CACxB,OAAO,oBAAC,WAAD;EAAiB;EAAe;EAAe;CAAO,CAAA;AAC/D;AAEA,SAAS,cAAc"}
@@ -0,0 +1,6 @@
1
+ //#region PLUGIN.mdl?raw
2
+ var PLUGIN_default = "---\nid: org.dxos.plugin.game\nname: GamePlugin\nversion: 0.1.0\n---\n\nA generic game host plugin for `DXOS` Composer that provides shared infrastructure for\nmulti-player, local-first turn-based games. Variant plugins (e.g. plugin-chess,\nplugin-tictactoe) contribute a `GameVariant` capability and game-specific surfaces;\nplugin-game supplies the `Game` ECHO type, create flow, and dispatch scaffolding.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `scenario` | `org.dxos.mdl.scenario@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n\n## Types\n\n```mdl\ntype Player\n fields:\n role: string # variant-defined (e.g. \"white\", \"x\"); required\n identity?: string # DID of a human player; absent for AI / empty seats\n name?: string # display label for AI or guest players\n```\n\n```mdl\ntype Game\n fields:\n name?: string\n players?: Player[] # populated during or after creation\n variant: Ref<Unknown> # reference to variant-specific state object (e.g. Chess.State)\n typename: org.dxos.type.game\n version: 0.1.0\n```\n\n```mdl\ntype GameVariant\n desc: |\n Contribution interface from a variant plugin. Registered via\n `Capability.contributes(GameCapabilities.VariantProvider, variant)`.\n fields:\n id: string # stable variant id; matches the variant state typename\n label: string # human-readable name shown in the picker\n icon?: string # Phosphor icon name\n variantSchema: Schema # Effect/Schema for the ECHO state object\n inputSchema?: Schema # optional form schema shown after variant is selected\n roles: string[] # player roles (e.g. [\"white\", \"black\"])\n createVariant: Effect # factory that builds the variant ECHO state object\n card?: ComponentType # optional Card surface component\n article?: ComponentType # optional Article/Section surface component\n```\n\n## Components\n\n```mdl\ncomponent CreateGamePanel\n desc: |\n Two-stage creation panel. Stage 1 shows a searchable variant picker.\n Stage 2 (optional) shows a form rendered from the variant's inputSchema.\n props:\n target: Space | Obj\n onCreateObject: (args: { variantId: string; input?: Record }) => void\n variants?: GameVariant[] # override; defaults to GameCapabilities.VariantProvider\n state:\n selectedId?: string # id of the variant chosen in stage 1\n actions:\n selectVariant(id: string)\n submitForm(input: Record)\n layout: |\n ┌─────────────────────────┐\n │ Stage 1: Variant Picker │\n │ ┌───────────────────┐ │\n │ │ Search input │ │\n │ ├───────────────────┤ │\n │ │ variant row │ │\n │ │ variant row │ │\n │ └───────────────────┘ │\n ├─────────────────────────┤\n │ Stage 2: Input Form │\n │ (rendered from schema) │\n │ [Submit] │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent GameArticle\n desc: |\n Article / Section surface for Game objects. Resolves the variant via\n `game.variant` ref, finds the matching `GameVariant` by typename, and\n delegates rendering to `variant.article`.\n props:\n game: Game\n role?: string\n attendableId?: string\n state:\n variant: Unknown # live variant state object from the Ref\n match?: GameVariant # resolved GameVariant from capabilities\n layout: |\n ┌─────────────────────────┐\n │ [variant.article] │\n │ (fallback: unsupported │\n │ variant message) │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent GameCard\n desc: |\n Card surface for Game objects. Follows the same variant-resolution\n logic as GameArticle and delegates to `variant.card`.\n props:\n game: Game\n role?: string\n state:\n variant: Unknown\n match?: GameVariant\n```\n\n## Operations\n\n```mdl\nop createGame\n desc: |\n Creates a variant state object via `variant.createVariant`, adds it\n to the space as a hidden object, then wraps it in a top-level Game.\n If the second write fails the variant state object is rolled back.\n input:\n variantId: string\n input?: Record # variant-specific form values\n target: Space | Obj\n targetNodeId?: string\n output: Game\n errors:\n UnknownVariant: no registered GameVariant matches variantId\n effects: [echo:write]\n requires: [GameCapabilities.VariantProvider, SpaceOperation.AddObject, SpaceOperation.RemoveObjects]\n note: |\n Invoked by CreateObjectEntry.createObject.\n Variant state is added with `hidden: true` so it does not appear\n as a top-level item in the user's space graph.\n```\n\n## Features\n\n```mdl\nfeat F-1: Variant Registration\n\n req F-1.1:\n when: a variant plugin loads\n then: it contributes a GameVariant via `Capability.contributes(GameCapabilities.VariantProvider, …)`\n\n req F-1.2:\n when: GameCapabilities.VariantProvider contributions are queried\n then: all registered variants are returned as an ordered list\n```\n\n```mdl\nfeat F-2: Game Creation\n\n req F-2.1:\n when: user opens the create-object flow for type `org.dxos.type.game`\n then: CreateGamePanel is shown with a searchable list of available variants\n\n req F-2.2:\n when: user selects a variant with no inputSchema\n then: op:createGame is invoked immediately with empty input\n\n req F-2.3:\n when: user selects a variant that has an inputSchema\n then: a form rendered from the schema is shown; op:createGame is invoked on submit\n\n req F-2.4:\n when: op:createGame completes\n then: a Game object appears in the space graph; variant state is hidden\n```\n\n```mdl\nfeat F-3: Game Article / Card Rendering\n\n req F-3.1:\n when: Composer navigates to a Game object\n then: GameArticle resolves `game.variant` and delegates to the matching `variant.article`\n\n req F-3.2:\n when: a Game object is shown in a Card layout\n then: GameCard resolves `game.variant` and delegates to `variant.card`\n\n req F-3.3:\n when: no matching variant is found for the loaded state object\n then: GameArticle renders an \"Unsupported game variant\" fallback message\n\n req F-3.4:\n when: the variant ref is not yet loaded\n then: neither GameArticle nor GameCard renders (returns null)\n```\n\n## Acceptance\n\n```mdl\nscenario T-1: Variant picker shows all contributions\n given: two variant plugins have contributed GameVariants\n when: the user opens the create-game panel\n then:\n - CreateGamePanel lists both variants\n - variants are sorted alphabetically\n```\n\n```mdl\nscenario T-2: Create game without form\n given: a variant with no inputSchema is selected\n when: user clicks the variant item\n then:\n - onCreateObject is called immediately with { variantId }\n - no form is shown\n```\n\n```mdl\nscenario T-3: Create game with form\n given: a variant with an inputSchema is selected\n when: user fills and submits the form\n then:\n - onCreateObject is called with { variantId, input: <form values> }\n```\n\n```mdl\nscenario T-4: Atomic game creation\n given: a valid variantId and target space\n when: op:createGame is invoked\n then:\n - variant state object added to space (hidden)\n - Game object added with game.variant pointing to the state object\n - both writes are visible to other peers via ECHO replication\n```\n\n```mdl\nscenario T-5: Rollback on second write failure\n given: variant state is successfully written\n when: the second write (Game object) fails\n then:\n - RemoveObjects is invoked for the variant state\n - no orphaned state object remains in the space\n```\n\n```mdl\nscenario T-6: Unknown variant rejected\n given: op:createGame is invoked with an unregistered variantId\n when: the handler runs\n then: UnknownVariant error is returned; no ECHO writes occur\n```\n\n```mdl\nscenario T-7: Unsupported variant fallback\n given: a Game whose variant typename has no registered GameVariant\n when: GameArticle renders\n then: \"Unsupported game variant\" message is shown instead of crashing\n```\n\n---\n\n## QA\n\nExecutable tests. Each step carries a human instruction (`do`) and, where the step maps to an\ninvocable operation, an agent binding (`invoke`). Report failures by test and step number.\n\n```mdl\ntest QA-1: Create a game through the variant picker and render it\n desc: |\n The generic host round-trip — pick a registered variant, create the Game wrapper plus its\n hidden variant state, and confirm dispatch renders the variant's article. Uses\n plugin-chess's variant since it is the only one shipped and enabled by default; the test\n tests plugin-game's dispatch, not chess's own gameplay (see plugin-chess's QA for that).\n actors: human\n covers: [F-1.2, F-2.1, F-2.4, F-3.1, T-2, T-4]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - collection: a collection in that space, to create into\n - plugin-chess (or another GameVariant contributor) is enabled, so the picker lists at least one variant\n - no game named \"QA Game\" exists — ABORT rather than deleting one this test did not create\n - game: (bound during the run, not beforehand) the Game object the \"Select the Chess variant\" step creates — recorded by the tester, since no invoke captures it\n steps:\n - name: Open the create-game panel\n do: In the navtree, click + on a collection and choose Game.\n expect: CreateGamePanel opens showing a searchable list including a \"Chess\" row\n note: |\n NO `invoke` — `createGame` is registered as a `CreateObjectEntry.customPanel`, not an\n `Operation.make` with a runtime key, so there is no key to invoke through the operation\n invoker; opening the panel is itself the create-object gesture (see `code-style`/`operations`\n conventions: only `Operation.make` instances get a DXN, and this factory is not one).\n - name: Select the Chess variant\n do: Click the \"Chess\" row (it has no inputSchema, so no form stage follows).\n expect: a Game object appears under the collection, named after the default, with hidden chess variant state referenced by `game.variant`\n note: |\n NO `invoke` for the same reason as the previous step — this whole creation gesture is\n human-only until `createGame` (or an equivalent) is exposed as an invocable operation.\n Because nothing here is scriptable, there is no captured result to thread forward; the\n tester notes the new Game's id manually for the `after` stage below.\n - name: Open the game and confirm dispatch\n do: Click the Game row in the navtree.\n expect: GameArticle resolves `game.variant` to the chess state object and delegates to chess's own article — a starting chess position renders, not the \"Unsupported game variant\" fallback\n after:\n - name: Delete the game\n do: Right-click the Game row in the navtree and choose Delete.\n invoke: org.dxos.operation.space.removeObjects { objects: [$given.game], target: $given.collection }\n expect: the Game row leaves the navtree, its plank closes if open, and the hidden variant state object is also removed\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next scenario\n uri: org.dxos.mdl.scenario@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n";
3
+ //#endregion
4
+ export { PLUGIN_default as t };
5
+
6
+ //# sourceMappingURL=chunk-PLUGIN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-PLUGIN.mjs","names":[],"sources":[],"mappings":""}
@@ -1,6 +1,6 @@
1
- import { Game, GameCapabilities } from "#types";
2
- import { Database, Type } from "@dxos/echo";
3
1
  import * as Effect from "effect/Effect";
2
+ import { Database, Type } from "@dxos/echo";
3
+ import { Game, GameCapabilities } from "#types";
4
4
  import * as Capability from "@dxos/app-framework/Capability";
5
5
  import * as Operation from "@dxos/compute/Operation";
6
6
  import { invariant } from "@dxos/invariant";
@@ -34,7 +34,7 @@ var create_object_default = Capability.makeModule(Effect.fnUntraced(function* ()
34
34
  return yield* Operation.invoke(SpaceOperation.AddObject, {
35
35
  object: game,
36
36
  target: options.target
37
- }, { spaceId: options.db.spaceId }).pipe(Effect.tapError(() => Operation.invoke(SpaceOperation.RemoveObjects, { objects: [stateObject] }).pipe(Effect.ignore)));
37
+ }, { spaceId: options.db.spaceId }).pipe(Effect.tapError(() => Operation.invoke(SpaceOperation.RemoveObjects, { objects: [stateObject] }, { spaceId: options.db.spaceId }).pipe(Effect.ignore)));
38
38
  })
39
39
  });
40
40
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-create-object.mjs","names":[],"sources":["../../src/capabilities/create-object.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Database, Type } from '@dxos/echo';\nimport { invariant } from '@dxos/invariant';\nimport * as SpaceCapabilities from '@dxos/plugin-space/SpaceCapabilities';\nimport * as SpaceOperation from '@dxos/plugin-space/SpaceOperation';\n\nimport { CreateGamePanel } from '#components';\nimport { Game, GameCapabilities } from '#types';\n\ntype CreateOptions = Parameters<SpaceCapabilities.CreateObjectEntry['createObject']>[1];\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contribute(SpaceCapabilities.CreateObjectEntry, {\n id: Type.getTypename(Game.Game),\n customPanel: CreateGamePanel,\n createObject: (\n { variantId, input }: { variantId: string; input?: Record<string, any> },\n options: CreateOptions,\n ) =>\n Effect.gen(function* () {\n const variants = yield* Capability.getAll(GameCapabilities.VariantProvider);\n const variant = variants.find((v) => v.id === variantId);\n invariant(variant, `Unknown game variant: ${variantId}`);\n\n // Build variant state object via the variant's factory.\n const stateObject = yield* variant\n .createVariant(input ?? {})\n .pipe(Effect.provideService(Database.Service, Database.makeService(options.db)));\n\n // Add variant state to the database. Stays hidden — it's referenced by Game and\n // shouldn't appear as a top-level item in the user's space.\n yield* Operation.invoke(\n SpaceOperation.AddObject,\n {\n object: stateObject,\n target: options.target,\n },\n { spaceId: options.db.spaceId },\n );\n\n const game = Game.make({\n name: typeof input?.name === 'string' ? input.name : undefined,\n variant: stateObject,\n });\n\n // Add the user-facing Game wrapper. Not hidden — this is the object the user sees\n // and navigates to. If this second write fails, roll back the variant state so we\n // don't leak an orphaned object into the space.\n return yield* Operation.invoke(\n SpaceOperation.AddObject,\n {\n object: game,\n target: options.target,\n },\n { spaceId: options.db.spaceId },\n ).pipe(\n Effect.tapError(() =>\n Operation.invoke(SpaceOperation.RemoveObjects, { objects: [stateObject] }).pipe(Effect.ignore),\n ),\n );\n }),\n });\n }),\n);\n"],"mappings":";;;;;;;;;;;AAkBA,IAAA,wBAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,WAAW,kBAAkB,mBAAmB;EAChE,IAAI,KAAK,YAAY,KAAK,IAAI;EAC9B,aAAa;EACb,eACE,EAAE,WAAW,SACb,YAEA,OAAO,IAAI,aAAa;GAEtB,MAAM,WAAU,OADQ,WAAW,OAAO,iBAAiB,eAAe,EAAA,CACjD,MAAM,MAAM,EAAE,OAAO,SAAS;GACvD,UAAU,SAAS,yBAAyB,aAAU;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,CAAA,WAAA,sCAAA;GAAA,CAAC;GAGvD,MAAM,cAAc,OAAO,QACxB,cAAc,SAAS,CAAC,CAAC,CAAA,CACzB,KAAK,OAAO,eAAe,SAAS,SAAS,SAAS,YAAY,QAAQ,EAAE,CAAC,CAAC;GAIjF,OAAO,UAAU,OACf,eAAe,WACf;IACE,QAAQ;IACR,QAAQ,QAAQ;GAClB,GACA,EAAE,SAAS,QAAQ,GAAG,QAAQ,CAChC;GAEA,MAAM,OAAO,KAAK,KAAK;IACrB,MAAM,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO,KAAA;IACrD,SAAS;GACX,CAAC;GAKD,OAAO,OAAO,UAAU,OACtB,eAAe,WACf;IACE,QAAQ;IACR,QAAQ,QAAQ;GAClB,GACA,EAAE,SAAS,QAAQ,GAAG,QAAQ,CAChC,CAAC,CAAC,KACA,OAAO,eACL,UAAU,OAAO,eAAe,eAAe,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM,CAC/F,CACF;EACF,CAAC;CACL,CAAC;AACH,CAAC,CACH"}
1
+ {"version":3,"file":"chunk-create-object.mjs","names":[],"sources":["../../src/capabilities/create-object.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Database, Type } from '@dxos/echo';\nimport { invariant } from '@dxos/invariant';\nimport * as SpaceCapabilities from '@dxos/plugin-space/SpaceCapabilities';\nimport * as SpaceOperation from '@dxos/plugin-space/SpaceOperation';\n\nimport { CreateGamePanel } from '#components';\nimport { Game, GameCapabilities } from '#types';\n\ntype CreateOptions = Parameters<SpaceCapabilities.CreateObjectEntry['createObject']>[1];\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contribute(SpaceCapabilities.CreateObjectEntry, {\n id: Type.getTypename(Game.Game),\n customPanel: CreateGamePanel,\n createObject: (\n { variantId, input }: { variantId: string; input?: Record<string, any> },\n options: CreateOptions,\n ) =>\n Effect.gen(function* () {\n const variants = yield* Capability.getAll(GameCapabilities.VariantProvider);\n const variant = variants.find((v) => v.id === variantId);\n invariant(variant, `Unknown game variant: ${variantId}`);\n\n // Build variant state object via the variant's factory.\n const stateObject = yield* variant\n .createVariant(input ?? {})\n .pipe(Effect.provideService(Database.Service, Database.makeService(options.db)));\n\n // Add variant state to the database. Stays hidden — it's referenced by Game and\n // shouldn't appear as a top-level item in the user's space.\n yield* Operation.invoke(\n SpaceOperation.AddObject,\n {\n object: stateObject,\n target: options.target,\n },\n { spaceId: options.db.spaceId },\n );\n\n const game = Game.make({\n name: typeof input?.name === 'string' ? input.name : undefined,\n variant: stateObject,\n });\n\n // Add the user-facing Game wrapper. Not hidden — this is the object the user sees\n // and navigates to. If this second write fails, roll back the variant state so we\n // don't leak an orphaned object into the space.\n return yield* Operation.invoke(\n SpaceOperation.AddObject,\n {\n object: game,\n target: options.target,\n },\n { spaceId: options.db.spaceId },\n ).pipe(\n Effect.tapError(() =>\n Operation.invoke(\n SpaceOperation.RemoveObjects,\n { objects: [stateObject] },\n { spaceId: options.db.spaceId },\n ).pipe(Effect.ignore),\n ),\n );\n }),\n });\n }),\n);\n"],"mappings":";;;;;;;;;;;AAkBA,IAAA,wBAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,WAAW,kBAAkB,mBAAmB;EAChE,IAAI,KAAK,YAAY,KAAK,IAAI;EAC9B,aAAa;EACb,eACE,EAAE,WAAW,SACb,YAEA,OAAO,IAAI,aAAa;GAEtB,MAAM,WAAU,OADQ,WAAW,OAAO,iBAAiB,eAAe,EAAA,CACjD,MAAM,MAAM,EAAE,OAAO,SAAS;GACvD,UAAU,SAAS,yBAAyB,aAAU;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,CAAA,WAAA,sCAAA;GAAA,CAAC;GAGvD,MAAM,cAAc,OAAO,QACxB,cAAc,SAAS,CAAC,CAAC,CAAA,CACzB,KAAK,OAAO,eAAe,SAAS,SAAS,SAAS,YAAY,QAAQ,EAAE,CAAC,CAAC;GAIjF,OAAO,UAAU,OACf,eAAe,WACf;IACE,QAAQ;IACR,QAAQ,QAAQ;GAClB,GACA,EAAE,SAAS,QAAQ,GAAG,QAAQ,CAChC;GAEA,MAAM,OAAO,KAAK,KAAK;IACrB,MAAM,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO,KAAA;IACrD,SAAS;GACX,CAAC;GAKD,OAAO,OAAO,UAAU,OACtB,eAAe,WACf;IACE,QAAQ;IACR,QAAQ,QAAQ;GAClB,GACA,EAAE,SAAS,QAAQ,GAAG,QAAQ,CAChC,CAAC,CAAC,KACA,OAAO,eACL,UAAU,OACR,eAAe,eACf,EAAE,SAAS,CAAC,WAAW,EAAE,GACzB,EAAE,SAAS,QAAQ,GAAG,QAAQ,CAChC,CAAC,CAAC,KAAK,OAAO,MAAM,CACtB,CACF;EACF,CAAC;CACL,CAAC;AACH,CAAC,CACH"}
@@ -1,9 +1,9 @@
1
- import { Game } from "#types";
2
- import { Surface } from "@dxos/app-framework/ui";
3
1
  import * as Effect from "effect/Effect";
2
+ import { Game } from "#types";
4
3
  import * as Capability from "@dxos/app-framework/Capability";
5
4
  import * as Capabilities from "@dxos/app-framework/Capabilities";
6
- import { AppSurface } from "@dxos/app-toolkit/ui";
5
+ import * as Surface from "@dxos/app-framework/Surface";
6
+ import * as AppSurface from "@dxos/app-toolkit/AppSurface";
7
7
  import { GameArticle, GameCard } from "#containers";
8
8
  //#region src/capabilities/react-surface.ts
9
9
  var react_surface_default = Capability.makeModule(() => Effect.succeed(Capability.contribute(Capabilities.ReactSurface, [Surface.create({
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-react-surface.mjs","names":[],"sources":["../../src/capabilities/react-surface.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\n\nimport { GameArticle, GameCard } from '#containers';\nimport { Game } from '#types';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contribute(Capabilities.ReactSurface, [\n Surface.create({\n id: 'game',\n filter: AppSurface.oneOf(\n AppSurface.object(AppSurface.Article, Game.Game),\n AppSurface.object(AppSurface.Section, Game.Game),\n ),\n component: GameArticle,\n props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),\n }),\n Surface.create({\n id: 'gameCard',\n filter: AppSurface.object(AppSurface.CardContent, Game.Game),\n component: GameCard,\n props: ({ role, data: { subject } }) => ({ role, subject }),\n }),\n ]),\n ),\n);\n"],"mappings":";;;;;;;;AAcA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,WAAW,aAAa,cAAc,CAC/C,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,MACjB,WAAW,OAAO,WAAW,SAAS,KAAK,IAAI,GAC/C,WAAW,OAAO,WAAW,SAAS,KAAK,IAAI,CACjD;CACA,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,sBAAsB;EAAE;EAAM;EAAS;CAAa;AACvF,CAAC,GACD,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,OAAO,WAAW,aAAa,KAAK,IAAI;CAC3D,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,iBAAiB;EAAE;EAAM;CAAQ;AAC3D,CAAC,CACH,CAAC,CACH,CACF"}
1
+ {"version":3,"file":"chunk-react-surface.mjs","names":[],"sources":["../../src/capabilities/react-surface.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Surface from '@dxos/app-framework/Surface';\nimport * as AppSurface from '@dxos/app-toolkit/AppSurface';\n\nimport { GameArticle, GameCard } from '#containers';\nimport { Game } from '#types';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contribute(Capabilities.ReactSurface, [\n Surface.create({\n id: 'game',\n filter: AppSurface.oneOf(\n AppSurface.object(AppSurface.Article, Game.Game),\n AppSurface.object(AppSurface.Section, Game.Game),\n ),\n component: GameArticle,\n props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),\n }),\n Surface.create({\n id: 'gameCard',\n filter: AppSurface.object(AppSurface.CardContent, Game.Game),\n component: GameCard,\n props: ({ role, data: { subject } }) => ({ role, subject }),\n }),\n ]),\n ),\n);\n"],"mappings":";;;;;;;;AAcA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,WAAW,aAAa,cAAc,CAC/C,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,MACjB,WAAW,OAAO,WAAW,SAAS,KAAK,IAAI,GAC/C,WAAW,OAAO,WAAW,SAAS,KAAK,IAAI,CACjD;CACA,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,sBAAsB;EAAE;EAAM;EAAS;CAAa;AACvF,CAAC,GACD,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,OAAO,WAAW,aAAa,KAAK,IAAI;CAC3D,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,iBAAiB;EAAE;EAAM;CAAQ;AAC3D,CAAC,CACH,CAAC,CACH,CACF"}