@dxos/plugin-game 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/lib/GamePlugin.mjs +18 -0
  2. package/dist/lib/GamePlugin.mjs.map +1 -0
  3. package/dist/lib/GamePlugin.node.mjs +17 -0
  4. package/dist/lib/GamePlugin.node.mjs.map +1 -0
  5. package/dist/lib/GamePlugin.workerd.mjs +10 -0
  6. package/dist/lib/GamePlugin.workerd.mjs.map +1 -0
  7. package/dist/lib/capabilities.mjs +8 -0
  8. package/dist/lib/capabilities.mjs.map +1 -0
  9. package/dist/lib/chunk-GameArticle.mjs +33 -0
  10. package/dist/lib/chunk-GameArticle.mjs.map +1 -0
  11. package/dist/lib/chunk-GameCard.mjs +26 -0
  12. package/dist/lib/chunk-GameCard.mjs.map +1 -0
  13. package/dist/lib/chunk-create-object.mjs +45 -0
  14. package/dist/lib/chunk-create-object.mjs.map +1 -0
  15. package/dist/lib/chunk-load-game.mjs +38 -0
  16. package/dist/lib/chunk-load-game.mjs.map +1 -0
  17. package/dist/lib/{neutral/chunk-R4V5PVU7.mjs → chunk-meta.mjs} +20 -28
  18. package/dist/lib/chunk-meta.mjs.map +1 -0
  19. package/dist/lib/chunk-react-surface.mjs +29 -0
  20. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  21. package/dist/lib/chunk-types.mjs +83 -0
  22. package/dist/lib/chunk-types.mjs.map +1 -0
  23. package/dist/lib/components.mjs +82 -0
  24. package/dist/lib/components.mjs.map +1 -0
  25. package/dist/lib/containers.mjs +8 -0
  26. package/dist/lib/containers.mjs.map +1 -0
  27. package/dist/lib/index.mjs +4 -0
  28. package/dist/lib/meta.mjs +2 -0
  29. package/dist/lib/plugin.mjs +8 -0
  30. package/dist/lib/plugin.mjs.map +1 -0
  31. package/dist/lib/translations.mjs +28 -0
  32. package/dist/lib/translations.mjs.map +1 -0
  33. package/dist/lib/types.mjs +3 -0
  34. package/dist/lib/util.mjs +2 -0
  35. package/dist/types/src/GamePlugin.workerd.d.ts.map +1 -1
  36. package/dist/types/src/capabilities/create-object.d.ts +1 -1
  37. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  38. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  39. package/dist/types/src/components/CreateGamePanel.stories.d.ts.map +1 -1
  40. package/dist/types/src/containers/GameArticle/GameArticle.d.ts +5 -2
  41. package/dist/types/src/containers/GameArticle/GameArticle.d.ts.map +1 -1
  42. package/dist/types/src/containers/GameCard/GameCard.d.ts +5 -2
  43. package/dist/types/src/containers/GameCard/GameCard.d.ts.map +1 -1
  44. package/dist/types/src/types/Game.d.ts.map +1 -1
  45. package/dist/types/src/types/index.d.ts +1 -1
  46. package/dist/types/src/types/index.d.ts.map +1 -1
  47. package/dist/types/src/types/types.d.ts +9 -5
  48. package/dist/types/src/types/types.d.ts.map +1 -1
  49. package/dist/types/src/util/load-game.d.ts +3 -3
  50. package/dist/types/src/util/load-game.d.ts.map +1 -1
  51. package/dist/types/tsconfig.tsbuildinfo +1 -1
  52. package/dx.config.ts +1 -0
  53. package/package.json +34 -33
  54. package/src/GamePlugin.node.tsx +1 -1
  55. package/src/GamePlugin.tsx +1 -1
  56. package/src/GamePlugin.workerd.tsx +1 -1
  57. package/src/capabilities/create-object.ts +3 -3
  58. package/src/capabilities/{react-surface.tsx → react-surface.ts} +7 -8
  59. package/src/components/CreateGamePanel.stories.tsx +2 -1
  60. package/src/containers/GameArticle/GameArticle.tsx +7 -9
  61. package/src/containers/GameCard/GameCard.tsx +7 -7
  62. package/src/translations.ts +1 -1
  63. package/src/types/Game.ts +4 -2
  64. package/src/types/index.ts +1 -1
  65. package/src/types/types.ts +9 -5
  66. package/src/util/load-game.ts +3 -3
  67. package/dist/lib/neutral/GameArticle-AUJ4YDKW.mjs +0 -40
  68. package/dist/lib/neutral/GameArticle-AUJ4YDKW.mjs.map +0 -7
  69. package/dist/lib/neutral/GameCard-QBKHIL4Q.mjs +0 -32
  70. package/dist/lib/neutral/GameCard-QBKHIL4Q.mjs.map +0 -7
  71. package/dist/lib/neutral/GamePlugin.mjs +0 -38
  72. package/dist/lib/neutral/GamePlugin.mjs.map +0 -7
  73. package/dist/lib/neutral/GamePlugin.node.mjs +0 -21
  74. package/dist/lib/neutral/GamePlugin.node.mjs.map +0 -7
  75. package/dist/lib/neutral/GamePlugin.workerd.mjs +0 -18
  76. package/dist/lib/neutral/GamePlugin.workerd.mjs.map +0 -7
  77. package/dist/lib/neutral/capabilities/index.mjs +0 -11
  78. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  79. package/dist/lib/neutral/chunk-DSUJWWNN.mjs +0 -98
  80. package/dist/lib/neutral/chunk-DSUJWWNN.mjs.map +0 -7
  81. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  82. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  83. package/dist/lib/neutral/chunk-R4V5PVU7.mjs.map +0 -7
  84. package/dist/lib/neutral/components/index.mjs +0 -96
  85. package/dist/lib/neutral/components/index.mjs.map +0 -7
  86. package/dist/lib/neutral/containers/index.mjs +0 -11
  87. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  88. package/dist/lib/neutral/create-object-NXKZWZLR.mjs +0 -51
  89. package/dist/lib/neutral/create-object-NXKZWZLR.mjs.map +0 -7
  90. package/dist/lib/neutral/index.mjs +0 -24
  91. package/dist/lib/neutral/index.mjs.map +0 -7
  92. package/dist/lib/neutral/meta.json +0 -1
  93. package/dist/lib/neutral/meta.mjs +0 -8
  94. package/dist/lib/neutral/meta.mjs.map +0 -7
  95. package/dist/lib/neutral/plugin.mjs +0 -12
  96. package/dist/lib/neutral/plugin.mjs.map +0 -7
  97. package/dist/lib/neutral/react-surface-GO3SKEMA.mjs +0 -35
  98. package/dist/lib/neutral/react-surface-GO3SKEMA.mjs.map +0 -7
  99. package/dist/lib/neutral/translations.mjs +0 -34
  100. package/dist/lib/neutral/translations.mjs.map +0 -7
  101. package/dist/lib/neutral/types/index.mjs +0 -20
  102. package/dist/lib/neutral/types/index.mjs.map +0 -7
