@mirasen/chessboard 1.0.2 → 1.0.4

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @mirasen/chessboard
2
2
 
3
+ ## 1.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - f586eea: Update README integration examples and export move-related public types.
8
+
9
+ ## 1.0.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 09962d6: Prevent native mobile selection and callout behavior on the board surface and coordinate labels.
14
+
3
15
  ## 1.0.2
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -24,7 +24,7 @@ This is not just a board renderer. It is a chessboard platform designed for real
24
24
 
25
25
  ## What you get out of the box
26
26
 
27
- The built-in first-party extension baseline currently includes both foundational runtime-facing extensions and user-facing board features.
27
+ The default first-party extension baseline includes rendering, events, selection, active target feedback, legal move hints, last move feedback, promotion UI, and optional auto-promotion.
28
28
 
29
29
  - `renderer` — the first-party rendering extension that validates the same extension architecture used for board features
30
30
  - `events`
@@ -78,44 +78,48 @@ if (!element) {
78
78
  const board = createBoard({
79
79
  element
80
80
  });
81
- // Set free movability, default - disabled
81
+
82
+ // User moves are disabled by default.
83
+ // Enable free movement when you want unconstrained UI moves.
82
84
  board.setMovability({
83
85
  mode: 'free'
84
86
  });
85
87
  ```
86
88
 
87
- ### Explicit extensions
88
-
89
- ```ts
90
- import { createBoard, type Chessboard, type ChessboardExtensionInput } from '@mirasen/chessboard';
91
-
92
- const extensionList = [
93
- // Always add renderer first, otherwise nothing will be rendered
94
- 'renderer',
95
- 'lastMove',
96
- // Keep autoPromote before promotion, otherwise promotion may defer the move first
97
- 'autoPromote',
98
- 'promotion'
99
- // customExtensionDefinition
100
- ] as const satisfies readonly ChessboardExtensionInput[];
101
- let board: Chessboard<typeof extensionList> | null = null;
102
-
103
- const element = document.getElementById('board');
104
-
105
- if (!element) {
106
- throw new Error('Missing board element');
107
- }
89
+ ### Connect to a game/rules layer
108
90
 
109
- board = createBoard({
110
- element,
111
- extensions: extensionList
112
- });
113
- ```
91
+ `@mirasen/chessboard` handles board interaction and move intent. Your application can keep chess rules and game state in its own game/rules layer.
114
92
 
115
- ### setOnUIMove
93
+ A typical integration uses a small adapter between your game move format and the board move format. The `game` object below represents your own chess rules layer, such as chess.js, a server-backed game model, or a custom engine.
116
94
 
117
95
  ```ts
118
- import { createBoard } from '@mirasen/chessboard';
96
+ import {
97
+ createBoard,
98
+ type MoveDestinationInput,
99
+ type MoveOutput,
100
+ type MoveRequestInput
101
+ } from '@mirasen/chessboard';
102
+
103
+ type GameMove = {
104
+ from: string;
105
+ to: string;
106
+ capturedSquare?: string;
107
+ secondary?: {
108
+ from: string;
109
+ to: string;
110
+ };
111
+ promotedTo?: 'queen' | 'rook' | 'bishop' | 'knight';
112
+ };
113
+
114
+ type GameDestination = {
115
+ to: string;
116
+ capturedSquare?: string;
117
+ secondary?: {
118
+ from: string;
119
+ to: string;
120
+ };
121
+ promotedTo?: ('queen' | 'rook' | 'bishop' | 'knight')[];
122
+ };
119
123
 
120
124
  const element = document.getElementById('board');
121
125
 
@@ -123,95 +127,72 @@ if (!element) {
123
127
  throw new Error('Missing board element');
124
128
  }
125
129
 
130
+ const game = createYourGameEngine(); // Your app/game layer.
126
131
  const board = createBoard({
127
- element,
128
- state: {
129
- board: 'start',
130
- interaction: {
131
- movability: {
132
- mode: 'free'
133
- }
134
- }
135
- }
136
- });
137
-
138
- board.extensions.events.setOnUIMove((move) => {
139
- console.log('UI move:', move);
132
+ element
140
133
  });
141
- ```
142
-
143
- ### setMovability - strict with explicit destinations
144
134
 
145
- ```ts
146
- import { createBoard } from '@mirasen/chessboard';
135
+ board.setPosition(game.getPosition());
147
136
 
148
- const element = document.getElementById('board');
137
+ function toBoardDestination(gameDestination: GameDestination): MoveDestinationInput {
138
+ return {
139
+ to: gameDestination.to,
140
+ capturedSquare: gameDestination.capturedSquare,
141
+ secondary: gameDestination.secondary, // e.g. castling.
142
+ promotedTo: gameDestination.promotedTo
143
+ };
144
+ }
149
145
 
150
- if (!element) {
151
- throw new Error('Missing board element');
146
+ function toBoardMoveRequest(gameMove: GameMove): MoveRequestInput {
147
+ return {
148
+ from: gameMove.from,
149
+ to: gameMove.to,
150
+ capturedSquare: gameMove.capturedSquare,
151
+ secondary: gameMove.secondary,
152
+ promotedTo: gameMove.promotedTo
153
+ };
152
154
  }
153
155
 
154
- const board = createBoard({
155
- element
156
- });
156
+ function toGameMove(move: MoveOutput): GameMove {
157
+ return {
158
+ from: move.from,
159
+ to: move.to,
160
+ capturedSquare: move.capturedSquare,
161
+ secondary: move.secondary,
162
+ promotedTo: move.promotedTo
163
+ };
164
+ }
157
165
 
158
166
  board.setMovability({
159
167
  mode: 'strict',
160
- destinations: {
161
- e2: [{ to: 'e3' }, { to: 'e4' }],
162
- d7: [
163
- { to: 'c8', promotedTo: ['queen', 'rook', 'bishop', 'knight'] },
164
- { to: 'd8', promotedTo: ['queen', 'rook', 'bishop', 'knight'] }
165
- ],
166
- g1: [{ to: 'f3' }, { to: 'h3' }]
168
+ destinations: (source): MoveDestinationInput[] | undefined => {
169
+ const gameDestinations = game.getDestinationsFor(source);
170
+
171
+ return gameDestinations.length > 0 ? gameDestinations.map(toBoardDestination) : undefined;
167
172
  }
168
173
  });
169
- ```
170
-
171
- ### setMovability - strict with a resolver
172
174
 
173
- ```ts
174
- import { createBoard } from '@mirasen/chessboard';
175
+ board.extensions.events.setOnUIMove((move) => {
176
+ // The board accepted and applied this UI move.
177
+ // Keep your game/rules layer in sync.
178
+ game.move(toGameMove(move));
179
+ });
175
180
 
176
- const element = document.getElementById('board');
181
+ function applyExternalMove(gameMove: GameMove) {
182
+ // Apply moves that come from outside the board UI:
183
+ // server, engine, opponent, imported game, replay, etc.
184
+ game.move(gameMove);
177
185
 
178
- if (!element) {
179
- throw new Error('Missing board element');
186
+ // Then animate/apply the same move on the board.
187
+ board.move(toBoardMoveRequest(gameMove));
180
188
  }
189
+ ```
181
190
 
182
- const board = createBoard({
183
- element
184
- });
191
+ `MoveDestinationInput` can describe more than a target square. For example, `secondary` can represent paired piece movement (e.g. castling), `capturedSquare` can represent captures away from the destination square (e.g. en passant), and `promotedTo` can represent promotion choices.
185
192
 
186
- board.setMovability({
187
- mode: 'strict',
188
- destinations: (source) => {
189
- if (source === 'e2')
190
- return [
191
- { to: 'e8', promotedTo: ['bishop', 'queen'] },
192
- { to: 'd8', promotedTo: ['rook', 'knight', 'queen'] }
193
- ];
194
- if (source === 'f7')
195
- return [
196
- { to: 'f8', promotedTo: ['bishop', 'rook', 'knight', 'queen'] },
197
- { to: 'g8', promotedTo: ['bishop', 'rook', 'knight', 'queen'] }
198
- ];
199
- if (source === 'd7')
200
- return [
201
- { to: 'd1', promotedTo: ['bishop', 'knight'] },
202
- { to: 'e1', promotedTo: ['rook', 'knight', 'queen'] }
203
- ];
204
- if (source === 'c2')
205
- return [
206
- { to: 'c1', promotedTo: ['bishop', 'rook', 'knight', 'queen'] },
207
- { to: 'b1', promotedTo: ['bishop', 'rook', 'knight', 'queen'] }
208
- ];
209
- return undefined;
210
- }
211
- });
212
- ```
193
+ This lets your game/rules layer own chess legality and game state while the board owns interaction, selection, targeting, visual feedback, promotion UI, animation, and its own displayed position.
213
194
 
214
- ## Promotion and auto-promotion
195
+ ### Promotion and auto-promotion
215
196
 
216
197
  Promotion is handled inside the board interaction pipeline through a natural deferred move flow.
217
198
 
@@ -249,6 +230,44 @@ const board = createBoard({
249
230
  board.extensions.autoPromote.toQueen = true;
250
231
  ```
251
232
 
233
+ ### Movability modes
234
+
235
+ Movability controls how the board accepts user-initiated moves.
236
+
237
+ - `disabled` — user moves are ignored
238
+ - `free` — user moves are accepted without legal destination filtering
239
+ - `strict` — user moves are accepted only when the target is allowed by the destinations provided by your app/rules layer
240
+
241
+ ### Advanced: explicit extension lists
242
+
243
+ Most users can rely on the default first-party extension baseline. Use an explicit extension list only when you want to control which built-in or custom extensions are installed.
244
+
245
+ ```ts
246
+ import { createBoard, type Chessboard, type ChessboardExtensionInput } from '@mirasen/chessboard';
247
+
248
+ const extensionList = [
249
+ // Always add renderer first, otherwise nothing will be rendered
250
+ 'renderer',
251
+ 'lastMove',
252
+ // Keep autoPromote before promotion, otherwise promotion may defer the move first
253
+ 'autoPromote',
254
+ 'promotion'
255
+ // customExtensionDefinition
256
+ ] as const satisfies readonly ChessboardExtensionInput[];
257
+ let board: Chessboard<typeof extensionList> | null = null;
258
+
259
+ const element = document.getElementById('board');
260
+
261
+ if (!element) {
262
+ throw new Error('Missing board element');
263
+ }
264
+
265
+ board = createBoard({
266
+ element,
267
+ extensions: extensionList
268
+ });
269
+ ```
270
+
252
271
  ## Architecture
253
272
 
254
273
  `@mirasen/chessboard` is built around explicit boundaries:
@@ -56,5 +56,10 @@ export function rendererCoordinatesRender(state, context, layer) {
56
56
  'dominant-baseline': 'auto'
57
57
  });
58
58
  text.textContent = label;
59
+ // Keep coordinate labels visual-only so mobile browsers do not select them
60
+ // or treat them as pointer targets during board interaction.
61
+ text.style.setProperty('pointer-events', 'none');
62
+ text.style.setProperty('user-select', 'none');
63
+ text.style.setProperty('-webkit-user-select', 'none');
59
64
  }
60
65
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export type { MoveOutput } from './extensions/build/index.js';
2
+ export type { MoveRequestInput, PiecePositionRecordString } from './state/board/types/input.js';
3
+ export type { MoveDestinationInput } from './state/interaction/types/input.js';
1
4
  export { createBoard } from './wrapper/factory.js';
2
5
  export type { Chessboard, ChessboardExtensionInput, ChessboardInitOptions } from './wrapper/types.js';
3
- export type { MoveRequestInput, PiecePositionRecordString } from './state/board/types/input.js';
@@ -2,6 +2,11 @@ import { createSvgElement } from './helpers.js';
2
2
  export function createSvgRoots(options) {
3
3
  const { doc } = options;
4
4
  const svgRoot = createSvgElement(doc, 'svg', { 'data-chessboard-id': 'svg-root' });
5
+ svgRoot.style.setProperty('user-select', 'none');
6
+ svgRoot.style.setProperty('-webkit-user-select', 'none');
7
+ svgRoot.style.setProperty('touch-action', 'pinch-zoom');
8
+ svgRoot.style.setProperty('-webkit-touch-callout', 'none');
9
+ svgRoot.style.setProperty('-webkit-tap-highlight-color', 'transparent');
5
10
  const result = {
6
11
  svgRoot,
7
12
  defs: createSvgElement(doc, 'defs', { 'data-chessboard-id': 'defs-root' }),
@@ -29,10 +29,6 @@ export function createInteractionController(options) {
29
29
  break;
30
30
  }
31
31
  transmitTransientInput(internalState, context);
32
- if (!context.rawEvent.defaultPrevented && context.rawEvent.cancelable) {
33
- // Suppress native browser behavior for board-handled events.
34
- context.rawEvent.preventDefault();
35
- }
36
32
  }
37
33
  };
38
34
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mirasen/chessboard",
3
3
  "description": "Framework-agnostic TypeScript chessboard platform with real chess interaction built in.",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Mirasen",