@dxos/plugin-chess 0.8.4-main.f9ba587 → 0.8.4-main.fffef41

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 (160) hide show
  1. package/dist/lib/browser/ChessboardContainer-5YXEM4VR.mjs +96 -0
  2. package/dist/lib/browser/ChessboardContainer-5YXEM4VR.mjs.map +7 -0
  3. package/dist/lib/browser/blueprint-definition-FD4TL5EE.mjs +11 -0
  4. package/dist/lib/browser/blueprint-definition-FD4TL5EE.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-5DOZV3PW.mjs +294 -0
  6. package/dist/lib/browser/chunk-5DOZV3PW.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-DUO4WTAM.mjs +28 -0
  8. package/dist/lib/browser/chunk-DUO4WTAM.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-FBLAVAMY.mjs +84 -0
  10. package/dist/lib/browser/chunk-FBLAVAMY.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-Q5TZCIH7.mjs +8 -0
  12. package/dist/lib/browser/chunk-Q5TZCIH7.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-TIIGNOGK.mjs +169 -0
  14. package/dist/lib/browser/chunk-TIIGNOGK.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +56 -28
  16. package/dist/lib/browser/index.mjs.map +3 -3
  17. package/dist/lib/browser/intent-resolver-GXZN2EBT.mjs +25 -0
  18. package/dist/lib/browser/intent-resolver-GXZN2EBT.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/react-surface-KTTAUHAA.mjs +38 -0
  21. package/dist/lib/browser/react-surface-KTTAUHAA.mjs.map +7 -0
  22. package/dist/lib/browser/types/index.mjs +6 -7
  23. package/dist/lib/node-esm/ChessboardContainer-7OQ6TOKB.mjs +97 -0
  24. package/dist/lib/node-esm/ChessboardContainer-7OQ6TOKB.mjs.map +7 -0
  25. package/dist/lib/node-esm/blueprint-definition-O7ZM3HUF.mjs +12 -0
  26. package/dist/lib/node-esm/blueprint-definition-O7ZM3HUF.mjs.map +7 -0
  27. package/dist/lib/node-esm/chunk-F4C2HVZL.mjs +295 -0
  28. package/dist/lib/node-esm/chunk-F4C2HVZL.mjs.map +7 -0
  29. package/dist/lib/node-esm/chunk-KNOOJFIR.mjs +29 -0
  30. package/dist/lib/node-esm/chunk-KNOOJFIR.mjs.map +7 -0
  31. package/dist/lib/node-esm/chunk-MP3A3AGA.mjs +85 -0
  32. package/dist/lib/node-esm/chunk-MP3A3AGA.mjs.map +7 -0
  33. package/dist/lib/node-esm/chunk-RTCAAOLF.mjs +170 -0
  34. package/dist/lib/node-esm/chunk-RTCAAOLF.mjs.map +7 -0
  35. package/dist/lib/node-esm/{chunk-TD2SWK2H.mjs → chunk-VVXTAVIS.mjs} +3 -3
  36. package/dist/lib/node-esm/chunk-VVXTAVIS.mjs.map +7 -0
  37. package/dist/lib/node-esm/index.mjs +56 -28
  38. package/dist/lib/node-esm/index.mjs.map +3 -3
  39. package/dist/lib/node-esm/intent-resolver-HK5L6P6Z.mjs +26 -0
  40. package/dist/lib/node-esm/intent-resolver-HK5L6P6Z.mjs.map +7 -0
  41. package/dist/lib/node-esm/meta.json +1 -1
  42. package/dist/lib/node-esm/react-surface-VO4PSY6O.mjs +39 -0
  43. package/dist/lib/node-esm/react-surface-VO4PSY6O.mjs.map +7 -0
  44. package/dist/lib/node-esm/types/index.mjs +6 -7
  45. package/dist/types/src/ChessPlugin.d.ts +1 -1
  46. package/dist/types/src/ChessPlugin.d.ts.map +1 -1
  47. package/dist/types/src/capabilities/artifact-definition.d.ts +1 -1
  48. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  49. package/dist/types/src/capabilities/blueprint-definition.d.ts +6 -0
  50. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/index.d.ts +4 -3
  52. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  53. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  54. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  55. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  56. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  57. package/dist/types/src/components/Card.stories.d.ts +22 -0
  58. package/dist/types/src/components/Card.stories.d.ts.map +1 -0
  59. package/dist/types/src/components/Chessboard.d.ts +38 -0
  60. package/dist/types/src/components/Chessboard.d.ts.map +1 -0
  61. package/dist/types/src/components/Chessboard.stories.d.ts +41 -5
  62. package/dist/types/src/components/Chessboard.stories.d.ts.map +1 -1
  63. package/dist/types/src/components/ChessboardArticle.d.ts +7 -0
  64. package/dist/types/src/components/ChessboardArticle.d.ts.map +1 -0
  65. package/dist/types/src/components/ChessboardContainer.d.ts +9 -0
  66. package/dist/types/src/components/ChessboardContainer.d.ts.map +1 -0
  67. package/dist/types/src/components/Info.d.ts +21 -0
  68. package/dist/types/src/components/Info.d.ts.map +1 -0
  69. package/dist/types/src/components/index.d.ts +2 -5
  70. package/dist/types/src/components/index.d.ts.map +1 -1
  71. package/dist/types/src/functions/create.d.ts +14 -0
  72. package/dist/types/src/functions/create.d.ts.map +1 -0
  73. package/dist/types/src/functions/index.d.ts +4 -0
  74. package/dist/types/src/functions/index.d.ts.map +1 -0
  75. package/dist/types/src/functions/move.d.ts +8 -0
  76. package/dist/types/src/functions/move.d.ts.map +1 -0
  77. package/dist/types/src/functions/play.d.ts +9 -0
  78. package/dist/types/src/functions/play.d.ts.map +1 -0
  79. package/dist/types/src/index.d.ts +2 -0
  80. package/dist/types/src/index.d.ts.map +1 -1
  81. package/dist/types/src/meta.d.ts +0 -1
  82. package/dist/types/src/meta.d.ts.map +1 -1
  83. package/dist/types/src/translations.d.ts +12 -2
  84. package/dist/types/src/translations.d.ts.map +1 -1
  85. package/dist/types/src/types/Chess.d.ts +27 -0
  86. package/dist/types/src/types/Chess.d.ts.map +1 -0
  87. package/dist/types/src/types/ChessAction.d.ts +24 -0
  88. package/dist/types/src/types/ChessAction.d.ts.map +1 -0
  89. package/dist/types/src/types/index.d.ts +2 -2
  90. package/dist/types/src/types/index.d.ts.map +1 -1
  91. package/dist/types/tsconfig.tsbuildinfo +1 -1
  92. package/package.json +44 -34
  93. package/src/ChessPlugin.tsx +49 -48
  94. package/src/capabilities/artifact-definition.ts +27 -22
  95. package/src/capabilities/blueprint-definition.ts +36 -0
  96. package/src/capabilities/index.ts +3 -1
  97. package/src/capabilities/intent-resolver.ts +3 -7
  98. package/src/capabilities/react-surface.tsx +6 -13
  99. package/src/components/Card.stories.tsx +66 -0
  100. package/src/components/Chessboard.stories.tsx +36 -26
  101. package/src/components/Chessboard.tsx +177 -0
  102. package/src/components/ChessboardArticle.tsx +68 -0
  103. package/src/components/ChessboardContainer.tsx +44 -0
  104. package/src/components/Info.tsx +236 -0
  105. package/src/components/index.ts +2 -2
  106. package/src/functions/create.ts +32 -0
  107. package/src/functions/index.ts +7 -0
  108. package/src/functions/move.ts +45 -0
  109. package/src/functions/play.ts +55 -0
  110. package/src/index.ts +2 -0
  111. package/src/meta.ts +7 -5
  112. package/src/translations.ts +12 -2
  113. package/src/types/Chess.ts +60 -0
  114. package/src/types/ChessAction.ts +19 -0
  115. package/src/types/index.ts +2 -2
  116. package/dist/lib/browser/ChessContainer-DL5J6VIK.mjs +0 -101
  117. package/dist/lib/browser/ChessContainer-DL5J6VIK.mjs.map +0 -7
  118. package/dist/lib/browser/artifact-definition-4AMZY5LQ.mjs +0 -182
  119. package/dist/lib/browser/artifact-definition-4AMZY5LQ.mjs.map +0 -7
  120. package/dist/lib/browser/chunk-2VF6U52K.mjs +0 -8
  121. package/dist/lib/browser/chunk-2VF6U52K.mjs.map +0 -7
  122. package/dist/lib/browser/chunk-A3WEUKPK.mjs +0 -61
  123. package/dist/lib/browser/chunk-A3WEUKPK.mjs.map +0 -7
  124. package/dist/lib/browser/chunk-AZSYKAFK.mjs +0 -61
  125. package/dist/lib/browser/chunk-AZSYKAFK.mjs.map +0 -7
  126. package/dist/lib/browser/intent-resolver-KQYYX44R.mjs +0 -28
  127. package/dist/lib/browser/intent-resolver-KQYYX44R.mjs.map +0 -7
  128. package/dist/lib/browser/react-surface-4TBVZUKU.mjs +0 -43
  129. package/dist/lib/browser/react-surface-4TBVZUKU.mjs.map +0 -7
  130. package/dist/lib/node-esm/ChessContainer-WVOL2KSE.mjs +0 -102
  131. package/dist/lib/node-esm/ChessContainer-WVOL2KSE.mjs.map +0 -7
  132. package/dist/lib/node-esm/artifact-definition-TT3747JB.mjs +0 -183
  133. package/dist/lib/node-esm/artifact-definition-TT3747JB.mjs.map +0 -7
  134. package/dist/lib/node-esm/chunk-2DRWEDVT.mjs +0 -63
  135. package/dist/lib/node-esm/chunk-2DRWEDVT.mjs.map +0 -7
  136. package/dist/lib/node-esm/chunk-IS3Y25PL.mjs +0 -63
  137. package/dist/lib/node-esm/chunk-IS3Y25PL.mjs.map +0 -7
  138. package/dist/lib/node-esm/chunk-TD2SWK2H.mjs.map +0 -7
  139. package/dist/lib/node-esm/intent-resolver-GXK2JROQ.mjs +0 -29
  140. package/dist/lib/node-esm/intent-resolver-GXK2JROQ.mjs.map +0 -7
  141. package/dist/lib/node-esm/react-surface-PAUEOIBL.mjs +0 -44
  142. package/dist/lib/node-esm/react-surface-PAUEOIBL.mjs.map +0 -7
  143. package/dist/types/src/components/ChessComponent.d.ts +0 -6
  144. package/dist/types/src/components/ChessComponent.d.ts.map +0 -1
  145. package/dist/types/src/components/ChessContainer.d.ts +0 -8
  146. package/dist/types/src/components/ChessContainer.d.ts.map +0 -1
  147. package/dist/types/src/components/ChessPanel.d.ts +0 -11
  148. package/dist/types/src/components/ChessPanel.d.ts.map +0 -1
  149. package/dist/types/src/components/PlayerSelector.d.ts +0 -8
  150. package/dist/types/src/components/PlayerSelector.d.ts.map +0 -1
  151. package/dist/types/src/types/schema.d.ts +0 -29
  152. package/dist/types/src/types/schema.d.ts.map +0 -1
  153. package/dist/types/src/types/types.d.ts +0 -32
  154. package/dist/types/src/types/types.d.ts.map +0 -1
  155. package/src/components/ChessComponent.tsx +0 -54
  156. package/src/components/ChessContainer.tsx +0 -35
  157. package/src/components/ChessPanel.tsx +0 -67
  158. package/src/components/PlayerSelector.tsx +0 -80
  159. package/src/types/schema.ts +0 -34
  160. package/src/types/types.ts +0 -20