package/dx.config.ts CHANGED
@@ -36,5 +36,6 @@ export default Config2.make({
36
36
  dark: 'https://customer-5rxcjpyab08avpmn.cloudflarestream.com/2797d56edc9658d018ad75fe48a47f27/iframe?poster=https%3A%2F%2Fcustomer-5rxcjpyab08avpmn.cloudflarestream.com%2F2797d56edc9658d018ad75fe48a47f27%2Fthumbnails%2Fthumbnail.jpg%3Ftime%3D%26height%3D600',
37
37
  },
38
38
  ],
39
+ tags: ['alpha'],
39
40
  },
40
41
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-game",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Game plugin for DXOS Composer (unifies game variants like chess, tic-tac-toe).",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,22 +16,22 @@
16
16
  "#capabilities": {
17
17
  "source": "./src/capabilities/index.ts",
18
18
  "types": "./dist/types/src/capabilities/index.d.ts",
19
- "default": "./dist/lib/neutral/capabilities/index.mjs"
19
+ "default": "./dist/lib/capabilities.mjs"
20
20
  },
21
21
  "#components": {
22
22
  "source": "./src/components/index.ts",
23
23
  "types": "./dist/types/src/components/index.d.ts",
24
- "default": "./dist/lib/neutral/components/index.mjs"
24
+ "default": "./dist/lib/components.mjs"
25
25
  },
26
26
  "#containers": {
27
27
  "source": "./src/containers/index.ts",
28
28
  "types": "./dist/types/src/containers/index.d.ts",
29
- "default": "./dist/lib/neutral/containers/index.mjs"
29
+ "default": "./dist/lib/containers.mjs"
30
30
  },
31
31
  "#meta": {
32
32
  "source": "./src/meta.ts",
33
33
  "types": "./dist/types/src/meta.d.ts",
34
- "default": "./dist/lib/neutral/meta.mjs"
34
+ "default": "./dist/lib/meta.mjs"
35
35
  },
36
36
  "#plugin": {
37
37
  "source": {
@@ -40,19 +40,19 @@
40
40
  "default": "./src/GamePlugin.tsx"
41
41
  },
42
42
  "types": "./dist/types/src/GamePlugin.d.ts",
43
- "workerd": "./dist/lib/neutral/GamePlugin.workerd.mjs",
44
- "node": "./dist/lib/neutral/GamePlugin.node.mjs",
45
- "default": "./dist/lib/neutral/GamePlugin.mjs"
43
+ "workerd": "./dist/lib/GamePlugin.workerd.mjs",
44
+ "node": "./dist/lib/GamePlugin.node.mjs",
45
+ "default": "./dist/lib/GamePlugin.mjs"
46
46
  },
47
47
  "#translations": {
48
48
  "source": "./src/translations.ts",
49
49
  "types": "./dist/types/src/translations.d.ts",
50
- "default": "./dist/lib/neutral/translations.mjs"
50
+ "default": "./dist/lib/translations.mjs"
51
51
  },
