@dxos/plugin-game 0.0.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 (117) hide show
  1. package/LICENSE +105 -0
  2. package/PLUGIN.mdl +334 -0
  3. package/dist/lib/neutral/GameArticle-AUJ4YDKW.mjs +40 -0
  4. package/dist/lib/neutral/GameArticle-AUJ4YDKW.mjs.map +7 -0
  5. package/dist/lib/neutral/GameCard-QBKHIL4Q.mjs +32 -0
  6. package/dist/lib/neutral/GameCard-QBKHIL4Q.mjs.map +7 -0
  7. package/dist/lib/neutral/GamePlugin.mjs +38 -0
  8. package/dist/lib/neutral/GamePlugin.mjs.map +7 -0
  9. package/dist/lib/neutral/GamePlugin.node.mjs +21 -0
  10. package/dist/lib/neutral/GamePlugin.node.mjs.map +7 -0
  11. package/dist/lib/neutral/GamePlugin.workerd.mjs +18 -0
  12. package/dist/lib/neutral/GamePlugin.workerd.mjs.map +7 -0
  13. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  14. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  15. package/dist/lib/neutral/chunk-DSUJWWNN.mjs +98 -0
  16. package/dist/lib/neutral/chunk-DSUJWWNN.mjs.map +7 -0
  17. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  18. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  19. package/dist/lib/neutral/chunk-R4V5PVU7.mjs +50 -0
  20. package/dist/lib/neutral/chunk-R4V5PVU7.mjs.map +7 -0
  21. package/dist/lib/neutral/components/index.mjs +96 -0
  22. package/dist/lib/neutral/components/index.mjs.map +7 -0
  23. package/dist/lib/neutral/containers/index.mjs +11 -0
  24. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  25. package/dist/lib/neutral/create-object-UAD2JUWF.mjs +51 -0
  26. package/dist/lib/neutral/create-object-UAD2JUWF.mjs.map +7 -0
  27. package/dist/lib/neutral/index.mjs +24 -0
  28. package/dist/lib/neutral/index.mjs.map +7 -0
  29. package/dist/lib/neutral/meta.json +1 -0
  30. package/dist/lib/neutral/meta.mjs +8 -0
  31. package/dist/lib/neutral/meta.mjs.map +7 -0
  32. package/dist/lib/neutral/plugin.mjs +12 -0
  33. package/dist/lib/neutral/plugin.mjs.map +7 -0
  34. package/dist/lib/neutral/react-surface-GO3SKEMA.mjs +35 -0
  35. package/dist/lib/neutral/react-surface-GO3SKEMA.mjs.map +7 -0
  36. package/dist/lib/neutral/translations.mjs +34 -0
  37. package/dist/lib/neutral/translations.mjs.map +7 -0
  38. package/dist/lib/neutral/types/index.mjs +20 -0
  39. package/dist/lib/neutral/types/index.mjs.map +7 -0
  40. package/dist/types/dx.config.d.ts +28 -0
  41. package/dist/types/dx.config.d.ts.map +1 -0
  42. package/dist/types/src/GamePlugin.d.ts +4 -0
  43. package/dist/types/src/GamePlugin.d.ts.map +1 -0
  44. package/dist/types/src/GamePlugin.node.d.ts +10 -0
  45. package/dist/types/src/GamePlugin.node.d.ts.map +1 -0
  46. package/dist/types/src/GamePlugin.workerd.d.ts +4 -0
  47. package/dist/types/src/GamePlugin.workerd.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/create-object.d.ts +5 -0
  49. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/index.d.ts +4 -0
  51. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/react-surface.d.ts +5 -0
  53. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  54. package/dist/types/src/components/CreateGamePanel.d.ts +19 -0
  55. package/dist/types/src/components/CreateGamePanel.d.ts.map +1 -0
  56. package/dist/types/src/components/CreateGamePanel.stories.d.ts +49 -0
  57. package/dist/types/src/components/CreateGamePanel.stories.d.ts.map +1 -0
  58. package/dist/types/src/components/index.d.ts +2 -0
  59. package/dist/types/src/components/index.d.ts.map +1 -0
  60. package/dist/types/src/containers/GameArticle/GameArticle.d.ts +6 -0
  61. package/dist/types/src/containers/GameArticle/GameArticle.d.ts.map +1 -0
  62. package/dist/types/src/containers/GameArticle/index.d.ts +2 -0
  63. package/dist/types/src/containers/GameArticle/index.d.ts.map +1 -0
  64. package/dist/types/src/containers/GameCard/GameCard.d.ts +6 -0
  65. package/dist/types/src/containers/GameCard/GameCard.d.ts.map +1 -0
  66. package/dist/types/src/containers/GameCard/index.d.ts +2 -0
  67. package/dist/types/src/containers/GameCard/index.d.ts.map +1 -0
  68. package/dist/types/src/containers/index.d.ts +4 -0
  69. package/dist/types/src/containers/index.d.ts.map +1 -0
  70. package/dist/types/src/index.d.ts +3 -0
  71. package/dist/types/src/index.d.ts.map +1 -0
  72. package/dist/types/src/meta.d.ts +33 -0
  73. package/dist/types/src/meta.d.ts.map +1 -0
  74. package/dist/types/src/plugin.d.ts +3 -0
  75. package/dist/types/src/plugin.d.ts.map +1 -0
  76. package/dist/types/src/translations.d.ts +36 -0
  77. package/dist/types/src/translations.d.ts.map +1 -0
  78. package/dist/types/src/types/Game.d.ts +67 -0
  79. package/dist/types/src/types/Game.d.ts.map +1 -0
  80. package/dist/types/src/types/GameCapabilities.d.ts +10 -0
  81. package/dist/types/src/types/GameCapabilities.d.ts.map +1 -0
  82. package/dist/types/src/types/index.d.ts +5 -0
  83. package/dist/types/src/types/index.d.ts.map +1 -0
  84. package/dist/types/src/types/types.d.ts +46 -0
  85. package/dist/types/src/types/types.d.ts.map +1 -0
  86. package/dist/types/src/util/index.d.ts +2 -0
  87. package/dist/types/src/util/index.d.ts.map +1 -0
  88. package/dist/types/src/util/load-game.d.ts +21 -0
  89. package/dist/types/src/util/load-game.d.ts.map +1 -0
  90. package/dist/types/tsconfig.tsbuildinfo +1 -0
  91. package/dx.config.ts +40 -0
  92. package/package.json +124 -0
  93. package/src/GamePlugin.node.tsx +24 -0
  94. package/src/GamePlugin.tsx +27 -0
  95. package/src/GamePlugin.workerd.tsx +13 -0
  96. package/src/capabilities/create-object.ts +67 -0
  97. package/src/capabilities/index.ts +8 -0
  98. package/src/capabilities/react-surface.tsx +36 -0
  99. package/src/components/CreateGamePanel.stories.tsx +92 -0
  100. package/src/components/CreateGamePanel.tsx +127 -0
  101. package/src/components/index.ts +5 -0
  102. package/src/containers/GameArticle/GameArticle.tsx +42 -0
  103. package/src/containers/GameArticle/index.ts +5 -0
  104. package/src/containers/GameCard/GameCard.tsx +35 -0
  105. package/src/containers/GameCard/index.ts +5 -0
  106. package/src/containers/index.ts +8 -0
  107. package/src/index.ts +6 -0
  108. package/src/meta.ts +9 -0
  109. package/src/plugin.ts +9 -0
  110. package/src/translations.ts +34 -0
  111. package/src/types/Game.ts +106 -0
  112. package/src/types/GameCapabilities.ts +19 -0
  113. package/src/types/index.ts +9 -0
  114. package/src/types/types.ts +53 -0
  115. package/src/util/index.ts +5 -0
  116. package/src/util/load-game.ts +41 -0
  117. package/src/vite-env.d.ts +10 -0
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './meta';
6
+ export * from './types';
package/src/meta.ts ADDED
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+
7
+ import config from '../dx.config';
8
+
9
+ export const meta = Plugin.getMetaFromConfig(config);
package/src/plugin.ts ADDED
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+
7
+ import { meta } from './meta';
8
+
9
+ export const GamePlugin = Plugin.lazy(meta, () => import('#plugin'));
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Type } from '@dxos/echo';
6
+ import { type Resource } from '@dxos/react-ui';
7
+
8
+ import { meta } from '#meta';
9
+ import { Game } from '#types';
10
+
11
+ export const translations = [
12
+ {
13
+ 'en-US': {
14
+ [Type.getTypename(Game)]: {
15
+ 'typename.label': 'Game',
16
+ 'typename.label_zero': 'Games',
17
+ 'typename.label_one': 'Game',
18
+ 'typename.label_other': 'Games',
19
+ 'object-name.placeholder': 'New game',
20
+ 'add-object.label': 'Add game',
21
+ 'rename-object.label': 'Rename game',
22
+ 'delete-object.label': 'Delete game',
23
+ 'object-deleted.label': 'Game deleted',
24
+ },
25
+ [meta.profile.key]: {
26
+ 'plugin.name': 'Game',
27
+ 'create-panel.variant.label': 'Select variant',
28
+ 'create-panel.variant.placeholder': 'Search variants...',
29
+ 'create-panel.submit.label': 'Create',
30
+ 'unsupported-variant.label': 'Unsupported game variant',
31
+ },
32
+ },
33
+ },
34
+ ] as const satisfies Resource[];
@@ -0,0 +1,106 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+
7
+ import { AppAnnotation } from '@dxos/app-toolkit';
8
+ import { Annotation, DXN, Obj, Ref, Type } from '@dxos/echo';
9
+ import { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/Annotation';
10
+
11
+ /**
12
+ * A player in a game.
13
+ * `role` is variant-defined (e.g. 'white' | 'black' for chess, 'x' | 'o' for tictactoe).
14
+ * `identity` is the optional DID of a human player; absent for AI or empty seats.
15
+ * `name` is an optional display label (useful for AI/guest players).
16
+ */
17
+ export const Player = Schema.Struct({
18
+ role: Schema.String.annotations({
19
+ description: 'Variant-defined player role (e.g. "white", "x").',
20
+ }),
21
+ identity: Schema.optional(
22
+ Schema.String.annotations({
23
+ description: 'DID of the player; absent for AI or empty seats.',
24
+ }),
25
+ ),
26
+ name: Schema.optional(
27
+ Schema.String.annotations({
28
+ description: 'Optional display name for the player.',
29
+ }),
30
+ ),
31
+ });
32
+
33
+ export type Player = Schema.Schema.Type<typeof Player>;
34
+
35
+ /**
36
+ * Base Game object. Variant-specific state lives in a separate referenced object.
37
+ * Unifies all game variants (chess, tic-tac-toe, ...) under one typename so they share
38
+ * graph node, create flow, and shared surface scaffolding.
39
+ */
40
+ export class Game extends Type.makeObject<Game>(DXN.make('org.dxos.type.game', '0.1.0'))(
41
+ Schema.Struct({
42
+ name: Schema.optional(Schema.String),
43
+ players: Schema.mutable(Schema.Array(Player))
44
+ .annotations({ description: 'Players in the game.' })
45
+ .pipe(FormInputAnnotation.set(false), Schema.optional),
46
+ variant: Ref.Ref(Obj.Unknown)
47
+ .annotations({ description: 'Reference to variant-specific state object.' })
48
+ .pipe(FormInputAnnotation.set(false)),
49
+ }).pipe(
50
+ LabelAnnotation.set(['name']),
51
+ Annotation.IconAnnotation.set({ icon: 'ph--sword--regular', hue: 'indigo' }),
52
+ AppAnnotation.CardAnnotation.set(true),
53
+ // Delegate the graph-node icon to the referenced variant state's schema. Falls back to
54
+ // the static `ph--sword--regular` above while the variant ref is still loading.
55
+ Annotation.IconFromRefAnnotation.set('variant'),
56
+ ),
57
+ ) {}
58
+
59
+ /**
60
+ * Variant-narrowed reference to a Game.
61
+ *
62
+ * Encodes — at the type level — that the variant ref points to a specific state type.
63
+ * Runtime narrowing is performed by `loadGame(ref, variantType)` which validates and
64
+ * returns the resolved Game together with its typed variant state.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * input: Schema.Struct({
69
+ * game: GameRef(Chess.State),
70
+ * }),
71
+ * // handler:
72
+ * const { game, variant } = yield* loadGame(input.game, Chess.State);
73
+ * // `variant` is typed as Chess.State.
74
+ * ```
75
+ */
76
+ export type GameRef<_V> = Ref.Ref<Game>;
77
+
78
+ export const GameRef = <S extends Type.AnyObj>(_variantType: S) =>
79
+ Ref.Ref(Game) as Schema.Schema<GameRef<Type.InstanceType<S>>, any, never>;
80
+
81
+ /**
82
+ * Build a base `Game` object referencing the given variant-state ECHO object.
83
+ *
84
+ * The variant is stored as a `Ref` so the underlying state (e.g. `Chess.State`,
85
+ * `TicTacToe.State`) lives as its own ECHO object alongside the Game.
86
+ *
87
+ * @param name Optional display name shown in the graph node and Properties form.
88
+ * @param variant Variant-specific state object (e.g. `Chess.State`, `TicTacToe.State`).
89
+ * @param players Optional initial players; copied into a mutable array on the Game.
90
+ * @returns A new `Game` object with `variant` stored as a Ref.
91
+ */
92
+ export const make = ({
93
+ name,
94
+ players,
95
+ variant,
96
+ }: {
97
+ name?: string;
98
+ players?: ReadonlyArray<Player>;
99
+ variant: Obj.Unknown;
100
+ }): Game => {
101
+ return Obj.make(Game, {
102
+ name,
103
+ players: players ? [...players] : [],
104
+ variant: Ref.make(variant),
105
+ });
106
+ };
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import { Capability } from '@dxos/app-framework';
8
+
9
+ import { meta } from '#meta';
10
+
11
+ import type { GameVariant } from './types';
12
+
13
+ /**
14
+ * A game variant contribution. Each variant plugin (chess, tic-tac-toe, ...)
15
+ * contributes one of these via `Capability.contributes(GameCapabilities.VariantProvider, variant)`.
16
+ * Consumers iterate via `Capability.getAll(GameCapabilities.VariantProvider)` (Effect) or
17
+ * `useCapabilities(GameCapabilities.VariantProvider)` (React).
18
+ */
19
+ export const VariantProvider = Capability.make<GameVariant>(`${meta.profile.key}.capability.variant`);
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export { Game, GameRef, Player, make } from './Game';
6
+ export * from './types';
7
+ export { GameVariantMismatchError, loadGame } from '../util/load-game';
8
+
9
+ export * as GameCapabilities from './GameCapabilities';
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import type * as Effect from 'effect/Effect';
6
+ import type * as Schema from 'effect/Schema';
7
+ import type { ComponentType } from 'react';
8
+
9
+ import type { Database, Obj, Type } from '@dxos/echo';
10
+
11
+ import type { Game } from './Game';
12
+
13
+ /**
14
+ * Contribution from a variant plugin (e.g. plugin-chess, plugin-tictactoe).
15
+ * Defines how a game variant is created, rendered, and the roles its players take.
16
+ */
17
+ export type GameVariant = {
18
+ /** Stable id, typically the variant state typename (e.g. 'org.dxos.type.chess.state'). */
19
+ id: string;
20
+ /** Human-readable variant name (e.g. 'Chess'). */
21
+ label: string;
22
+ /** Optional Phosphor icon name (e.g. 'ph--shield-chevron--regular'). */
23
+ icon?: string;
24
+ /** ECHO Type entity of the variant state object referenced by `Game.variant`. */
25
+ variantType: Type.AnyObj;
26
+ /**
27
+ * Optional Effect Schema rendered as a form after the user picks the variant.
28
+ * To use a `Type.Type` entity, extract its schema first via `Type.getSchema(...)`.
29
+ */
30
+ inputSchema?: Schema.Schema.AnyNoContext;
31
+ /** Roles a player may take in this variant (e.g. ['white', 'black']). */
32
+ roles: readonly string[];
33
+ /**
34
+ * Build the variant state object from the user's form input. May allocate ECHO
35
+ * objects, run effects, etc. Returned object is added to the database alongside the Game.
36
+ */
37
+ createVariant: (input: Record<string, any>) => Effect.Effect<Obj.Any, Error, Database.Service>;
38
+ /** Optional Card surface component for this variant. */
39
+ card?: ComponentType<GameVariantSurfaceProps>;
40
+ /** Optional Article/Section surface component for this variant. */
41
+ article?: ComponentType<GameVariantSurfaceProps>;
42
+ };
43
+
44
+ export type GameVariantSurfaceProps = {
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;
49
+ /** Surface role passed through from the host. */
50
+ role?: string;
51
+ /** Attendable id passed through from the host. */
52
+ attendableId?: string;
53
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './load-game';
@@ -0,0 +1,41 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { Database, Obj, type Ref, Type } from '@dxos/echo';
8
+
9
+ import { Game } from '../types/Game';
10
+
11
+ export class GameVariantMismatchError extends Error {
12
+ readonly _tag = 'GameVariantMismatchError';
13
+ constructor(
14
+ readonly gameId: string,
15
+ readonly expectedTypename: string,
16
+ readonly actualTypename: string,
17
+ ) {
18
+ super(`Game ${gameId} variant typename mismatch: expected ${expectedTypename}, got ${actualTypename}.`);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Loads a Game from a Ref and resolves its variant state, asserting the variant matches
24
+ * the expected type. Returns the typed pair.
25
+ *
26
+ * Use in operation handlers that declare `input.game: GameRef<MyVariantState>`.
27
+ */
28
+ export const loadGame = <S extends Type.AnyObj>(
29
+ ref: Ref.Ref<Game>,
30
+ variantType: S,
31
+ ): Effect.Effect<{ game: Game; variant: Type.InstanceType<S> }, Error, Database.Service> =>
32
+ Effect.gen(function* () {
33
+ const game = yield* Database.load(ref);
34
+ const variant = yield* Database.load(game.variant);
35
+ if (!Obj.instanceOf(variantType, variant)) {
36
+ const expected = Type.getTypename(variantType) ?? 'unknown';
37
+ const actual = Obj.getTypename(variant as Obj.Any) ?? 'unknown';
38
+ return yield* Effect.fail(new GameVariantMismatchError(game.id, expected, actual));
39
+ }
40
+ return { game, variant: variant as Type.InstanceType<S> };
41
+ });
@@ -0,0 +1,10 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ //
4
+ // Copyright 2026 DXOS.org
5
+ //
6
+
7
+ declare module '*.mdl?raw' {
8
+ const content: string;
9
+ export default content;
10
+ }