@@ -0,0 +1,177 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, {
6
+ type PropsWithChildren,
7
+ forwardRef,
8
+ useCallback,
9
+ useEffect,
10
+ useImperativeHandle,
11
+ useMemo,
12
+ useRef,
13
+ } from 'react';
14
+
15
+ import { addEventListener } from '@dxos/async';
16
+ import { type ThemedClassName } from '@dxos/react-ui';
17
+ import {
18
+ ChessModel,
19
+ Gameboard,
20
+ type GameboardRootProps,
21
+ Chessboard as NaturalChessboard,
22
+ type ChessboardProps as NaturalChessboardProps,
23
+ getRawPgn,
24
+ useGameboardContext,
25
+ } from '@dxos/react-ui-gameboard';
26
+ import { useSoundEffect } from '@dxos/react-ui-sfx';
27
+ import { mx } from '@dxos/react-ui-theme';
28
+
29
+ import { type Chess } from '../types';
30
+
31
+ import { Info, type InfoProps } from './Info';
32
+
33
+ export interface ChessboardController {
34
+ setMoveNumber(index: number): void;
35
+ }
36
+
37
+ export class ExtendedChessModel extends ChessModel {
38
+ constructor(readonly object: Chess.Game) {
39
+ super();
40
+ }
41
+ }
42
+
43
+ //
44
+ // Root
45
+ //
46
+
47
+ type RootProps = PropsWithChildren<{
48
+ game: Chess.Game;
49
+ }>;
50
+
51
+ const Root = forwardRef<ChessboardController, RootProps>(({ game, children }, forwardedRef) => {
52
+ const model = useMemo(() => new ExtendedChessModel(game), []);
53
+ const click = useSoundEffect('Click');
54
+
55
+ // Controller.
56
+ useImperativeHandle(forwardedRef, () => {
57
+ return {
58
+ setMoveNumber: (index) => model.setMoveIndex(index),
59
+ };
60
+ }, [model]);
61
+
62
+ // External change.
63
+ // NOTE: Warning if user has not interacted with the board.
64
+ useEffect(() => {
65
+ if (model.pgn !== getRawPgn(game.pgn ?? '')) {
66
+ const silent = model.pgn === '*';
67
+ model.update(game.pgn);
68
+ if (!silent) {
69
+ void click.play();
70
+ }
71
+ }
72
+ }, [game.pgn]);
73
+
74
+ // Move.
75
+ const handleDrop = useCallback<NonNullable<GameboardRootProps<ChessModel>['onDrop']>>(
76
+ (move) => {
77
+ if (!model.makeMove(move)) {
78
+ return false;
79
+ }
80
+
81
+ void click.play();
82
+ game.pgn = model.pgn;
83
+ return true;
84
+ },
85
+ [model],
86
+ );
87
+
88
+ return (
89
+ <Gameboard.Root model={model} onDrop={handleDrop}>
90
+ {children}
91
+ </Gameboard.Root>
92
+ );
93
+ });
94
+
95
+ //
96
+ // Content
97
+ //
98
+
99
+ type Role = 'card--popover' | 'card--intrinsic' | 'card--extrinsic';
100
+
101
+ type ContentProps = ThemedClassName<PropsWithChildren<{ role?: Role }>>;
102
+
103
+ const Content = ({ classNames, children, role }: ContentProps) => {
104
+ return (
105
+ <Gameboard.Content
106
+ classNames={mx(classNames, role === 'card--popover' && 'size-container popover-square')}
107
+ grow={!role || role === 'card--extrinsic'}
108
+ contain={!role || role === 'card--extrinsic' || role === 'card--popover'}
109
+ >
110
+ {children}
111
+ </Gameboard.Content>
112
+ );
113
+ };
114
+
115
+ //
116
+ // Board
117
+ //
118
+
119
+ type BoardProps = NaturalChessboardProps;
120
+
121
+ const Board = (props: BoardProps) => {
122
+ const { model } = useGameboardContext<ChessModel>(Board.displayName);
123
+
124
+ // Keyboard navigation.
125
+ const ref = useRef<HTMLDivElement>(null);
126
+ useEffect(() => {
127
+ if (!ref.current) {
128
+ return;
129
+ }
130
+
131
+ // Participate in keyboard navigation (set tabIndex={0})
132
+ ref.current.setAttribute('data-arrow-keys', 'all');
133
+
134
+ return addEventListener(ref.current, 'keydown', (ev) => {
135
+ switch (ev.key) {
136
+ case 'ArrowUp':
137
+ model.setMoveIndex(0);
138
+ break;
139
+ case 'ArrowDown':
140
+ model.setMoveIndex(model.game.history().length);
141
+ break;
142
+ case 'ArrowLeft':
143
+ if (model.moveIndex.value > 0) {
144
+ model.setMoveIndex(model.moveIndex.value - 1);
145
+ }
146
+ break;
147
+ case 'ArrowRight':
148
+ if (model.moveIndex.value < model.game.history().length) {
149
+ model.setMoveIndex(model.moveIndex.value + 1);
150
+ }
151
+ break;
152
+ }
153
+ });
154
+ }, [model]);
155
+
156
+ return <NaturalChessboard ref={ref} {...props} />;
157
+ };
158
+
159
+ Board.displayName = 'Chessboard.Board';
160
+
161
+ //
162
+ // Chessboard
163
+ //
164
+
165
+ export const Chessboard = {
166
+ Root: Root,
167
+ Content: Content,
168
+ Board: Board,
169
+ Info: Info,
170
+ };
171
+
172
+ export type {
173
+ RootProps as ChessboardRootProps,
174
+ ContentProps as ChessboardContentProps,
175
+ BoardProps as ChessboardBoardProps,
176
+ InfoProps as ChessboardInfoProps,
177
+ };
@@ -0,0 +1,68 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useRef, useState } from 'react';
6
+
7
+ import { Toolbar, useTranslation } from '@dxos/react-ui';
8
+ import { type Player } from '@dxos/react-ui-gameboard';
9
+ import { StackItem } from '@dxos/react-ui-stack';
10
+ import { mx } from '@dxos/react-ui-theme';
11
+
12
+ import { meta } from '../meta';
13
+ import { type Chess } from '../types';
14
+
15
+ import { Chessboard, type ChessboardController, type ChessboardInfoProps } from './Chessboard';
16
+
17
+ export const ChessboardArticle = ({ game, role }: { game: Chess.Game; role?: string }) => {
18
+ const { t } = useTranslation(meta.id);
19
+ const [orientation, setOrientation] = useState<Player>('white');
20
+ const [open, setOpen] = useState(true);
21
+ const controller = useRef<ChessboardController>(null);
22
+
23
+ // TODO(burdon): Keyboard handler.
24
+ const handleSelect = useCallback<NonNullable<ChessboardInfoProps['onSelect']>>((index) => {
25
+ controller.current?.setMoveNumber(index);
26
+ }, []);
27
+
28
+ return (
29
+ <StackItem.Content toolbar classNames='@container'>
30
+ <Chessboard.Root game={game} ref={controller}>
31
+ <Toolbar.Root>
32
+ <Toolbar.IconButton
33
+ icon='ph--info--regular'
34
+ iconOnly
35
+ label={t('toggle info button')}
36
+ disabled={open}
37
+ classNames={mx('invisible @3xl:visible')}
38
+ onClick={() => setOpen((open) => !open)}
39
+ />
40
+ </Toolbar.Root>
41
+ <div
42
+ className={mx(
43
+ 'grid bs-full is-full gap-2',
44
+ open && '@3xl:grid-cols-[1fr_320px]',
45
+ role === 'section' && 'aspect-square',
46
+ role === 'section' && open && '@3xl:aspect-auto',
47
+ )}
48
+ >
49
+ <Chessboard.Content>
50
+ <Chessboard.Board classNames='m-4 rounded-sm overflow-hidden' orientation={orientation} />
51
+ </Chessboard.Content>
52
+ {open && (
53
+ <div className='hidden @3xl:flex flex-col p-8 justify-center items-center overflow-hidden'>
54
+ <Chessboard.Info
55
+ orientation={orientation}
56
+ min={8}
57
+ max={8}
58
+ onOrientationChange={setOrientation}
59
+ onClose={() => setOpen(false)}
60
+ onSelect={handleSelect}
61
+ />
62
+ </div>
63
+ )}
64
+ </div>
65
+ </Chessboard.Root>
66
+ </StackItem.Content>
67
+ );
68
+ };
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { getSpace } from '@dxos/react-client/echo';
8
+
9
+ import { type Chess } from '../types';
10
+
11
+ import { Chessboard } from './Chessboard';
12
+ import { ChessboardArticle } from './ChessboardArticle';
13
+
14
+ export type ChessboardContainerProps = {
15
+ game: Chess.Game;
16
+ role?: string;
17
+ };
18
+
19
+ export const ChessboardContainer = ({ game, role }: ChessboardContainerProps) => {
20
+ const space = getSpace(game);
21
+ if (!space) {
22
+ return null;
23
+ }
24
+
25
+ switch (role) {
26
+ case 'card--popover':
27
+ case 'card--intrinsic':
28
+ case 'card--extrinsic': {
29
+ return (
30
+ <Chessboard.Root game={game}>
31
+ <Chessboard.Content role={role}>
32
+ <Chessboard.Board />
33
+ </Chessboard.Content>
34
+ </Chessboard.Root>
35
+ );
36
+ }
37
+
38
+ default: {
39
+ return <ChessboardArticle game={game} role={role} />;
40
+ }
41
+ }
42
+ };
43
+
44
+ export default ChessboardContainer;
@@ -0,0 +1,236 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { type JSX, type PropsWithChildren, useEffect, useMemo, useRef } from 'react';
6
+
7
+ import { generateName } from '@dxos/display-name';
8
+ import { type SpaceMember, getSpace, useMembers } from '@dxos/react-client/echo';
9
+ import { Icon, IconButton, Select, type ThemedClassName, useTranslation } from '@dxos/react-ui';
10
+ import { type Player, useGameboardContext } from '@dxos/react-ui-gameboard';
11
+ import { mx } from '@dxos/react-ui-theme';
12
+
13
+ import { meta } from '../meta';
14
+
15
+ import { type ExtendedChessModel } from './Chessboard';
16
+
17
+ export type InfoProps = ThemedClassName<
18
+ {
19
+ orientation?: Player;
20
+ onOrientationChange?: (orientation: Player) => void;
21
+ onClose?: () => void;
22
+ } & Pick<HistoryProps, 'min' | 'max' | 'onSelect'>
23
+ >;
24
+
25
+ export const Info = ({ classNames, orientation = 'white', onOrientationChange, onClose, ...props }: InfoProps) => {
26
+ const { t } = useTranslation(meta.id);
27
+ const { model } = useGameboardContext<ExtendedChessModel>(Info.displayName);
28
+ const members = useMembers(getSpace(model.object)?.key);
29
+
30
+ return (
31
+ <div
32
+ className={mx(
33
+ 'grid grid-rows-[min-content_1fr_min-content] is-full min-is-[18rem] p-2 overflow-hidden bg-groupSurface rounded-sm',
34
+ classNames,
35
+ )}
36
+ >
37
+ <PlayerIndicator
38
+ model={model}
39
+ player={orientation === 'white' ? 'black' : 'white'}
40
+ icon={
41
+ onClose && (
42
+ <IconButton icon='ph--x--regular' iconOnly label={t('close info button')} size={4} onClick={onClose} />
43
+ )
44
+ }
45
+ >
46
+ <PlayerSelector
47
+ value={model.object.players?.[orientation === 'white' ? 'black' : 'white']}
48
+ onValueChange={(value) => {
49
+ model.object.players![orientation === 'white' ? 'black' : 'white'] = value;
50
+ }}
51
+ members={members}
52
+ />
53
+ </PlayerIndicator>
54
+
55
+ <History model={model} {...props} />
56
+
57
+ <PlayerIndicator
58
+ model={model}
59
+ player={orientation}
60
+ icon={
61
+ onOrientationChange && (
62
+ <IconButton
63
+ classNames={mx('transition duration-200 ease-linear', orientation === 'white' && 'rotate-180')}
64
+ icon='ph--arrows-clockwise--regular'
65
+ iconOnly
66
+ label={t('flip board button')}
67
+ size={4}
68
+ onClick={() => onOrientationChange(orientation === 'white' ? 'black' : 'white')}
69
+ />
70
+ )
71
+ }
72
+ >
73
+ <PlayerSelector
74
+ value={model.object.players?.[orientation]}
75
+ onValueChange={(value) => {
76
+ model.object.players![orientation] = value;
77
+ }}
78
+ members={members}
79
+ />
80
+ </PlayerIndicator>
81
+ </div>
82
+ );
83
+ };
84
+
85
+ Info.displayName = 'Chessboard.Info';
86
+
87
+ //
88
+ // History
89
+ //
90
+
91
+ type HistoryProps = ThemedClassName<{
92
+ model: ExtendedChessModel;
93
+ min?: number;
94
+ max?: number;
95
+ onSelect?: (index: number) => void;
96
+ }>;
97
+
98
+ const History = ({ classNames, model, min, max, onSelect }: HistoryProps) => {
99
+ const { t } = useTranslation(meta.id);
100
+ const label = model.game.isGameOver()
101
+ ? model.game.isCheckmate()
102
+ ? t('game.checkmate label')
103
+ : model.game.isStalemate()
104
+ ? t('game.stalemate label')
105
+ : t('game.draw label')
106
+ : model.game.isCheck()
107
+ ? t('game.check label')
108
+ : undefined;
109
+
110
+ const history = model.game.history();
111
+ const moves = useMemo(
112
+ () =>
113
+ history.reduce(
114
+ (acc, move, index) => {
115
+ if (index % 2 === 0) {
116
+ acc.push([{ index, move }]);
117
+ } else {
118
+ acc[acc.length - 1].push({ index, move });
119
+ }
120
+ return acc;
121
+ },
122
+ [] as { index: number; move: string }[][],
123
+ ),
124
+ [history.length],
125
+ );
126
+
127
+ const scrollerRef = useRef<HTMLDivElement>(null);
128
+ useEffect(() => {
129
+ scrollerRef.current?.scrollTo({ top: scrollerRef.current.scrollHeight });
130
+ }, [history.length]);
131
+
132
+ useEffect(() => {
133
+ const div = scrollerRef.current?.querySelector(`[data-index="${model.moveIndex.value - 1}"]`);
134
+ scrollerRef.current?.classList.add('scrollbar-none');
135
+ div?.scrollIntoView({ behavior: 'smooth' });
136
+ scrollerRef.current?.classList.remove('scrollbar-none');
137
+ }, [model.moveIndex.value]);
138
+
139
+ return (
140
+ <div
141
+ ref={scrollerRef}
142
+ className={mx('overflow-y-auto', classNames)}
143
+ style={{
144
+ minHeight: min === undefined ? 'auto' : `${min * 24}px`,
145
+ maxHeight: max === undefined ? 'auto' : `${max * 24}px`,
146
+ }}
147
+ >
148
+ {moves.map(([a, b], index) => (
149
+ <div key={index} className='grid grid-cols-[3rem_1fr_1fr_1rem] gap-2 pis-4 leading-1'>
150
+ <div className='content-center text-xs text-subdued'>{index + 1}</div>
151
+ {a && (
152
+ <div
153
+ data-index={a.index}
154
+ className={mx('pis-2 cursor-pointer', a.index === model.moveIndex.value - 1 && 'bg-primary-500')}
155
+ onClick={() => onSelect?.(a.index + 1)}
156
+ >
157
+ {a.move}
158
+ </div>
159
+ )}
160
+ {b && (
161
+ <div
162
+ data-index={b.index}
163
+ className={mx('pis-2 cursor-pointer', b.index === model.moveIndex.value - 1 && 'bg-primary-500')}
164
+ onClick={() => onSelect?.(b.index + 1)}
165
+ >
166
+ {b.move}
167
+ </div>
168
+ )}
169
+ </div>
170
+ ))}
171
+ {label && <div className='text-center'>{label}</div>}
172
+ </div>
173
+ );
174
+ };
175
+
176
+ //
177
+ // PlayerIndicator
178
+ //
179
+
180
+ type PlayerIndicatorProps = PropsWithChildren<{
181
+ model: ExtendedChessModel;
182
+ player: Player;
183
+ icon?: JSX.Element;
184
+ }>;
185
+
186
+ const PlayerIndicator = ({ children, model, player, icon }: PlayerIndicatorProps) => {
187
+ const turn = player === (model.game.turn() === 'w' ? 'white' : 'black');
188
+ return (
189
+ <div className='grid grid-cols-[2rem_1fr_2rem] gap-2 bs-[--rail-size] pli-1 flex items-center overflow-hidden'>
190
+ <div className='place-items-center'>
191
+ <Icon
192
+ icon={turn ? 'ph--circle--fill' : 'ph--circle--thin'}
193
+ size={6}
194
+ classNames={mx(turn && (model.game.isCheckmate() ? 'text-red-500' : 'text-green-500'))}
195
+ />
196
+ </div>
197
+ <div className='truncate overflow-hidden items-center'>{children}</div>
198
+ {icon}
199
+ </div>
200
+ );
201
+ };
202
+
203
+ //
204
+ // PlayerSelector
205
+ //
206
+
207
+ type PlayerSelectorProps = {
208
+ value?: string;
209
+ onValueChange: (player: string) => void;
210
+ members: SpaceMember[];
211
+ };
212
+
213
+ const PlayerSelector = ({ value, onValueChange, members }: PlayerSelectorProps) => {
214
+ const { t } = useTranslation(meta.id);
215
+ return (
216
+ <Select.Root value={value} onValueChange={onValueChange}>
217
+ <Select.TriggerButton placeholder={t('select player button')} />
218
+ <Select.Portal>
219
+ <Select.Content>
220
+ <Select.Viewport>
221
+ {members.map((member) => {
222
+ const memberKey = member.identity.identityKey.toHex();
223
+ const displayName = member.identity?.profile?.displayName || generateName(memberKey);
224
+ return (
225
+ <Select.Option key={memberKey} value={memberKey}>
226
+ {displayName}
227
+ </Select.Option>
228
+ );
229
+ })}
230
+ </Select.Viewport>
231
+ <Select.Arrow />
232
+ </Select.Content>
233
+ </Select.Portal>
234
+ </Select.Root>
235
+ );
236
+ };
@@ -4,6 +4,6 @@
4
4
 
