@dxos/react-ui-gameboard 0.8.4-main.c1de068 → 0.8.4-main.fd6878d

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 (61) hide show
  1. package/dist/lib/browser/index.mjs +309 -274
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +309 -274
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Chessboard/Chessboard.d.ts.map +1 -0
  8. package/dist/types/src/components/Chessboard/Chessboard.stories.d.ts +28 -0
  9. package/dist/types/src/components/Chessboard/Chessboard.stories.d.ts.map +1 -0
  10. package/dist/types/src/{Chessboard → components/Chessboard}/chess.d.ts +19 -6
  11. package/dist/types/src/components/Chessboard/chess.d.ts.map +1 -0
  12. package/dist/types/src/components/Chessboard/index.d.ts.map +1 -0
  13. package/dist/types/src/components/Gameboard/Gameboard.d.ts +37 -0
  14. package/dist/types/src/components/Gameboard/Gameboard.d.ts.map +1 -0
  15. package/dist/types/src/{Gameboard → components/Gameboard}/Piece.d.ts +3 -2
  16. package/dist/types/src/components/Gameboard/Piece.d.ts.map +1 -0
  17. package/dist/types/src/components/Gameboard/Square.d.ts.map +1 -0
  18. package/dist/types/src/components/Gameboard/index.d.ts +4 -0
  19. package/dist/types/src/components/Gameboard/index.d.ts.map +1 -0
  20. package/dist/types/src/{Gameboard → components/Gameboard}/types.d.ts +1 -0
  21. package/dist/types/src/components/Gameboard/types.d.ts.map +1 -0
  22. package/dist/types/src/components/Gameboard/util.d.ts.map +1 -0
  23. package/dist/types/src/components/index.d.ts +3 -0
  24. package/dist/types/src/components/index.d.ts.map +1 -0
  25. package/dist/types/src/index.d.ts +1 -2
  26. package/dist/types/src/index.d.ts.map +1 -1
  27. package/dist/types/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +14 -12
  29. package/src/{Chessboard → components/Chessboard}/Chessboard.stories.tsx +26 -25
  30. package/src/{Chessboard → components/Chessboard}/Chessboard.tsx +32 -35
  31. package/src/{Chessboard → components/Chessboard}/chess.ts +86 -26
  32. package/src/components/Gameboard/Gameboard.tsx +139 -0
  33. package/src/{Gameboard → components/Gameboard}/Piece.tsx +19 -20
  34. package/src/{Gameboard → components/Gameboard}/Square.tsx +4 -4
  35. package/src/{Gameboard → components/Gameboard}/index.ts +0 -3
  36. package/src/{Gameboard → components/Gameboard}/types.ts +2 -0
  37. package/src/components/index.ts +6 -0
  38. package/src/index.ts +1 -2
  39. package/dist/types/src/Chessboard/Chessboard.d.ts.map +0 -1
  40. package/dist/types/src/Chessboard/Chessboard.stories.d.ts +0 -16
  41. package/dist/types/src/Chessboard/Chessboard.stories.d.ts.map +0 -1
  42. package/dist/types/src/Chessboard/chess.d.ts.map +0 -1
  43. package/dist/types/src/Chessboard/index.d.ts.map +0 -1
  44. package/dist/types/src/Gameboard/Gameboard.d.ts +0 -23
  45. package/dist/types/src/Gameboard/Gameboard.d.ts.map +0 -1
  46. package/dist/types/src/Gameboard/Piece.d.ts.map +0 -1
  47. package/dist/types/src/Gameboard/Square.d.ts.map +0 -1
  48. package/dist/types/src/Gameboard/context.d.ts +0 -10
  49. package/dist/types/src/Gameboard/context.d.ts.map +0 -1
  50. package/dist/types/src/Gameboard/index.d.ts +0 -7
  51. package/dist/types/src/Gameboard/index.d.ts.map +0 -1
  52. package/dist/types/src/Gameboard/types.d.ts.map +0 -1
  53. package/dist/types/src/Gameboard/util.d.ts.map +0 -1
  54. package/src/Gameboard/Gameboard.tsx +0 -103
  55. package/src/Gameboard/context.ts +0 -22
  56. /package/dist/types/src/{Chessboard → components/Chessboard}/Chessboard.d.ts +0 -0
  57. /package/dist/types/src/{Chessboard → components/Chessboard}/index.d.ts +0 -0
  58. /package/dist/types/src/{Gameboard → components/Gameboard}/Square.d.ts +0 -0
  59. /package/dist/types/src/{Gameboard → components/Gameboard}/util.d.ts +0 -0
  60. /package/src/{Chessboard → components/Chessboard}/index.ts +0 -0
  61. /package/src/{Gameboard → components/Gameboard}/util.ts +0 -0