52
52
  "#types": {
53
53
  "source": "./src/types/index.ts",
54
54
  "types": "./dist/types/src/types/index.d.ts",
55
- "default": "./dist/lib/neutral/types/index.mjs"
55
+ "default": "./dist/lib/types.mjs"
56
56
  },
57
57
  "#util": "./src/util/index.ts"
58
58
  },
@@ -60,23 +60,23 @@
60
60
  ".": {
61
61
  "source": "./src/index.ts",
62
62
  "types": "./dist/types/src/index.d.ts",
63
- "default": "./dist/lib/neutral/index.mjs"
63
+ "import": "./dist/lib/index.mjs"
64
64
  },
65
65
  "./assets/PLUGIN.mdl": "./PLUGIN.mdl",
66
66
  "./plugin": {
67
67
  "source": "./src/plugin.ts",
68
68
  "types": "./dist/types/src/plugin.d.ts",
69
- "default": "./dist/lib/neutral/plugin.mjs"
69
+ "import": "./dist/lib/plugin.mjs"
70
70
  },
71
71
  "./translations": {
72
72
  "source": "./src/translations.ts",
73
73
  "types": "./dist/types/src/translations.d.ts",
74
- "default": "./dist/lib/neutral/translations.mjs"
74
+ "import": "./dist/lib/translations.mjs"
75
75
  },
76
76
  "./types": {
77
77
  "source": "./src/types/index.ts",
78
78
  "types": "./dist/types/src/types/index.d.ts",
79
- "default": "./dist/lib/neutral/types/index.mjs"
79
+ "import": "./dist/lib/types.mjs"
80
80
  }
81
81
  },
82
82
  "types": "dist/types/src/index.d.ts",
@@ -88,35 +88,36 @@
88
88
  ],
89
89
  "dependencies": {
90
90
  "effect": "3.21.4",
91
- "@dxos/app-framework": "0.10.0",
92
- "@dxos/compute": "0.10.0",
93
- "@dxos/app-toolkit": "0.10.0",
94
- "@dxos/echo": "0.10.0",
95
- "@dxos/log": "0.10.0",
96
- "@dxos/echo-react": "0.10.0",
97
- "@dxos/invariant": "0.10.0",
98
- "@dxos/react-ui-form": "0.10.0",
99
- "@dxos/keys": "0.10.0",
100
- "@dxos/plugin-space": "0.10.0",
101
- "@dxos/react-ui": "0.10.0",
102
- "@dxos/react-ui-search": "0.10.0",
103
- "@dxos/util": "0.10.0"
91
+ "@dxos/app-framework": "0.11.1",
92
+ "@dxos/app-toolkit": "0.11.1",
93
+ "@dxos/compute": "0.11.1",
94
+ "@dxos/echo": "0.11.1",
95
+ "@dxos/echo-react": "0.11.1",
96
+ "@dxos/invariant": "0.11.1",
97
+ "@dxos/keys": "0.11.1",
98
+ "@dxos/log": "0.11.1",
99
+ "@dxos/plugin-space": "0.11.1",
100
+ "@dxos/react-ui": "0.11.1",
101
+ "@dxos/react-ui-form": "0.11.1",
102
+ "@dxos/react-ui-search": "0.11.1",
103
+ "@dxos/schema": "0.11.1",
104
+ "@dxos/util": "0.11.1"
104
105
  },
105
106
  "devDependencies": {
106
107
  "@types/react": "~19.2.17",
107
108
  "@types/react-dom": "~19.2.3",
108
109
  "react": "~19.2.7",
109
110
  "react-dom": "~19.2.7",
110
- "vite": "^8.0.16",
111
- "@dxos/plugin-testing": "0.10.0",
112
- "@dxos/plugin-theme": "0.10.0",
113
- "@dxos/ui-theme": "0.10.0"
111
+ "vite": "^8.1.4",
112
+ "@dxos/plugin-testing": "0.11.1",
113
+ "@dxos/plugin-theme": "0.11.1",
114
+ "@dxos/ui-theme": "0.11.1"
114
115
  },
115
116
  "peerDependencies": {
116
117
  "react": "~19.2.7",
117
118
  "react-dom": "~19.2.7",
118
- "@dxos/react-ui": "0.10.0",
119
- "@dxos/ui-theme": "0.10.0"
119
+ "@dxos/react-ui": "^0.11.1",
120
+ "@dxos/ui-theme": "^0.11.1"
120
121
  },