5
5
  import { lazy } from 'react';
6
6
 
7
- export * from './ChessComponent';
7
+ export * from './Chessboard';
8
8
 
9
- export const ChessContainer = lazy(() => import('./ChessContainer'));
9
+ export const ChessboardContainer = lazy(() => import('./ChessboardContainer'));
@@ -0,0 +1,32 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Schema from 'effect/Schema';
7
+
8
+ import { DatabaseService, defineFunction } from '@dxos/functions';
9
+
10
+ import { Chess } from '../types';
11
+
12
+ export default defineFunction({
13
+ key: 'dxos.org/function/chess/create',
14
+ name: 'Create Chess',
15
+ description: 'Creates a new chess game.',
16
+ inputSchema: Schema.Struct({
17
+ pgn: Schema.optional(
18
+ Schema.String.annotations({
19
+ description: 'Portable Game Notation.',
20
+ }),
21
+ ),
22
+ fen: Schema.optional(
23
+ Schema.String.annotations({
24
+ description: 'Forsyth-Edwards Notation.',
25
+ }),
26
+ ),
27
+ }),
28
+ outputSchema: Chess.Game,
29
+ handler: Effect.fn(function* ({ data: { pgn, fen } }) {
30
+ return yield* DatabaseService.add(Chess.makeGame({ pgn, fen }));
31
+ }),
32
+ });
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export { default as create } from './create';
6
+ export { default as move } from './move';
7
+ export { default as play } from './play';
@@ -0,0 +1,45 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Chess as ChessJS } from 'chess.js';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Schema from 'effect/Schema';
8
+
9
+ import { ArtifactId } from '@dxos/assistant';
10
+ import { DatabaseService, defineFunction } from '@dxos/functions';
11
+
12
+ import { Chess } from '../types';
13
+
14
+ export default defineFunction({
15
+ key: 'dxos.org/function/chess/move',
16
+ name: 'Move',
17
+ description: 'Makes a move in the given chess game.',
18
+ inputSchema: Schema.Struct({
19
+ id: ArtifactId.annotations({
20
+ description: 'The ID of the chess object.',
21
+ }),
22
+ move: Schema.String.annotations({
23
+ description: 'The move to make in the chess game.',
24
+ examples: ['e4', 'Bf3'],
25
+ }),
26
+ }),
27
+ outputSchema: Schema.Struct({
28
+ pgn: Schema.String.annotations({
29
+ description: 'The PGN of the game after the move was played.',
30
+ }),
31
+ }),
32
+ handler: Effect.fn(function* ({ data: { id, move } }) {
33
+ const object = yield* DatabaseService.resolve(ArtifactId.toDXN(id), Chess.Game);
34
+ const chess = new ChessJS();
35
+ if (object.pgn) {
36
+ chess.loadPgn(object.pgn);
37
+ } else if (object.fen) {
38
+ chess.load(object.fen);
39
+ }
40
+
41
+ chess.move(move, { strict: false });
42
+ object.pgn = chess.pgn();
43
+ return { pgn: object.pgn };
44
+ }),
45
+ });
@@ -0,0 +1,55 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Chess as ChessJS } from 'chess.js';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Schema from 'effect/Schema';
8
+
9
+ import { ArtifactId } from '@dxos/assistant';
10
+ import { DatabaseService, defineFunction } from '@dxos/functions';
11
+
12
+ import { Chess } from '../types';
13
+
14
+ export default defineFunction({
15
+ key: 'dxos.org/function/chess/play',
16
+ name: 'Play',
17
+ description: 'Uses the chess engine to play the next move.',
18
+ inputSchema: Schema.Struct({
19
+ id: ArtifactId.annotations({
20
+ description: 'The ID of the chess object.',
21
+ }),
22
+ side: Schema.optional(Schema.Literal('white', 'black', 'any')).annotations({
23
+ description: 'The side to play.',
24
+ defaultValue: 'any',
25
+ }),
26
+ }),
27
+ outputSchema: Schema.Struct({
28
+ pgn: Schema.String.annotations({
29
+ description: 'The PGN of the game after the move was played.',
30
+ }),
31
+ move: Schema.optional(Schema.String).annotations({
32
+ description: 'The move that was played.',
33
+ }),
34
+ }),
35
+ handler: Effect.fn(function* ({ data: { id, side = 'any' } }) {
36
+ const object = yield* DatabaseService.resolve(ArtifactId.toDXN(id), Chess.Game);
37
+ const chess = new ChessJS();
38
+ if (object.pgn) {
39
+ chess.loadPgn(object.pgn);
40
+ } else if (object.fen) {
41
+ chess.load(object.fen);
42
+ }
43
+
44
+ if (!(side === 'any' || (chess.turn() === 'w' && side === 'white') || (chess.turn() === 'b' && side === 'black'))) {
45
+ return { move: undefined, pgn: object.pgn! };
46
+ }
47
+
48
+ const moves = chess.moves();
49
+ const move = moves[Math.floor(Math.random() * moves.length)];
50
+
51
+ chess.move(move, { strict: false });
52
+ object.pgn = chess.pgn();
53
+ return { move, pgn: object.pgn };
54
+ }),
55
+ });
package/src/index.ts CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  export * from './capabilities';
6
6
  export * from './components';
7
+ export * from './functions';
7
8
  export * from './meta';
9
+ export * from './types';
8
10
 
9
11
  export * from './ChessPlugin';