@@ -5,22 +5,14 @@ var __export = (target, all) => {
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
- // src/Gameboard/context.ts
9
- import { createContext, useContext } from "react";
10
- import { raise } from "@dxos/debug";
11
- var GameboardContext = createContext(void 0);
12
- var useBoardContext = () => {
13
- return useContext(GameboardContext) ?? raise(new Error("Missing BoardContext"));
14
- };
15
-
16
- // src/Gameboard/types.ts
8
+ // src/components/Gameboard/types.ts
17
9
  var locationToString = (location) => location.join("-");
18
10
  var stringToLocation = (str) => str.split("-").map(Number);
19
11
  var isPiece = (piece) => piece != null && typeof piece === "object" && "id" in piece && "type" in piece && "location" in piece && isLocation(piece.location);
20
12
  var isLocation = (token) => Array.isArray(token) && token.length === 2 && token.every((val) => typeof val === "number");
21
13
  var isEqualLocation = (l1, l2) => l1[0] === l2[0] && l1[1] === l2[1];
22
14
 
23
- // src/Gameboard/util.ts
15
+ // src/components/Gameboard/util.ts
24
16
  var getRelativeBounds = (container, element) => {
25
17
  const containerRect = container.getBoundingClientRect();
26
18
  const elementRect = element.getBoundingClientRect();
@@ -32,133 +24,170 @@ var getRelativeBounds = (container, element) => {
32
24
  };
33
25
  };
34
26
 
35
- // src/Gameboard/Gameboard.tsx
36
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
27
+ // src/components/Gameboard/Gameboard.tsx
28
+ import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
37
29
  import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
38
- import React, { forwardRef, useCallback, useEffect, useState } from "react";
30
+ import { createContext } from "@radix-ui/react-context";
31
+ import React3, { forwardRef, useCallback, useEffect as useEffect3, useState as useState3 } from "react";
32
+ import { log as log3 } from "@dxos/log";
33
+ import { mx as mx3 } from "@dxos/react-ui-theme";
34
+
35
+ // src/components/Gameboard/Piece.tsx
36
+ import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
37
+ import { draggable } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
38
+ import { centerUnderPointer } from "@atlaskit/pragmatic-drag-and-drop/element/center-under-pointer";
39
+ import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
40
+ import React, { memo, useEffect, useRef, useState } from "react";
41
+ import { createPortal } from "react-dom";
42
+ import { invariant } from "@dxos/invariant";
39
43
  import { log } from "@dxos/log";
44
+ import { useDynamicRef, useTrackProps } from "@dxos/react-ui";
40
45
  import { mx } from "@dxos/react-ui-theme";
41
- var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/Gameboard/Gameboard.tsx";
42
- var GameboardRoot = ({ children, model, onDrop }) => {
46
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/components/Gameboard/Piece.tsx";
47
+ var Piece = /* @__PURE__ */ memo(({ classNames, Component, piece, orientation, bounds, label, onClick }) => {
43
48
  var _effect = _useSignals();
44
49
  try {
50
+ useTrackProps({
51
+ classNames,
52
+ Component,
53
+ piece,
54
+ orientation,
55
+ bounds,
56
+ label
57
+ }, Piece.displayName, false);
58
+ const { model, dragging: isDragging, promoting } = useGameboardContext(Piece.displayName);
59
+ const promotingRef = useDynamicRef(promoting);
45
60
  const [dragging, setDragging] = useState(false);
46
- const [promoting, setPromoting] = useState();
47
- const onPromotion = useCallback((move) => {
48
- log("onPromotion", {
49
- move
50
- }, {
61
+ const [preview, setPreview] = useState();
62
+ const [current, setCurrent] = useState({});
63
+ const ref = useRef(null);
64
+ useEffect(() => {
65
+ const el = ref.current;
66
+ invariant(el, void 0, {
51
67
  F: __dxlog_file,
52
- L: 29,
68
+ L: 42,
53
69
  S: void 0,
54
- C: (f, a) => f(...a)
70
+ A: [
71
+ "el",
72
+ ""
73
+ ]
55
74
  });
56
- setPromoting(void 0);
57
- onDrop?.(move);
58
- }, []);
59
- useEffect(() => {
60
- if (!model) {
61
- return;
62
- }
63
- return monitorForElements({
64
- onDragStart: ({ source }) => {
65
- log("onDragStart", {
66
- source
75
+ return draggable({
76
+ element: el,
77
+ getInitialData: () => ({
78
+ piece
79
+ }),
80
+ onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
81
+ log("onGenerateDragPreview", {
82
+ source: source.data
67
83
  }, {
68
84
  F: __dxlog_file,
69
- L: 42,
85
+ L: 48,
70
86
  S: void 0,
71
87
  C: (f, a) => f(...a)
72
88
  });
73
- setDragging(true);
74
- },
75
- onDrop: ({ source, location }) => {
76
- log("onDrop", {
77
- source,
78
- location
79
- }, {
80
- F: __dxlog_file,
81
- L: 46,
82
- S: void 0,
83
- C: (f, a) => f(...a)
89
+ setCustomNativeDragPreview({
90
+ getOffset: centerUnderPointer,
91
+ render: ({ container }) => {
92
+ setPreview(container);
93
+ const { width, height } = el.getBoundingClientRect();
94
+ container.style.width = width + "px";
95
+ container.style.height = height + "px";
96
+ return () => {
97
+ setPreview(void 0);
98
+ };
99
+ },
100
+ nativeSetDragImage
84
101
  });
85
- const target = location.current.dropTargets[0];
86
- if (!target) {
87
- return;
88
- }
89
- const targetLocation = target.data.location;
90
- const piece = source.data.piece;
91
- if (!isLocation(targetLocation) || !isPiece(piece)) {
92
- return;
93
- }
94
- const move = {
95
- from: piece.location,
96
- to: targetLocation,
97
- piece: piece.type
98
- };
99
- if (model.canPromote?.(move)) {
100
- setPromoting({
101
- ...piece,
102
- location: targetLocation
103
- });
104
- } else {
105
- onDrop?.(move);
102
+ },
103
+ canDrag: () => !promotingRef.current && model?.turn === piece.side,
104
+ onDragStart: () => setDragging(true),
105
+ onDrop: ({ location: { current: current2 } }) => {
106
+ try {
107
+ const location = current2.dropTargets[0]?.data.location;
108
+ if (isLocation(location)) {
109
+ setCurrent((current3) => ({
110
+ ...current3,
111
+ location
112
+ }));
113
+ }
114
+ } catch {
106
115
  }
107
116
  setDragging(false);
108
117
  }
109
118
  });
110
119
  }, [
111
- model
120
+ model,
121
+ piece
112
122
  ]);
113
- return /* @__PURE__ */ React.createElement(GameboardContext.Provider, {
114
- value: {
115
- model,
116
- dragging,
117
- promoting,
118
- onPromotion
119
- }
120
- }, children);
121
- } finally {
122
- _effect.f();
123
- }
124
- };
125
- GameboardRoot.displayName = "Gameboard.Root";
126
- var GameboardContent = /* @__PURE__ */ forwardRef(({ children, classNames, style }, forwardedRef) => {
127
- var _effect = _useSignals();
128
- try {
129
- return /* @__PURE__ */ React.createElement("div", {
130
- ref: forwardedRef,
131
- style,
132
- className: "flex w-full h-full justify-center overflow-hidden"
133
- }, /* @__PURE__ */ React.createElement("div", {
134
- className: mx("max-w-full max-h-full content-center aspect-square", classNames)
135
- }, children));
123
+ useEffect(() => {
124
+ requestAnimationFrame(() => {
125
+ if (!ref.current || !bounds) {
126
+ return;
127
+ }
128
+ if (!current.location || !isEqualLocation(current.location, piece.location)) {
129
+ ref.current.style.transition = "top 400ms ease-out, left 400ms ease-out";
130
+ ref.current.style.top = bounds.top + "px";
131
+ ref.current.style.left = bounds.left + "px";
132
+ setCurrent({
133
+ location: piece.location,
134
+ bounds
135
+ });
136
+ } else if (current.bounds !== bounds) {
137
+ ref.current.style.transition = "none";
138
+ ref.current.style.top = bounds.top + "px";
139
+ ref.current.style.left = bounds.left + "px";
140
+ setCurrent({
141
+ location: piece.location,
142
+ bounds
143
+ });
144
+ }
145
+ });
146
+ }, [
147
+ current,
148
+ piece.location,
149
+ bounds
150
+ ]);
151
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
152
+ ref,
153
+ style: {
154
+ width: bounds?.width,
155
+ height: bounds?.height
156
+ },
157
+ className: mx("absolute", classNames, dragging && "opacity-20", isDragging && "pointer-events-none"),
158
+ onClick
159
+ }, /* @__PURE__ */ React.createElement(Component, {
160
+ className: "grow"
161
+ }), label && /* @__PURE__ */ React.createElement("div", {
162
+ className: "absolute inset-1 text-xs text-black"
163
+ }, label)), preview && /* @__PURE__ */ createPortal(/* @__PURE__ */ React.createElement("div", {
164
+ className: mx(classNames)
165
+ }, /* @__PURE__ */ React.createElement(Component, {
166
+ className: "grow"
167
+ })), preview));
136
168
  } finally {
137
169
  _effect.f();
138
170
  }
139
171
  });
140
- var Gameboard = {
141
- Root: GameboardRoot,
142
- Content: GameboardContent
143
- };
172
+ Piece.displayName = "Piece";
144
173
 
145
- // src/Gameboard/Square.tsx
174
+ // src/components/Gameboard/Square.tsx
146
175
  import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
147
176
  import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
148
- import React2, { useRef, useState as useState2, useEffect as useEffect2, memo } from "react";
149
- import { invariant } from "@dxos/invariant";
177
+ import React2, { memo as memo2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
178
+ import { invariant as invariant2 } from "@dxos/invariant";
150
179
  import { log as log2 } from "@dxos/log";
151
180
  import { mx as mx2 } from "@dxos/react-ui-theme";
152
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/Gameboard/Square.tsx";
153
- var Square = /* @__PURE__ */ memo(({ location, bounds, label, classNames }) => {
181
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/components/Gameboard/Square.tsx";
182
+ var Square = /* @__PURE__ */ memo2(({ location, bounds, label, classNames }) => {
154
183
  var _effect = _useSignals2();
155
184
  try {
156
- const ref = useRef(null);
185
+ const ref = useRef2(null);
157
186
  const [state, setState] = useState2("idle");
158
- const { model } = useBoardContext();
187
+ const { model } = useGameboardContext(Square.displayName);
159
188
  useEffect2(() => {
160
189
  const el = ref.current;
161
- invariant(el, void 0, {
190
+ invariant2(el, void 0, {
162
191
  F: __dxlog_file2,
163
192
  L: 32,
164
193
  S: void 0,
@@ -226,155 +255,121 @@ var Square = /* @__PURE__ */ memo(({ location, bounds, label, classNames }) => {
226
255
  });
227
256
  Square.displayName = "Square";
228
257
 
229
- // src/Gameboard/Piece.tsx
230
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
231
- import { draggable } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
232
- import { centerUnderPointer } from "@atlaskit/pragmatic-drag-and-drop/element/center-under-pointer";
233
- import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
234
- import React3, { useState as useState3, useRef as useRef2, useEffect as useEffect3, memo as memo2 } from "react";
235
- import { createPortal } from "react-dom";
236
- import { invariant as invariant2 } from "@dxos/invariant";
237
- import { log as log3 } from "@dxos/log";
238
- import { useDynamicRef, useTrackProps } from "@dxos/react-ui";
239
- import { mx as mx3 } from "@dxos/react-ui-theme";
240
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/Gameboard/Piece.tsx";
241
- var Piece = /* @__PURE__ */ memo2(({ classNames, piece, orientation, bounds, label, Component }) => {
258
+ // src/components/Gameboard/Gameboard.tsx
259
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/components/Gameboard/Gameboard.tsx";
260
+ var [GameboardContextProvider, useRadixGameboardContext] = createContext("Gameboard");
261
+ var useGameboardContext = (consumerName) => {
262
+ return useRadixGameboardContext(consumerName);
263
+ };
264
+ var GameboardRoot = ({ children, model, onDrop }) => {
242
265
  var _effect = _useSignals3();
243
266
  try {
244
- useTrackProps({
245
- classNames,
246
- piece,
247
- orientation,
248
- bounds,
249
- label,
250
- Component
251
- }, Piece.displayName, false);
252
- const { model } = useBoardContext();
253
- const { dragging: isDragging, promoting } = useBoardContext();
254
- const promotingRef = useDynamicRef(promoting);
255
267
  const [dragging, setDragging] = useState3(false);
256
- const [preview, setPreview] = useState3();
257
- const [current, setCurrent] = useState3({});
258
- const ref = useRef2(null);
259
- useEffect3(() => {
260
- const el = ref.current;
261
- invariant2(el, void 0, {
268
+ const [promoting, setPromoting] = useState3();
269
+ const handlePromotion = useCallback((move) => {
270
+ log3("onPromotion", {
271
+ move
272
+ }, {
262
273
  F: __dxlog_file3,
263
- L: 44,
274
+ L: 47,
264
275
  S: void 0,
265
- A: [
266
- "el",
267
- ""
268
- ]
276
+ C: (f, a) => f(...a)
269
277
  });
270
- return draggable({
271
- element: el,
272
- getInitialData: () => ({
273
- piece
274
- }),
275
- onGenerateDragPreview: ({ nativeSetDragImage, location, source }) => {
276
- log3("onGenerateDragPreview", {
277
- source: source.data
278
+ setPromoting(void 0);
279
+ onDrop?.(move);
280
+ }, []);
281
+ useEffect3(() => {
282
+ if (!model) {
283
+ return;
284
+ }
285
+ return monitorForElements({
286
+ onDragStart: ({ source }) => {
287
+ log3("onDragStart", {
288
+ source
278
289
  }, {
279
290
  F: __dxlog_file3,
280
- L: 50,
291
+ L: 60,
281
292
  S: void 0,
282
293
  C: (f, a) => f(...a)
283
294
  });
284
- setCustomNativeDragPreview({
285
- getOffset: centerUnderPointer,
286
- // getOffset: preserveOffsetOnSource({
287
- // element: source.element,
288
- // input: location.current.input,
289
- // }),
290
- render: ({ container }) => {
291
- setPreview(container);
292
- const { width, height } = el.getBoundingClientRect();
293
- container.style.width = width + "px";
294
- container.style.height = height + "px";
295
- return () => {
296
- setPreview(void 0);
297
- };
298
- },
299
- nativeSetDragImage
300
- });
295
+ setDragging(true);
301
296
  },
302
- canDrag: () => !promotingRef.current && model?.turn === piece.side,
303
- onDragStart: () => setDragging(true),
304
- onDrop: ({ location: { current: current2 } }) => {
305
- const location = current2.dropTargets[0].data.location;
306
- if (isLocation(location)) {
307
- setCurrent((current3) => ({
308
- ...current3,
309
- location
310
- }));
297
+ onDrop: ({ source, location }) => {
298
+ log3("onDrop", {
299
+ source,
300
+ location
301
+ }, {
302
+ F: __dxlog_file3,
303
+ L: 64,
304
+ S: void 0,
305
+ C: (f, a) => f(...a)
306
+ });
307
+ const target = location.current.dropTargets[0];
308
+ if (!target) {
309
+ return;
310
+ }
311
+ const targetLocation = target.data.location;
312
+ const piece = source.data.piece;
313
+ if (!isLocation(targetLocation) || !isPiece(piece)) {
314
+ return;
315
+ }
316
+ const move = {
317
+ from: piece.location,
318
+ to: targetLocation,
319
+ piece: piece.type
320
+ };
321
+ if (model.isValidMove(move)) {
322
+ if (model.canPromote?.(move)) {
323
+ setPromoting({
324
+ ...piece,
325
+ location: targetLocation
326
+ });
327
+ } else {
328
+ onDrop?.(move);
329
+ }
311
330
  }
312
331
  setDragging(false);
313
332
  }
314
333
  });
315
334
  }, [
316
- model,
317
- piece
318
- ]);
319
- useEffect3(() => {
320
- requestAnimationFrame(() => {
321
- if (!ref.current || !bounds) {
322
- return;
323
- }
324
- if (!current.location || !isEqualLocation(current.location, piece.location)) {
325
- ref.current.style.transition = "top 400ms ease-out, left 400ms ease-out";
326
- ref.current.style.top = bounds.top + "px";
327
- ref.current.style.left = bounds.left + "px";
328
- setCurrent({
329
- location: piece.location,
330
- bounds
331
- });
332
- } else if (current.bounds !== bounds) {
333
- ref.current.style.transition = "none";
334
- ref.current.style.top = bounds.top + "px";
335
- ref.current.style.left = bounds.left + "px";
336
- setCurrent({
337
- location: piece.location,
338
- bounds
339
- });
340
- }
341
- });
342
- }, [
343
- current,
344
- piece.location,
345
- bounds
335
+ model
346
336
  ]);
347
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", {
348
- ref,
349
- style: {
350
- width: bounds?.width,
351
- height: bounds?.height
352
- },
353
- className: mx3(
354
- "absolute",
355
- classNames,
356
- // orientation === 'black' && '_rotate-180',
357
- dragging && "opacity-20",
358
- isDragging && "pointer-events-none"
359
- )
360
- }, /* @__PURE__ */ React3.createElement(Component, {
361
- className: "grow"
362
- }), label && /* @__PURE__ */ React3.createElement("div", {
363
- className: "absolute inset-1 text-xs text-black"
364
- }, label)), preview && /* @__PURE__ */ createPortal(/* @__PURE__ */ React3.createElement("div", {
365
- className: mx3(classNames)
366
- }, /* @__PURE__ */ React3.createElement(Component, {
367
- className: "grow"
368
- })), preview));
337
+ return /* @__PURE__ */ React3.createElement(GameboardContextProvider, {
338
+ model,
339
+ dragging,
340
+ promoting,
341
+ onPromotion: handlePromotion
342
+ }, children);
343
+ } finally {
344
+ _effect.f();
345
+ }
346
+ };
347
+ GameboardRoot.displayName = "Gameboard.Root";
348
+ var GameboardContent = /* @__PURE__ */ forwardRef(({ children, classNames, grow, contain }, forwardedRef) => {
349
+ var _effect = _useSignals3();
350
+ try {
351
+ return /* @__PURE__ */ React3.createElement("div", {
352
+ role: "none",
353
+ className: mx3(grow && "grid is-full bs-full size-container place-content-center", classNames),
354
+ ref: forwardedRef
355
+ }, contain ? /* @__PURE__ */ React3.createElement("div", {
356
+ className: "is-[min(100cqw,100cqh)] bs-[min(100cqw,100cqh)]"
357
+ }, children) : children);
369
358
  } finally {
370
359
  _effect.f();
371
360
  }
372
361
  });
373
- Piece.displayName = "Piece";
362
+ GameboardContent.displayName = "Gameboard.Content";
363
+ var Gameboard = {
364
+ Root: GameboardRoot,
365
+ Content: GameboardContent,
366
+ Piece,
367
+ Square
368
+ };
374
369
 
375
- // src/Chessboard/chess.ts
370
+ // src/components/Chessboard/chess.ts
376
371
  import { signal } from "@preact/signals-core";
377
- import { Chess, validateFen } from "chess.js";
372
+ import { Chess as ChessJS } from "chess.js";
378
373
  import { log as log4 } from "@dxos/log";
379
374
 
380
375
  // src/gen/pieces/chess/alpha/index.ts
@@ -667,8 +662,8 @@ var SvgWR = (props) => {
667
662
  };
668
663
  var wR_default = SvgWR;
669
664
 
670
- // src/Chessboard/chess.ts
671
- var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/Chessboard/chess.ts";
665
+ // src/components/Chessboard/chess.ts
666
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-gameboard/src/components/Chessboard/chess.ts";
672
667
  var ChessPieces = alpha_exports;
673
668
  var posToLocation = (pos) => {
674
669
  const col = pos.charCodeAt(0) - "a".charCodeAt(0);
@@ -707,55 +702,88 @@ var boardStyles = styles.original;
707
702
  var getSquareColor = ([row, col]) => {
708
703
  return (col + row) % 2 === 0 ? boardStyles.black : boardStyles.white;
709
704
  };
710
- var makeMove = (game, move) => {
705
+ var createChess = (pgn) => {
706
+ const chess = new ChessJS();
707
+ if (pgn) {
708
+ try {
709
+ chess.loadPgn(pgn);
710
+ } catch {
711
+ log4.warn(pgn, void 0, {
712
+ F: __dxlog_file4,
713
+ L: 71,
714
+ S: void 0,
715
+ C: (f, a) => f(...a)
716
+ });
717
+ }
718
+ }
719
+ return chess;
720
+ };
721
+ var tryMove = (chess, move) => {
711
722
  const from = locationToPos(move.from);
712
723
  const to = locationToPos(move.to);
713
724
  try {
714
- log4("makeMove", {
715
- move
716
- }, {
717
- F: __dxlog_file4,
718
- L: 72,
719
- S: void 0,
720
- C: (f, a) => f(...a)
721
- });
722
725
  const promotion = move.promotion ? move.promotion[1].toLowerCase() : "q";
723
- game.move({
726
+ chess.move({
724
727
  from,
725
728
  to,
726
729
  promotion
727
730
  }, {
728
731
  strict: false
729
732
  });
730
- return game;
731
- } catch (err) {
733
+ return chess;
734
+ } catch {
732
735
  return null;
733
736
  }
734
737
  };
735
738
  var ChessModel = class {
736
- constructor(fen) {
737
- this._pieces = signal({});
738
- this.initialize(fen);
739
+ _chess = new ChessJS();
740
+ _pieces = signal({});
741
+ constructor(pgn) {
742
+ this.update(pgn);
739
743
  }
740
744
  get turn() {
741
- return this._game.turn() === "w" ? "white" : "black";
745
+ return this._chess.turn() === "w" ? "white" : "black";
742
746
  }
743
747
  get pieces() {
744
748
  return this._pieces;
745
749
  }
746
750
  get game() {
747
- return this._game;
751
+ return this._chess;
752
+ }
753
+ /**
754
+ * PGN with headers.
755
+ *
756
+ * [Event "?"]
757
+ * [Site "?"]
758
+ * [Date "2025.08.05"]
759
+ * [Round "?"]
760
+ * [White "?"]
761
+ * [Black "?"]
762
+ * [Result "*"]
763
+ */
764
+ // TODO(burdon): Update headers.
765
+ get pgn() {
766
+ return this._chess.pgn();
748
767
  }
749
768
  get fen() {
750
- return this._game.fen();
769
+ return this._chess.fen();
751
770
  }
752
- initialize(fen) {
753
- this._pieces.value = {};
754
- this._game = new Chess(fen ? validateFen(fen).ok ? fen : void 0 : void 0);
771
+ update(pgn = "") {
772
+ const previous = this._chess.history();
773
+ try {
774
+ this._chess.loadPgn(pgn);
775
+ this._chess.setHeader("Date", createDate());
776
+ this._chess.setHeader("Site", "dxos.org");
777
+ } catch {
778
+ }
779
+ const current = this._chess.history();
780
+ if (!isValidNextMove(previous, current)) {
781
+ this._pieces.value = {};
782
+ }
755
783
  this._update();
756
784
  }
757
785
  isValidMove(move) {
758
- return makeMove(new Chess(this._game.fen()), move) !== null;
786
+ return tryMove(new ChessJS(this._chess.fen()), move) !== null;
759
787
  }
760
788
  canPromote(move) {
761
789
  const isPawnMove = move.piece === "BP" || move.piece === "WP";
@@ -763,21 +791,20 @@ var ChessModel = class {
763
791
  return isPawnMove && isToLastRank;
764
792
  }
765
793
  makeMove(move) {
766
- const game = makeMove(this._game, move);
794
+ const game = tryMove(this._chess, move);
767
795
  if (!game) {
768
796
  return false;
769
797
  }
770
- this._game = game;
771
798
  this._update();
772
799
  return true;
773
800
  }
774
801
  makeRandomMove() {
775
- const moves = this._game.moves();
802
+ const moves = this._chess.moves();
776
803
  if (!moves.length) {
777
804
  return false;
778
805
  }
779
806
  const move = moves[Math.floor(Math.random() * moves.length)];
780
- this._game.move(move);
807
+ this._chess.move(move);
781
808
  this._update();
782
809
  return true;
783
810
  }
@@ -786,7 +813,7 @@ var ChessModel = class {
786
813
  */
787
814
  _update() {
788
815
  const pieces = {};
789
- this._game.board().flatMap((row) => row.forEach((record) => {
816
+ this._chess.board().flatMap((row) => row.forEach((record) => {
790
817
  if (!record) {
791
818
  return;
792
819
  }
@@ -803,6 +830,17 @@ var ChessModel = class {
803
830
  this._pieces.value = mapPieces(this._pieces.value, pieces);
804
831
  }
805
832
  };
833
+ var isValidNextMove = (previous, current) => {
834
+ if (current.length > previous.length + 1) {
835
+ return false;
836
+ }
837
+ for (let i = 0; i < previous.length; i++) {
838
+ if (previous[i] !== current[i]) {
839
+ return false;
840
+ }
841
+ }
842
+ return true;
843
+ };
806
844
  var mapPieces = (before, after) => {
807
845
  const difference = {
808
846
  removed: {},
@@ -833,16 +871,17 @@ var mapPieces = (before, after) => {
833
871
  added: Object.keys(difference.added).length
834
872
  }, {
835
873
  F: __dxlog_file4,
836
- L: 205,
874
+ L: 263,
837
875
  S: void 0,
838
876
  C: (f, a) => f(...a)
839
877
  });
840
878
  return after;
841
879
  };
880
+ var createDate = (date = /* @__PURE__ */ new Date()) => date.toISOString().slice(0, 10).replace(/-/g, ".");
842
881
 
843
- // src/Chessboard/Chessboard.tsx
882
+ // src/components/Chessboard/Chessboard.tsx
844
883
  import { useSignals as _useSignals16 } from "@preact-signals/safe-react/tracking";
845
- import React16, { useRef as useRef3, useMemo, useEffect as useEffect4, useState as useState4, memo as memo3 } from "react";
884
+ import React16, { memo as memo3, useEffect as useEffect4, useMemo, useRef as useRef3, useState as useState4 } from "react";
846
885
  import { useResizeDetector } from "react-resize-detector";
847
886
  import { useTrackProps as useTrackProps2 } from "@dxos/react-ui";
848
887
  import { mx as mx4 } from "@dxos/react-ui-theme";
@@ -858,7 +897,7 @@ var Chessboard = /* @__PURE__ */ memo3(({ orientation, showLabels, debug, rows =
858
897
  const { ref: containerRef, width, height } = useResizeDetector({
859
898
  refreshRate: 200
860
899
  });
861
- const { model, promoting, onPromotion } = useBoardContext();
900
+ const { model, promoting, onPromotion } = useGameboardContext(Chessboard.displayName);
862
901
  const locations = useMemo(() => {
863
902
  return Array.from({
864
903
  length: rows
@@ -924,7 +963,7 @@ var Chessboard = /* @__PURE__ */ memo3(({ orientation, showLabels, debug, rows =
924
963
  }, /* @__PURE__ */ React16.createElement("div", {
925
964
  ref: gridRef,
926
965
  className: "grid grid-rows-8 grid-cols-8 aspect-square select-none"
927
- }, layout), /* @__PURE__ */ React16.createElement("div", null, locations.map((location) => /* @__PURE__ */ React16.createElement(Square, {
966
+ }, layout), /* @__PURE__ */ React16.createElement("div", null, locations.map((location) => /* @__PURE__ */ React16.createElement(Gameboard.Square, {
928
967
  key: locationToString(location),
929
968
  location,
930
969
  label: showLabels ? locationToPos(location) : void 0,
@@ -932,14 +971,14 @@ var Chessboard = /* @__PURE__ */ memo3(({ orientation, showLabels, debug, rows =
932
971
  classNames: getSquareColor(location)
933
972
  }))), /* @__PURE__ */ React16.createElement("div", {
934
973
  className: mx4(promoting && "opacity-50")
935
- }, positions.map(({ bounds, piece }) => /* @__PURE__ */ React16.createElement(Piece, {
974
+ }, positions.map(({ bounds, piece }) => /* @__PURE__ */ React16.createElement(Gameboard.Piece, {
936
975
  key: piece.id,
937
976
  piece,
938
977
  bounds,
939
978
  label: debug ? piece.id : void 0,
940
979
  orientation,
941
980
  Component: ChessPieces[piece.type]
942
- }))), /* @__PURE__ */ React16.createElement("div", null, promoting && /* @__PURE__ */ React16.createElement(PromotionSelector, {
981
+ }))), promoting && /* @__PURE__ */ React16.createElement(PromotionSelector, {
943
982
  grid,
944
983
  piece: promoting,
945
984
  onSelect: (piece) => {
@@ -950,7 +989,7 @@ var Chessboard = /* @__PURE__ */ memo3(({ orientation, showLabels, debug, rows =
950
989
  promotion: piece.type
951
990
  });
952
991
  }
953
- })));
992
+ }));
954
993
  } finally {
955
994
  _effect.f();
956
995
  }
@@ -988,16 +1027,14 @@ var PromotionSelector = ({ grid, piece, onSelect }) => {
988
1027
  type: selected.type
989
1028
  });
990
1029
  };
991
- return /* @__PURE__ */ React16.createElement("div", null, positions.map(({ piece: piece2, bounds }) => /* @__PURE__ */ React16.createElement("div", {
1030
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, positions.map(({ piece: piece2, bounds }) => /* @__PURE__ */ React16.createElement(Gameboard.Piece, {
992
1031
  key: piece2.id,
993
- style: bounds,
994
- onClick: () => handleSelect(piece2)
995
- }, /* @__PURE__ */ React16.createElement(Piece, {
996
1032
  piece: piece2,
997
1033
  bounds,
998
1034
  Component: ChessPieces[piece2.type],
999
- classNames: mx4("border-2 border-neutral-700 rounded-full", boardStyles.promotion)
1000
- }))));
1035
+ classNames: mx4("border-2 border-neutral-700 rounded-full", boardStyles.promotion),
1036
+ onClick: () => handleSelect(piece2)
1037
+ })));
1001
1038
  } finally {
1002
1039
  _effect.f();
1003
1040
  }
@@ -1007,10 +1044,8 @@ export {
1007
1044
  ChessPieces,
1008
1045
  Chessboard,
1009
1046
  Gameboard,
1010
- GameboardContext,
1011
- Piece,
1012
- Square,
1013
1047
  boardStyles,
1048
+ createChess,
1014
1049
  getRelativeBounds,
1015
1050
  getSquareColor,
1016
1051
  isEqualLocation,
@@ -1021,6 +1056,6 @@ export {
1021
1056
  mapPieces,
1022
1057
  posToLocation,
1023
1058
  stringToLocation,
1024
- useBoardContext
1059
+ useGameboardContext
1025
1060
  };
1026
1061
  //# sourceMappingURL=index.mjs.map