@dxos/react-ui-gameboard 0.8.4-main.b97322e → 0.8.4-main.f5c0578

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