121
122
  "publishConfig": {
122
123
  "access": "public"
@@ -16,7 +16,7 @@ import { Game } from '#types';
16
16
  * downstream bundlers don't resolve cleanly under nested pnpm symlinks.
17
17
  */
18
18
  export const GamePlugin = Plugin.define(meta).pipe(
19
- AppPlugin.addSchemaModule({ schema: [Game] }),
19
+ AppPlugin.addSchemaModule({ schema: [Game.Game] }),
20
20
  AppPlugin.addTranslationsModule({ translations }),
21
21
  Plugin.make,
22
22
  );
@@ -15,7 +15,7 @@ import pluginSpec from '../PLUGIN.mdl?raw';
15
15
 
16
16
  export const GamePlugin = Plugin.define(meta).pipe(
17
17
  AppPlugin.addCreateObjectModule({ activate: CreateObject }),
18
- AppPlugin.addSchemaModule({ schema: [Game] }),
18
+ AppPlugin.addSchemaModule({ schema: [Game.Game] }),
19
19
  AppPlugin.addSurfaceModule({ activate: ReactSurface }),
20
20
  AppPlugin.addTranslationsModule({ translations }),
21
21
  AppPlugin.addPluginAssetModule({
@@ -8,6 +8,6 @@ import { AppPlugin } from '@dxos/app-toolkit';
8
8
  import { meta } from '#meta';
9
9
  import { Game } from '#types';
10
10
 
11
- export const GamePlugin = Plugin.define(meta).pipe(AppPlugin.addSchemaModule({ schema: [Game] }), Plugin.make);
11
+ export const GamePlugin = Plugin.define(meta).pipe(AppPlugin.addSchemaModule({ schema: [Game.Game] }), Plugin.make);
12
12
 
13
13
  export default GamePlugin;
@@ -13,14 +13,14 @@ import { SpaceCapabilities } from '@dxos/plugin-space';
13
13
 
14
14
  import { CreateGamePanel } from '#components';
15
15
 
16
- import { Game, GameCapabilities, make as makeGame } from '../types';
16
+ import { Game, GameCapabilities } from '../types';
17
17
 
18
18
  type CreateOptions = Parameters<SpaceCapabilities.CreateObjectEntry['createObject']>[1];
19
19
 
20
20
  export default Capability.makeModule(
21
21
  Effect.fnUntraced(function* () {
22
22
  return Capability.contributes(SpaceCapabilities.CreateObjectEntry, {
23
- id: Type.getTypename(Game),
23
+ id: Type.getTypename(Game.Game),
24
24
  customPanel: CreateGamePanel,
25
25
  createObject: (
26
26
  { variantId, input }: { variantId: string; input?: Record<string, any> },
@@ -44,7 +44,7 @@ export default Capability.makeModule(
44
44
  targetNodeId: options.targetNodeId,
45
45
  });
46
46
 
47
- const game = makeGame({
47
+ const game = Game.make({
48
48
  name: typeof input?.name === 'string' ? input.name : undefined,
49
49
  variant: stateObject,
50
50
  });
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  import * as Effect from 'effect/Effect';
6
- import React from 'react';
7
6
 
8
7
  import { Capabilities, Capability } from '@dxos/app-framework';
9
8
  import { Surface } from '@dxos/app-framework/ui';
@@ -19,17 +18,17 @@ export default Capability.makeModule(() =>
19
18
  Surface.create({
20
19
  id: 'game',
21
20
  filter: AppSurface.oneOf(
22
- AppSurface.object(AppSurface.Article, Game),
23
- AppSurface.object(AppSurface.Section, Game),
24
- ),
25
- component: ({ data, role }) => (
26
- <GameArticle role={role} subject={data.subject} attendableId={data.attendableId} />
21
+ AppSurface.object(AppSurface.Article, Game.Game),
22
+ AppSurface.object(AppSurface.Section, Game.Game),
27
23
  ),
24
+ component: GameArticle,
25
+ props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),
28
26
  }),
29
27
  Surface.create({
30
28
  id: 'gameCard',
31
- filter: AppSurface.object(AppSurface.CardContent, Game),
32
- component: ({ data, role }) => <GameCard role={role} subject={data.subject} />,
29
+ filter: AppSurface.object(AppSurface.CardContent, Game.Game),
30
+ component: GameCard,
31
+ props: ({ role, data: { subject } }) => ({ role, subject }),
33
32
  }),
34
33
  ]),
35
34
  ),
@@ -7,6 +7,7 @@ import * as Effect from 'effect/Effect';
7
7
  import * as Schema from 'effect/Schema';
8
8
  import React, { useState } from 'react';
9
9
 
10
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
11
  import { DXN, Obj, Type } from '@dxos/echo';
11
12
  import { Dialog } from '@dxos/react-ui';
12
13
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
@@ -81,7 +82,7 @@ const DefaultStory = () => {
81
82
  const meta = {
82
83
  title: 'plugins/plugin-game/components/CreateGamePanel',
83
84
  component: DefaultStory,
84
- decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
85
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' }), withPluginManager()],
85
86
  parameters: { layout: 'fullscreen', translations },
86
87
  } satisfies Meta<typeof DefaultStory>;
87
88
 
@@ -6,24 +6,20 @@ import React from 'react';
6
6
 
7
7
  import { useCapabilities } from '@dxos/app-framework/ui';
8
8
  import { type AppSurface } from '@dxos/app-toolkit/ui';
9
- import { Obj, type Ref } from '@dxos/echo';
10
- import { useObject } from '@dxos/echo-react';
9
+ import { Obj } from '@dxos/echo';
10
+ import { useResolveRef } from '@dxos/echo-react';
11
11
  import { useTranslation } from '@dxos/react-ui';
12
12
 
13
13
  import { meta } from '#meta';
14
14
  import { type Game, GameCapabilities } from '#types';
15
15
 
16
- export type GameArticleProps = AppSurface.ObjectArticleProps<Game>;
16
+ export type GameArticleProps = AppSurface.ObjectArticleProps<Game.Game>;
17
17
 
18
18
  export const GameArticle = ({ role, attendableId, subject: game }: GameArticleProps) => {
19
19
  const { t } = useTranslation(meta.profile.key);
20
20
  const variants = useCapabilities(GameCapabilities.VariantProvider);
21
- const ref = game.variant as Ref.Ref<Obj.Unknown>;
22
- // Subscribe via useObject so the article re-renders when the variant ref loads or changes.
23
- // The snapshot is discarded; we pass the live ref target to variant components so they can
24
- // subscribe to their own properties via useObject(state, 'prop').
25
- useObject(ref);
26
- const variant = ref?.target as Obj.Unknown | undefined;
21
+ // Resolved live rather than as a snapshot: variants mutate their state, and a snapshot is frozen.
22
+ const variant = useResolveRef(game.variant);
27
23
 
28
24
  if (!variant) {
29
25
  return null;
@@ -40,3 +36,5 @@ export const GameArticle = ({ role, attendableId, subject: game }: GameArticlePr
40
36
  const Component = match.article;
41
37
  return <Component game={game} variant={variant} role={role} attendableId={attendableId} />;
42
38
  };
39
+
40
+ GameArticle.displayName = 'GameArticle';
@@ -6,19 +6,17 @@ import React from 'react';
6
6
 
7
7
  import { useCapabilities } from '@dxos/app-framework/ui';
8
8
  import { type AppSurface } from '@dxos/app-toolkit/ui';
9
- import { Obj, type Ref } from '@dxos/echo';
10
- import { useObject } from '@dxos/echo-react';
9
+ import { Obj } from '@dxos/echo';
10
+ import { useResolveRef } from '@dxos/echo-react';
11
11
 
12
12
  import { type Game, GameCapabilities } from '#types';
13
13
 
14
- export type GameCardProps = AppSurface.ObjectCardProps<Game>;
14
+ export type GameCardProps = AppSurface.ObjectCardProps<Game.Game>;
15
15
 
16
16
  export const GameCard = ({ role, subject: game }: GameCardProps) => {
17
17
  const variants = useCapabilities(GameCapabilities.VariantProvider);
18
- const ref = game.variant as Ref.Ref<Obj.Unknown>;
19
- // Subscribe for re-renders; pass the live ref target down to variant components.
20
- useObject(ref);
21
- const variant = ref?.target as Obj.Unknown | undefined;
18
+ // Resolved live rather than as a snapshot: variants mutate their state, and a snapshot is frozen.
19
+ const variant = useResolveRef(game.variant);
22
20
 
23
21
  if (!variant) {
24
22
  return null;
@@ -33,3 +31,5 @@ export const GameCard = ({ role, subject: game }: GameCardProps) => {
33
31
  const Component = match.card;
34
32
  return <Component game={game} variant={variant} role={role} />;
35
33
  };
34
+
35
+ GameCard.displayName = 'GameCard';
@@ -11,7 +11,7 @@ import { Game } from '#types';
11
11
  export const translations = [
12
12
  {
13
13
  'en-US': {
14
- [Type.getTypename(Game)]: {
14
+ [Type.getTypename(Game.Game)]: {
15
15
  'typename.label': 'Game',
16
16
  'typename.label_zero': 'Games',
17
17
  'typename.label_one': 'Game',
package/src/types/Game.ts CHANGED
@@ -4,9 +4,11 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { AppAnnotation } from '@dxos/app-toolkit';
8
7
  import { Annotation, DXN, Obj, Ref, Type } from '@dxos/echo';
9
8
  import { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/Annotation';
9
+ import { CardAnnotation } from '@dxos/schema';
10
+
11
+ // @import-as-namespace
10
12
 
11
13
  /**
12
14
  * A player in a game.
@@ -49,7 +51,7 @@ export class Game extends Type.makeObject<Game>(DXN.make('org.dxos.type.game', '
49
51
  }).pipe(
50
52
  LabelAnnotation.set(['name']),
51
53
  Annotation.IconAnnotation.set({ icon: 'ph--sword--regular', hue: 'indigo' }),
52
- AppAnnotation.CardAnnotation.set(true),
54
+ CardAnnotation.set(true),
53
55
  // Delegate the graph-node icon to the referenced variant state's schema. Falls back to
54
56
  // the static `ph--sword--regular` above while the variant ref is still loading.
55
57
  Annotation.IconFromRefAnnotation.set('variant'),
@@ -2,7 +2,7 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- export { Game, GameRef, Player, make } from './Game';
5
+ export * as Game from './Game';
6
6
  export * from './types';
7
7
  export { GameVariantMismatchError, loadGame } from '../util/load-game';
8
8
 
@@ -8,10 +8,10 @@ import type { ComponentType } from 'react';
8
8
 
9
9
  import type { Database, Obj, Type } from '@dxos/echo';
10
10
 
11
- import type { Game } from './Game';
11
+ import * as Game from './Game';
12
12
 
13
13
  /**
14
- * Contribution from a variant plugin (e.g. plugin-chess, plugin-tictactoe).
14
+ * Contribution from a variant plugin (e.g. plugin-chess).
15
15
  * Defines how a game variant is created, rendered, and the roles its players take.
16
16
  */
17
17
  export type GameVariant = {
@@ -43,9 +43,13 @@ export type GameVariant = {
43
43
 
44
44
  export type GameVariantSurfaceProps = {
45
45
  /** The base Game object (may be a snapshot from useObject/Surface). */
46
- game: Obj.Snapshot<Game> | Game;
47
- /** The resolved variant state object (may be a snapshot from useObject). */
48
- variant: Obj.Snapshot<Obj.Unknown> | Obj.Unknown;
46
+ game: Obj.Snapshot<Game.Game> | Game.Game;
47
+ /**
48
+ * The resolved variant state object, live rather than a snapshot so variants can write to it.
49
+ * Reads must subscribe via `useObject(variant, prop)` — the host re-renders only when the ref
50
+ * resolves, not on every mutation.
51
+ */
52
+ variant: Obj.Unknown;
49
53
  /** Surface role passed through from the host. */
50
54
  role?: string;
51
55
  /** Attendable id passed through from the host. */
@@ -6,7 +6,7 @@ import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Database, Obj, type Ref, Type } from '@dxos/echo';
8
8
 
9
- import { Game } from '../types/Game';
9
+ import * as Game from '../types/Game';
10
10
 
11
11
  export class GameVariantMismatchError extends Error {
12
12
  readonly _tag = 'GameVariantMismatchError';
@@ -26,9 +26,9 @@ export class GameVariantMismatchError extends Error {
26
26
  * Use in operation handlers that declare `input.game: GameRef<MyVariantState>`.
27
27
  */
28
28
  export const loadGame = <S extends Type.AnyObj>(
29
- ref: Ref.Ref<Game>,
29
+ ref: Ref.Ref<Game.Game>,
30
30
  variantType: S,
31
- ): Effect.Effect<{ game: Game; variant: Type.InstanceType<S> }, Error, Database.Service> =>
31
+ ): Effect.Effect<{ game: Game.Game; variant: Type.InstanceType<S> }, Error, Database.Service> =>
32
32
  Effect.gen(function* () {
33
33
  const game = yield* Database.load(ref);
34
34
  const variant = yield* Database.load(game.variant);
@@ -1,40 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/GameArticle/GameArticle.tsx
4
- import React from "react";
5
- import { useCapabilities } from "@dxos/app-framework/ui";
6
- import { Obj } from "@dxos/echo";
7
- import { useObject } from "@dxos/echo-react";
8
- import { useTranslation } from "@dxos/react-ui";
9
- import { meta } from "#meta";
10
- import { GameCapabilities } from "#types";
11
- var GameArticle = ({ role, attendableId, subject: game }) => {
12
- const { t } = useTranslation(meta.profile.key);
13
- const variants = useCapabilities(GameCapabilities.VariantProvider);
14
- const ref = game.variant;
15
- useObject(ref);
16
- const variant = ref?.target;
17
- if (!variant) {
18
- return null;
19
- }
20
- const variantTypename = Obj.getTypename(variant);
21
- const match = variants.find((v) => v.id === variantTypename);
22
- if (!match?.article) {
23
- return /* @__PURE__ */ React.createElement("div", {
24
- className: "p-4 text-sm"
25
- }, t("unsupported-variant.label", {
26
- defaultValue: "Unsupported game variant"
27
- }));
28
- }
29
- const Component = match.article;
30
- return /* @__PURE__ */ React.createElement(Component, {
31
- game,
32
- variant,
33
- role,
34
- attendableId
35
- });
36
- };
37
- export {
38
- GameArticle as default
39
- };
40
- //# sourceMappingURL=GameArticle-AUJ4YDKW.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/GameArticle/GameArticle.tsx"],
4
- "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, type Ref } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport { useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\nimport { type Game, GameCapabilities } from '#types';\n\nexport type GameArticleProps = AppSurface.ObjectArticleProps<Game>;\n\nexport const GameArticle = ({ role, attendableId, subject: game }: GameArticleProps) => {\n const { t } = useTranslation(meta.profile.key);\n const variants = useCapabilities(GameCapabilities.VariantProvider);\n const ref = game.variant as Ref.Ref<Obj.Unknown>;\n // Subscribe via useObject so the article re-renders when the variant ref loads or changes.\n // The snapshot is discarded; we pass the live ref target to variant components so they can\n // subscribe to their own properties via useObject(state, 'prop').\n useObject(ref);\n const variant = ref?.target as Obj.Unknown | undefined;\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"],
5
- "mappings": ";;;AAIA,OAAOA,WAAW;AAElB,SAASC,uBAAuB;AAEhC,SAASC,WAAqB;AAC9B,SAASC,iBAAiB;AAC1B,SAASC,sBAAsB;AAE/B,SAASC,YAAY;AACrB,SAAoBC,wBAAwB;AAIrC,IAAMC,cAAc,CAAC,EAAEC,MAAMC,cAAcC,SAASC,KAAI,MAAoB;AACjF,QAAM,EAAEC,EAAC,IAAKR,eAAeC,KAAKQ,QAAQC,GAAG;AAC7C,QAAMC,WAAWd,gBAAgBK,iBAAiBU,eAAe;AACjE,QAAMC,MAAMN,KAAKO;AAIjBf,YAAUc,GAAAA;AACV,QAAMC,UAAUD,KAAKE;AAErB,MAAI,CAACD,SAAS;AACZ,WAAO;EACT;AAEA,QAAME,kBAAkBlB,IAAImB,YAAYH,OAAAA;AACxC,QAAMI,QAAQP,SAASQ,KAAK,CAACC,MAAMA,EAAEC,OAAOL,eAAAA;AAC5C,MAAI,CAACE,OAAOI,SAAS;AACnB,WACE,sBAAA,cAACC,OAAAA;MAAIC,WAAU;OAAehB,EAAE,6BAA6B;MAAEiB,cAAc;IAA2B,CAAA,CAAA;EAE5G;AAEA,QAAMC,YAAYR,MAAMI;AACxB,SAAO,sBAAA,cAACI,WAAAA;IAAUnB;IAAYO;IAAkBV;IAAYC;;AAC9D;",
6
- "names": ["React", "useCapabilities", "Obj", "useObject", "useTranslation", "meta", "GameCapabilities", "GameArticle", "role", "attendableId", "subject", "game", "t", "profile", "key", "variants", "VariantProvider", "ref", "variant", "target", "variantTypename", "getTypename", "match", "find", "v", "id", "article", "div", "className", "defaultValue", "Component"]
7
- }
@@ -1,32 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/GameCard/GameCard.tsx
4
- import React from "react";
5
- import { useCapabilities } from "@dxos/app-framework/ui";
6
- import { Obj } from "@dxos/echo";
7
- import { useObject } from "@dxos/echo-react";
8
- import { GameCapabilities } from "#types";
9
- var GameCard = ({ role, subject: game }) => {
10
- const variants = useCapabilities(GameCapabilities.VariantProvider);
11
- const ref = game.variant;
12
- useObject(ref);
13
- const variant = ref?.target;
14
- if (!variant) {
15
- return null;
16
- }
17
- const variantTypename = Obj.getTypename(variant);
18
- const match = variants.find((v) => v.id === variantTypename);
19
- if (!match?.card) {
20
- return null;
21
- }
22
- const Component = match.card;
23
- return /* @__PURE__ */ React.createElement(Component, {
24
- game,
25
- variant,
26
- role
27
- });
28
- };
29
- export {
30
- GameCard as default
31
- };
32
- //# sourceMappingURL=GameCard-QBKHIL4Q.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/GameCard/GameCard.tsx"],
4
- "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, type Ref } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\n\nimport { type Game, GameCapabilities } from '#types';\n\nexport type GameCardProps = AppSurface.ObjectCardProps<Game>;\n\nexport const GameCard = ({ role, subject: game }: GameCardProps) => {\n const variants = useCapabilities(GameCapabilities.VariantProvider);\n const ref = game.variant as Ref.Ref<Obj.Unknown>;\n // Subscribe for re-renders; pass the live ref target down to variant components.\n useObject(ref);\n const variant = ref?.target as Obj.Unknown | undefined;\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"],
5
- "mappings": ";;;AAIA,OAAOA,WAAW;AAElB,SAASC,uBAAuB;AAEhC,SAASC,WAAqB;AAC9B,SAASC,iBAAiB;AAE1B,SAAoBC,wBAAwB;AAIrC,IAAMC,WAAW,CAAC,EAAEC,MAAMC,SAASC,KAAI,MAAiB;AAC7D,QAAMC,WAAWR,gBAAgBG,iBAAiBM,eAAe;AACjE,QAAMC,MAAMH,KAAKI;AAEjBT,YAAUQ,GAAAA;AACV,QAAMC,UAAUD,KAAKE;AAErB,MAAI,CAACD,SAAS;AACZ,WAAO;EACT;AAEA,QAAME,kBAAkBZ,IAAIa,YAAYH,OAAAA;AACxC,QAAMI,QAAQP,SAASQ,KAAK,CAACC,MAAMA,EAAEC,OAAOL,eAAAA;AAC5C,MAAI,CAACE,OAAOI,MAAM;AAChB,WAAO;EACT;AAEA,QAAMC,YAAYL,MAAMI;AACxB,SAAO,sBAAA,cAACC,WAAAA;IAAUb;IAAYI;IAAkBN;;AAClD;",
6
- "names": ["React", "useCapabilities", "Obj", "useObject", "GameCapabilities", "GameCard", "role", "subject", "game", "variants", "VariantProvider", "ref", "variant", "target", "variantTypename", "getTypename", "match", "find", "v", "id", "card", "Component"]
7
- }
@@ -1,38 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/GamePlugin.tsx
4
- import { Plugin } from "@dxos/app-framework";
5
- import { AppPlugin } from "@dxos/app-toolkit";
6
- import { CreateObject, ReactSurface } from "#capabilities";
7
- import { meta } from "#meta";
8
- import { translations } from "#translations";
9
- import { Game } from "#types";
10
-
11
- // raw-loader:/__w/dxos/dxos/packages/plugins/plugin-game/PLUGIN.mdl
12
- 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 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Stage 1: Variant Picker \u2502\n \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Search input \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 Stage 2: Input Form \u2502\n \u2502 (rendered from schema) \u2502\n \u2502 [Submit] \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\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 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [variant.article] \u2502\n \u2502 (fallback: unsupported \u2502\n \u2502 variant message) \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\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, \u2026)`\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 \u2014 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';
13
-
14
- // src/GamePlugin.tsx
15
- var GamePlugin = Plugin.define(meta).pipe(AppPlugin.addCreateObjectModule({
16
- activate: CreateObject
17
- }), AppPlugin.addSchemaModule({
18
- schema: [
19
- Game
20
- ]
21
- }), AppPlugin.addSurfaceModule({
22
- activate: ReactSurface
23
- }), AppPlugin.addTranslationsModule({
24
- translations
25
- }), AppPlugin.addPluginAssetModule({
26
- asset: {
27
- pluginId: meta.profile.key,
28
- path: "PLUGIN.mdl",
29
- content: PLUGIN_default,
30
- mimeType: "application/x-mdl"
31
- }
32
- }), Plugin.make);
33
- var GamePlugin_default = GamePlugin;
34
- export {
35
- GamePlugin,
36
- GamePlugin_default as default
37
- };
38
- //# sourceMappingURL=GamePlugin.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/GamePlugin.tsx", "raw-loader:/__w/dxos/dxos/packages/plugins/plugin-game/PLUGIN.mdl"],
4
- "sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { AppPlugin } from '@dxos/app-toolkit';\n\nimport { CreateObject, ReactSurface } from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { Game } from '#types';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\n\nexport const GamePlugin = Plugin.define(meta).pipe(\n AppPlugin.addCreateObjectModule({ activate: CreateObject }),\n AppPlugin.addSchemaModule({ schema: [Game] }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations }),\n AppPlugin.addPluginAssetModule({\n asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },\n }),\n Plugin.make,\n);\n\nexport default GamePlugin;\n", "---\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 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Stage 1: Variant Picker \u2502\n \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Search input \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 Stage 2: Input Form \u2502\n \u2502 (rendered from schema) \u2502\n \u2502 [Submit] \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\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 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [variant.article] \u2502\n \u2502 (fallback: unsupported \u2502\n \u2502 variant message) \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\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, \u2026)`\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 \u2014 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"],
5
- "mappings": ";;;AAIA,SAASA,cAAc;AACvB,SAASC,iBAAiB;AAE1B,SAASC,cAAcC,oBAAoB;AAC3C,SAASC,YAAY;AACrB,SAASC,oBAAoB;AAC7B,SAASC,YAAY;;;ACVrB;;;ADeO,IAAMC,aAAaC,OAAOC,OAAOC,IAAAA,EAAMC,KAC5CC,UAAUC,sBAAsB;EAAEC,UAAUC;AAAa,CAAA,GACzDH,UAAUI,gBAAgB;EAAEC,QAAQ;IAACC;;AAAM,CAAA,GAC3CN,UAAUO,iBAAiB;EAAEL,UAAUM;AAAa,CAAA,GACpDR,UAAUS,sBAAsB;EAAEC;AAAa,CAAA,GAC/CV,UAAUW,qBAAqB;EAC7BC,OAAO;IAAEC,UAAUf,KAAKgB,QAAQC;IAAKC,MAAM;IAAcC,SAASC;IAAYC,UAAU;EAAoB;AAC9G,CAAA,GACAvB,OAAOwB,IAAI;AAGb,IAAA,qBAAezB;",
6
- "names": ["Plugin", "AppPlugin", "CreateObject", "ReactSurface", "meta", "translations", "Game", "GamePlugin", "Plugin", "define", "meta", "pipe", "AppPlugin", "addCreateObjectModule", "activate", "CreateObject", "addSchemaModule", "schema", "Game", "addSurfaceModule", "ReactSurface", "addTranslationsModule", "translations", "addPluginAssetModule", "asset", "pluginId", "profile", "key", "path", "content", "pluginSpec", "mimeType", "make"]
7
- }