@mirasen/chessboard 1.0.0-rc.0 → 1.0.1

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 (27) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +54 -25
  3. package/dist/extensions/build/index.d.ts +21 -0
  4. package/dist/extensions/build/index.js +9 -0
  5. package/dist/extensions/first-party/active-target/factory.js +4 -4
  6. package/dist/extensions/first-party/active-target/types.d.ts +2 -2
  7. package/dist/extensions/first-party/active-target/types.js +4 -1
  8. package/dist/extensions/first-party/auto-promote/types.d.ts +0 -2
  9. package/dist/extensions/first-party/board-events/types.d.ts +0 -2
  10. package/dist/extensions/first-party/common/helpers.d.ts +7 -7
  11. package/dist/extensions/first-party/common/helpers.js +11 -11
  12. package/dist/extensions/first-party/common/types.d.ts +2 -3
  13. package/dist/extensions/first-party/last-move/factory.js +4 -4
  14. package/dist/extensions/first-party/last-move/types.d.ts +2 -2
  15. package/dist/extensions/first-party/legal-moves/factory.js +4 -4
  16. package/dist/extensions/first-party/legal-moves/types.d.ts +2 -2
  17. package/dist/extensions/first-party/main-renderer/factory.js +4 -4
  18. package/dist/extensions/first-party/main-renderer/types/instance.d.ts +2 -2
  19. package/dist/extensions/first-party/promotion/factory.js +4 -4
  20. package/dist/extensions/first-party/promotion/types/main.d.ts +2 -2
  21. package/dist/extensions/first-party/selected-square/factory.js +4 -4
  22. package/dist/extensions/first-party/selected-square/types.d.ts +2 -2
  23. package/dist/extensions/index.d.ts +1 -8
  24. package/dist/extensions/index.js +1 -6
  25. package/dist/index.d.ts +1 -2
  26. package/dist/runtime/input/controller/factory.js +5 -0
  27. package/package.json +9 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @mirasen/chessboard
2
2
 
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - be927e3: Call `preventDefault()` for board-handled input events to suppress native browser behavior.
8
+
9
+ ## 1.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - 2609920: First v1.0.0 release with the new extension-driven architecture, built-in chess interaction baseline, promotion flow, and auto-promote support.
14
+
15
+ ### Patch Changes
16
+
17
+ - 31364b7: dependabot: directory '/', update @sveltejs/kit
18
+ - 31364b7: dependabot: directory '/', update @vitest/browser-playwright
19
+ - 31364b7: dependabot: directory '/', update eslint-plugin-svelte
20
+ - 31364b7: dependabot: directory '/', update svelte
21
+ - 2609920: Added promotion and auto-promotion to queen extensions
22
+
3
23
  ## 1.0.0-rc.0
4
24
 
5
25
  ### Major Changes
package/README.md CHANGED
@@ -55,6 +55,15 @@ That means the board already covers a meaningful baseline chess UX instead of st
55
55
  npm install @mirasen/chessboard
56
56
  ```
57
57
 
58
+ ## Live examples
59
+
60
+ Interactive examples are available on the Mirasen website:
61
+
62
+ - [Examples overview](https://mirasen.io/chessboard/examples/)
63
+ - [Minimal interactive example](https://mirasen.io/chessboard/examples/minimal.html)
64
+ - [Legal moves example](https://mirasen.io/chessboard/examples/legal-moves.html)
65
+ - [Promotion example](https://mirasen.io/chessboard/examples/promotion.html)
66
+
58
67
  ## Usage
59
68
 
60
69
  ### Minimal example
@@ -77,10 +86,21 @@ board.setMovability({
77
86
  });
78
87
  ```
79
88
 
80
- ### Explicit built-in interaction extensions
89
+ ### Explicit extensions
81
90
 
82
91
  ```ts
83
- import { createBoard } from '@mirasen/chessboard';
92
+ import { createBoard, type Chessboard, type ChessboardExtensionInput } from '@mirasen/chessboard';
93
+
94
+ const extensionList = [
95
+ // Always add renderer first, otherwise nothing will be rendered
96
+ 'renderer',
97
+ 'lastMove',
98
+ // Keep autoPromote before promotion, otherwise promotion may defer the move first
99
+ 'autoPromote',
100
+ 'promotion'
101
+ // customExtensionDefinition
102
+ ] as const satisfies readonly ChessboardExtensionInput[];
103
+ let board: Chessboard<typeof extensionList> | null = null;
84
104
 
85
105
  const element = document.getElementById('board');
86
106
 
@@ -88,20 +108,9 @@ if (!element) {
88
108
  throw new Error('Missing board element');
89
109
  }
90
110
 
91
- const board = createBoard({
111
+ board = createBoard({
92
112
  element,
93
- extensions: [
94
- // Always add renderer first, otherwise nothing will be rendered
95
- 'renderer',
96
- 'selectedSquare',
97
- 'activeTarget',
98
- 'legalMoves',
99
- 'lastMove',
100
- 'events',
101
- // Keep autoPromote before promotion, otherwise promotion may defer the move first
102
- 'autoPromote',
103
- 'promotion'
104
- ]
113
+ extensions: extensionList
105
114
  });
106
115
  ```
107
116
 
@@ -226,6 +235,22 @@ These are separate extensions on purpose:
226
235
 
227
236
  This separation is a good example of the architecture: common chess UX can be built in without collapsing everything into one hardcoded board layer.
228
237
 
238
+ ```ts
239
+ import { createBoard } from '@mirasen/chessboard';
240
+
241
+ const element = document.getElementById('board');
242
+
243
+ if (!element) {
244
+ throw new Error('Missing board element');
245
+ }
246
+
247
+ const board = createBoard({
248
+ element
249
+ });
250
+
251
+ board.extensions.autoPromote.toQueen = true;
252
+ ```
253
+
229
254
  ## Architecture
230
255
 
231
256
  `@mirasen/chessboard` is built around explicit boundaries:
@@ -255,19 +280,23 @@ A board that handles everything internally without clean boundaries becomes diff
255
280
  - clean internal architecture
256
281
  - extension-driven feature growth
257
282
 
258
- ## Status
283
+ ## Project direction
259
284
 
260
- Release candidate for version `1.0.0`, focused on a strong built-in interaction baseline and extension-driven architecture.
285
+ This platform is being built as a foundation for richer chess learning and interaction workflows, but the board itself is designed to stand as a serious open-source UI platform in its own right.
261
286
 
262
- ## Roadmap after RC
287
+ ## Artwork
263
288
 
264
- After the first release candidate, the next steps are expected to include:
289
+ ### Chessnut piece set
265
290
 
266
- - remaining test hardening
267
- - documentation expansion
268
- - more examples
269
- - further polish toward final `1.0.0`
291
+ This project uses the Chessnut chess piece set.
270
292
 
271
- ## Project direction
293
+ This is the current default piece set used by the library.
272
294
 
273
- This platform is being built as a foundation for richer chess learning and interaction workflows, but the board itself is designed to stand as a serious open-source UI platform in its own right.
295
+ - Author: Alexis Luengas — https://github.com/LexLuengas
296
+ - Source: https://github.com/LexLuengas/chessnut-pieces
297
+ - License: Apache License 2.0 — https://github.com/LexLuengas/chessnut-pieces/blob/master/LICENSE.txt
298
+
299
+ For details, see:
300
+
301
+ - [./assets/pieces/chessnut/ATTRIBUTION.md](./assets/pieces/chessnut/ATTRIBUTION.md)
302
+ - [./assets/pieces/chessnut/LICENSE.txt](./assets/pieces/chessnut/LICENSE.txt)
@@ -0,0 +1,21 @@
1
+ export { clearElementChildren, createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
2
+ export type { RuntimeReadonlyMutationSession } from '../../runtime/mutation/types.js';
3
+ export { isNonEmptyPieceCode } from '../../state/board/check.js';
4
+ export { fileOf, rankOf, squareOf } from '../../state/board/coords.js';
5
+ export { denormalizeMove } from '../../state/board/denormalize.js';
6
+ export { normalizePiece } from '../../state/board/normalize.js';
7
+ export { fromPieceCode, toPieceCode } from '../../state/board/piece.js';
8
+ export type { ColorCode, EmptyPieceCode, NonEmptyPieceCode, PieceCode, RoleCode, RolePromotionCode, Square, SquareFile, SquareRank } from '../../state/board/types/internal.js';
9
+ export type { MoveOutput } from '../../state/board/types/output.js';
10
+ export type { MovabilityModeCode } from '../../state/interaction/types/internal.js';
11
+ export { extensionCreateInternalBase, extensionDestroyBase, extensionIsDestroyedBase, extensionIsMountedBase, extensionMountBase, extensionUnmountBase } from '../first-party/common/helpers.js';
12
+ export type { ExtensionInternalBase, OpaqueColor } from '../first-party/common/types.js';
13
+ export type { PieceUrlsInput } from '../first-party/main-renderer/types/input.js';
14
+ export type { PieceUrls } from '../first-party/main-renderer/types/internal.js';
15
+ export type { PromotionPieceCode } from '../first-party/promotion/types/internal.js';
16
+ export type { ExtensionSlotName, ExtensionSlotSvgRoots } from '../types/basic/mount.js';
17
+ export { isFrameMounted, isFrameRenderable } from '../types/basic/update.js';
18
+ export type { UpdateFrameSnapshot } from '../types/basic/update.js';
19
+ export { isUpdateContextMounted, isUpdateContextRenderable } from '../types/context/update.js';
20
+ export type { ExtensionCreateInstanceOptions, ExtensionDefinition, ExtensionInstance } from '../types/extension.js';
21
+ export type { ExtensionRuntimeSurface } from '../types/surface/main.js';
@@ -0,0 +1,9 @@
1
+ export { clearElementChildren, createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
2
+ export { isNonEmptyPieceCode } from '../../state/board/check.js';
3
+ export { fileOf, rankOf, squareOf } from '../../state/board/coords.js';
4
+ export { denormalizeMove } from '../../state/board/denormalize.js';
5
+ export { normalizePiece } from '../../state/board/normalize.js';
6
+ export { fromPieceCode, toPieceCode } from '../../state/board/piece.js';
7
+ export { extensionCreateInternalBase, extensionDestroyBase, extensionIsDestroyedBase, extensionIsMountedBase, extensionMountBase, extensionUnmountBase } from '../first-party/common/helpers.js';
8
+ export { isFrameMounted, isFrameRenderable } from '../types/basic/update.js';
9
+ export { isUpdateContextMounted, isUpdateContextRenderable } from '../types/context/update.js';
@@ -2,7 +2,7 @@ import assert from '@ktarmyshov/assert';
2
2
  import { toMerged } from 'es-toolkit';
3
3
  import { createSvgElement, updateElementAttributes } from '../../../render/svg/helpers.js';
4
4
  import { isUpdateContextRenderable } from '../../types/context/update.js';
5
- import { extensionCreateInternalBase, extensionDestroy, extensionMount, extensionUnmount } from '../common/helpers.js';
5
+ import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
6
6
  import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
7
7
  export function createActiveTarget(config = {}) {
8
8
  const mergedConfig = toMerged(DEFAULT_CONFIG, config);
@@ -31,7 +31,7 @@ function createActiveTargetInstance(config) {
31
31
  return {
32
32
  id: EXTENSION_ID,
33
33
  mount(env) {
34
- extensionMount(internalState, env.slotRoots);
34
+ extensionMountBase(internalState, env.slotRoots);
35
35
  },
36
36
  onUpdate(context) {
37
37
  const needsRender = context.mutation.hasMutation({
@@ -98,11 +98,11 @@ function createActiveTargetInstance(config) {
98
98
  }
99
99
  },
100
100
  unmount() {
101
- extensionUnmount(internalState);
101
+ extensionUnmountBase(internalState);
102
102
  extensionClean(internalState);
103
103
  },
104
104
  destroy() {
105
- extensionDestroy(internalState);
105
+ extensionDestroyBase(internalState);
106
106
  extensionClean(internalState);
107
107
  }
108
108
  };
@@ -1,6 +1,6 @@
1
1
  import { PartialDeep } from 'type-fest';
2
2
  import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
3
- import { ExtensionInternal, OpaqueColor } from '../common/types.js';
3
+ import { ExtensionInternalBase, OpaqueColor } from '../common/types.js';
4
4
  export declare const EXTENSION_SLOTS: readonly ["underPieces", "overPieces"];
5
5
  export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
6
6
  export declare const EXTENSION_ID: "activeTarget";
@@ -15,7 +15,7 @@ export declare const DEFAULT_CONFIG: ActiveTargetConfig;
15
15
  export type ActiveTargetInitConfig = PartialDeep<ActiveTargetConfig>;
16
16
  export type ActiveTargetDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
17
17
  export type ActiveTargetInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
18
- export interface ActiveTargetInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
18
+ export interface ActiveTargetInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
19
19
  svgRect: SVGRectElement | null;
20
20
  svgCircle: SVGCircleElement | null;
21
21
  readonly config: ActiveTargetConfig;
@@ -1,4 +1,7 @@
1
- export const EXTENSION_SLOTS = ['underPieces', 'overPieces'];
1
+ export const EXTENSION_SLOTS = [
2
+ 'underPieces',
3
+ 'overPieces'
4
+ ];
2
5
  export const EXTENSION_ID = 'activeTarget';
3
6
  export const DEFAULT_CONFIG = {
4
7
  squareColor: {
@@ -1,4 +1,3 @@
1
- import { ExtensionSlotSvgRoots } from '../../types/basic/mount.js';
2
1
  import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
3
2
  export declare const EXTENSION_SLOTS: readonly [];
4
3
  export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
@@ -8,7 +7,6 @@ export interface AutoPromotePublic {
8
7
  toQueen: boolean;
9
8
  }
10
9
  export type AutoPromoteInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, AutoPromotePublic>;
11
- export type AutoPromoteSlotRoots = ExtensionSlotSvgRoots<typeof EXTENSION_SLOTS>;
12
10
  export interface AutoPromoteInstanceInternal {
13
11
  toQueen: boolean;
14
12
  }
@@ -1,6 +1,5 @@
1
1
  import { RuntimeReadonlyMutationSession } from '../../../runtime/mutation/types.js';
2
2
  import { MoveOutput } from '../../../state/board/types/output.js';
3
- import { ExtensionSlotSvgRoots } from '../../types/basic/mount.js';
4
3
  import { UpdateFrameSnapshot } from '../../types/basic/update.js';
5
4
  import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
6
5
  export declare const EXTENSION_SLOTS: readonly [];
@@ -19,7 +18,6 @@ export interface BoardEventsPublic {
19
18
  setOnRawUpdate(callback: OnRawUpdateCallback | null): void;
20
19
  }
21
20
  export type BoardEventsInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, BoardEventsPublic>;
22
- export type BoardEventsSlotRoots = ExtensionSlotSvgRoots<typeof EXTENSION_SLOTS>;
23
21
  export interface BoardEventsInstanceInternal {
24
22
  onUIMove: OnUIMoveCallback | null;
25
23
  onRawUpdate: OnRawUpdateCallback | null;
@@ -1,8 +1,8 @@
1
1
  import { ExtensionSlotName } from '../../types/basic/mount.js';
2
- import { ExtensionInternal } from './types.js';
3
- export declare function extensionCreateInternalBase<TSlots extends readonly ExtensionSlotName[]>(): ExtensionInternal<TSlots>;
4
- export declare function extensionIsMounted<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternal<TSlots>): boolean;
5
- export declare function extensionIsDestroyed<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternal<TSlots>): boolean;
6
- export declare function extensionMount<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternal<TSlots>, slotRoots: NonNullable<ExtensionInternal<TSlots>['slotRoots']>): void;
7
- export declare function extensionUnmount<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternal<TSlots>): void;
8
- export declare function extensionDestroy<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternal<TSlots>): void;
2
+ import { ExtensionInternalBase } from './types.js';
3
+ export declare function extensionCreateInternalBase<TSlots extends readonly ExtensionSlotName[]>(): ExtensionInternalBase<TSlots>;
4
+ export declare function extensionIsMountedBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): boolean;
5
+ export declare function extensionIsDestroyedBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): boolean;
6
+ export declare function extensionMountBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>, slotRoots: NonNullable<ExtensionInternalBase<TSlots>['slotRoots']>): void;
7
+ export declare function extensionUnmountBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): void;
8
+ export declare function extensionDestroyBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): void;
@@ -6,28 +6,28 @@ export function extensionCreateInternalBase() {
6
6
  destroyed: false
7
7
  };
8
8
  }
9
- export function extensionIsMounted(state) {
9
+ export function extensionIsMountedBase(state) {
10
10
  return state.slotRoots !== null;
11
11
  }
12
- export function extensionIsDestroyed(state) {
12
+ export function extensionIsDestroyedBase(state) {
13
13
  return state.destroyed;
14
14
  }
15
- export function extensionMount(state, slotRoots) {
16
- assert(!extensionIsMounted(state), 'Extension is already mounted');
17
- assert(!extensionIsDestroyed(state), 'Extension is destroyed');
15
+ export function extensionMountBase(state, slotRoots) {
16
+ assert(!extensionIsMountedBase(state), 'Extension is already mounted');
17
+ assert(!extensionIsDestroyedBase(state), 'Extension is destroyed');
18
18
  state.slotRoots = slotRoots;
19
19
  }
20
- export function extensionUnmount(state) {
21
- assert(extensionIsMounted(state), 'Extension is not mounted');
20
+ export function extensionUnmountBase(state) {
21
+ assert(extensionIsMountedBase(state), 'Extension is not mounted');
22
22
  for (const slotRoot of Object.values(state.slotRoots ?? {})) {
23
23
  clearElementChildren(slotRoot);
24
24
  }
25
25
  state.slotRoots = null;
26
26
  }
27
- export function extensionDestroy(state) {
28
- assert(!extensionIsDestroyed(state), 'Extension is already destroyed');
29
- if (extensionIsMounted(state)) {
30
- extensionUnmount(state);
27
+ export function extensionDestroyBase(state) {
28
+ assert(!extensionIsDestroyedBase(state), 'Extension is already destroyed');
29
+ if (extensionIsMountedBase(state)) {
30
+ extensionUnmountBase(state);
31
31
  }
32
32
  state.destroyed = true;
33
33
  }
@@ -1,10 +1,9 @@
1
1
  import { ExtensionSlotName, ExtensionSlotSvgRoots } from '../../types/basic/mount.js';
2
- export interface ExtensionInternal<TSlots extends readonly ExtensionSlotName[]> {
2
+ export interface ExtensionInternalBase<TSlots extends readonly ExtensionSlotName[]> {
3
3
  slotRoots: ExtensionSlotSvgRoots<TSlots> | null;
4
4
  destroyed: boolean;
5
5
  }
6
- export type AnyExtensionInternal = ExtensionInternal<readonly ExtensionSlotName[]>;
7
6
  export interface OpaqueColor {
8
7
  color: string;
9
- opacity: number;
8
+ opacity: number | string;
10
9
  }
@@ -2,7 +2,7 @@ import assert from '@ktarmyshov/assert';
2
2
  import { toMerged } from 'es-toolkit';
3
3
  import { createSvgElement, updateElementAttributes } from '../../../render/svg/helpers.js';
4
4
  import { isUpdateContextRenderable } from '../../types/context/update.js';
5
- import { extensionCreateInternalBase, extensionDestroy, extensionMount, extensionUnmount } from '../common/helpers.js';
5
+ import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
6
6
  import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
7
7
  export function createLastMove(config = {}) {
8
8
  const mergedConfig = toMerged(DEFAULT_CONFIG, config);
@@ -31,7 +31,7 @@ function createLastMoveInstance(config) {
31
31
  return {
32
32
  id: EXTENSION_ID,
33
33
  mount(env) {
34
- extensionMount(internalState, env.slotRoots);
34
+ extensionMountBase(internalState, env.slotRoots);
35
35
  },
36
36
  onUpdate(context) {
37
37
  const needsRender = context.mutation.hasMutation({
@@ -95,11 +95,11 @@ function createLastMoveInstance(config) {
95
95
  }
96
96
  },
97
97
  unmount() {
98
- extensionUnmount(internalState);
98
+ extensionUnmountBase(internalState);
99
99
  extensionClean(internalState);
100
100
  },
101
101
  destroy() {
102
- extensionDestroy(internalState);
102
+ extensionDestroyBase(internalState);
103
103
  extensionClean(internalState);
104
104
  }
105
105
  };
@@ -1,6 +1,6 @@
1
1
  import { PartialDeep } from 'type-fest';
2
2
  import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
3
- import { ExtensionInternal, OpaqueColor } from '../common/types.js';
3
+ import { ExtensionInternalBase, OpaqueColor } from '../common/types.js';
4
4
  export declare const EXTENSION_SLOTS: readonly ["underPieces"];
5
5
  export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
6
6
  export declare const EXTENSION_ID: "lastMove";
@@ -9,7 +9,7 @@ export declare const DEFAULT_CONFIG: LastMoveConfig;
9
9
  export type LastMoveInitConfig = PartialDeep<LastMoveConfig>;
10
10
  export type LastMoveDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
11
11
  export type LastMoveInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
12
- export interface LastMoveInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
12
+ export interface LastMoveInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
13
13
  svgRectFrom: SVGRectElement | null;
14
14
  svgRectTo: SVGRectElement | null;
15
15
  readonly config: LastMoveConfig;
@@ -4,7 +4,7 @@ import { clearElementChildren, createSvgElement } from '../../../render/svg/help
4
4
  import { isNonEmptyPieceCode } from '../../../state/board/check.js';
5
5
  import { fromPieceCode } from '../../../state/board/piece.js';
6
6
  import { isUpdateContextRenderable } from '../../types/context/update.js';
7
- import { extensionCreateInternalBase, extensionDestroy, extensionMount, extensionUnmount } from '../common/helpers.js';
7
+ import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
8
8
  import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
9
9
  export function createLegalMoves(config = {}) {
10
10
  const mergedConfig = toMerged(DEFAULT_CONFIG, config);
@@ -31,7 +31,7 @@ function createLegalMovesInstance(config) {
31
31
  return {
32
32
  id: EXTENSION_ID,
33
33
  mount(env) {
34
- extensionMount(internalState, env.slotRoots);
34
+ extensionMountBase(internalState, env.slotRoots);
35
35
  },
36
36
  onUpdate(context) {
37
37
  const needsRender = context.mutation.hasMutation({
@@ -90,11 +90,11 @@ function createLegalMovesInstance(config) {
90
90
  }
91
91
  },
92
92
  unmount() {
93
- extensionUnmount(internalState);
93
+ extensionUnmountBase(internalState);
94
94
  extensionClean(internalState);
95
95
  },
96
96
  destroy() {
97
- extensionDestroy(internalState);
97
+ extensionDestroyBase(internalState);
98
98
  extensionClean(internalState);
99
99
  }
100
100
  };
@@ -1,6 +1,6 @@
1
1
  import { PartialDeep } from 'type-fest';
2
2
  import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
3
- import { ExtensionInternal, OpaqueColor } from '../common/types.js';
3
+ import { ExtensionInternalBase, OpaqueColor } from '../common/types.js';
4
4
  export declare const EXTENSION_SLOTS: readonly ["overPieces"];
5
5
  export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
6
6
  export declare const EXTENSION_ID: "legalMoves";
@@ -19,7 +19,7 @@ export declare const DEFAULT_CONFIG: LegalMovesConfig;
19
19
  export type LegalMovesInitConfig = PartialDeep<LegalMovesConfig>;
20
20
  export type LegalMovesDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
21
21
  export type LegalMovesInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
22
- export interface LegalMovesInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
22
+ export interface LegalMovesInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
23
23
  destroyed: boolean;
24
24
  svgCircles: SVGCircleElement[];
25
25
  readonly config: LegalMovesConfig;
@@ -1,5 +1,5 @@
1
1
  import { toMerged } from 'es-toolkit';
2
- import { extensionCreateInternalBase, extensionDestroy, extensionMount, extensionUnmount } from '../common/helpers.js';
2
+ import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
3
3
  import { createMainRendererAnimation } from './animation/factory.js';
4
4
  import { createMainRendererBoard } from './board/factory.js';
5
5
  import { createMainRendererCoordinates } from './coordinates/factory.js';
@@ -51,7 +51,7 @@ function createMainRendererInstance(options, config) {
51
51
  return {
52
52
  id: EXTENSION_ID,
53
53
  mount(env) {
54
- extensionMount(internalState, env.slotRoots);
54
+ extensionMountBase(internalState, env.slotRoots);
55
55
  },
56
56
  onUpdate(context) {
57
57
  internalState.board.onUpdate(context);
@@ -85,11 +85,11 @@ function createMainRendererInstance(options, config) {
85
85
  },
86
86
  unmount() {
87
87
  extensionUnmountLocal(internalState);
88
- extensionUnmount(internalState);
88
+ extensionUnmountBase(internalState);
89
89
  },
90
90
  destroy() {
91
91
  extensionUnmountLocal(internalState);
92
- extensionDestroy(internalState);
92
+ extensionDestroyBase(internalState);
93
93
  }
94
94
  };
95
95
  }
@@ -1,12 +1,12 @@
1
1
  import { ExtensionRuntimeSurface } from '../../../types/surface/main.js';
2
- import { ExtensionInternal } from '../../common/types.js';
2
+ import { ExtensionInternalBase } from '../../common/types.js';
3
3
  import { MainRendererAnimation } from '../animation/types.js';
4
4
  import { MainRendererBoard } from '../board/types.js';
5
5
  import { MainRendererCoordinates } from '../coordinates/types.js';
6
6
  import { MainRendererDrag } from '../drag/types.js';
7
7
  import { MainRendererPieces } from '../pieces/types.js';
8
8
  import { ExtensionSlotsType } from './extension.js';
9
- export interface MainRendererInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
9
+ export interface MainRendererInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
10
10
  readonly board: MainRendererBoard;
11
11
  readonly coordinates: MainRendererCoordinates;
12
12
  readonly pieces: MainRendererPieces;
@@ -3,7 +3,7 @@ import { createSvgElement, updateElementAttributes } from '../../../render/svg/h
3
3
  import { fileOf, rankOf, squareOf } from '../../../state/board/coords.js';
4
4
  import { toPieceCode } from '../../../state/board/piece.js';
5
5
  import { isUpdateContextRenderable } from '../../types/context/update.js';
6
- import { extensionCreateInternalBase, extensionDestroy, extensionMount, extensionUnmount } from '../common/helpers.js';
6
+ import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
7
7
  import { normalizePromotionConfig } from './normalize.js';
8
8
  import { EXTENSION_ID, EXTENSION_SLOTS } from './types/main.js';
9
9
  export function createPromotion(config = {}) {
@@ -57,7 +57,7 @@ function createPromotionInstance(options, config) {
57
57
  return {
58
58
  id: EXTENSION_ID,
59
59
  mount(env) {
60
- extensionMount(internalState, env.slotRoots);
60
+ extensionMountBase(internalState, env.slotRoots);
61
61
  },
62
62
  onUIMoveRequest(context) {
63
63
  const request = context.request;
@@ -208,11 +208,11 @@ function createPromotionInstance(options, config) {
208
208
  internalState.runtimeSurface.commands.cancelDeferredUIMoveRequest();
209
209
  },
210
210
  unmount() {
211
- extensionUnmount(internalState);
211
+ extensionUnmountBase(internalState);
212
212
  extensionClean(internalState);
213
213
  },
214
214
  destroy() {
215
- extensionDestroy(internalState);
215
+ extensionDestroyBase(internalState);
216
216
  extensionClean(internalState);
217
217
  }
218
218
  };
@@ -1,7 +1,7 @@
1
1
  import { RolePromotionCode, Square } from '../../../../state/board/types/internal.js';
2
2
  import { ExtensionDefinition, ExtensionInstance } from '../../../types/extension.js';
3
3
  import { ExtensionRuntimeSurface } from '../../../types/surface/main.js';
4
- import { ExtensionInternal } from '../../common/types.js';
4
+ import { ExtensionInternalBase } from '../../common/types.js';
5
5
  import { PromotionConfig } from './internal.js';
6
6
  export declare const EXTENSION_SLOTS: readonly ["animation"];
7
7
  export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
@@ -12,7 +12,7 @@ export interface PromotionPieceNode {
12
12
  readonly svg: SVGImageElement;
13
13
  readonly rect: SVGRectElement;
14
14
  }
15
- export interface PromotionInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
15
+ export interface PromotionInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
16
16
  readonly config: PromotionConfig;
17
17
  readonly svgPromotionPieces: Map<RolePromotionCode, PromotionPieceNode>;
18
18
  readonly activePromotionSquares: Map<Square, RolePromotionCode>;
@@ -2,7 +2,7 @@ import assert from '@ktarmyshov/assert';
2
2
  import { toMerged } from 'es-toolkit';
3
3
  import { createSvgElement, updateElementAttributes } from '../../../render/svg/helpers.js';
4
4
  import { isUpdateContextRenderable } from '../../types/context/update.js';
5
- import { extensionCreateInternalBase, extensionDestroy, extensionMount, extensionUnmount } from '../common/helpers.js';
5
+ import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
6
6
  import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
7
7
  export function createSelectedSquare(config = {}) {
8
8
  const mergedConfig = toMerged(DEFAULT_CONFIG, config);
@@ -29,7 +29,7 @@ function createSelectedSquareInstance(config) {
29
29
  return {
30
30
  id: EXTENSION_ID,
31
31
  mount(env) {
32
- extensionMount(internalState, env.slotRoots);
32
+ extensionMountBase(internalState, env.slotRoots);
33
33
  },
34
34
  onUpdate(context) {
35
35
  const needsRender = context.mutation.hasMutation({
@@ -77,11 +77,11 @@ function createSelectedSquareInstance(config) {
77
77
  }
78
78
  },
79
79
  unmount() {
80
- extensionUnmount(internalState);
80
+ extensionUnmountBase(internalState);
81
81
  extensionClean(internalState);
82
82
  },
83
83
  destroy() {
84
- extensionDestroy(internalState);
84
+ extensionDestroyBase(internalState);
85
85
  extensionClean(internalState);
86
86
  }
87
87
  };
@@ -1,6 +1,6 @@
1
1
  import { PartialDeep } from 'type-fest';
2
2
  import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
3
- import { ExtensionInternal, OpaqueColor } from '../common/types.js';
3
+ import { ExtensionInternalBase, OpaqueColor } from '../common/types.js';
4
4
  export declare const EXTENSION_SLOTS: readonly ["underPieces"];
5
5
  export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
6
6
  export declare const EXTENSION_ID: "selectedSquare";
@@ -9,7 +9,7 @@ export declare const DEFAULT_CONFIG: SelectedSquareConfig;
9
9
  export type SelectedSquareInitConfig = PartialDeep<SelectedSquareConfig>;
10
10
  export type SelectedSquareDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
11
11
  export type SelectedSquareInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
12
- export interface SelectedSquareInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
12
+ export interface SelectedSquareInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
13
13
  svgRect: SVGRectElement | null;
14
14
  readonly config: SelectedSquareConfig;
15
15
  }
@@ -1,8 +1 @@
1
- export { createActiveTarget } from './first-party/active-target/factory.js';
2
- export { createBoardEvents } from './first-party/board-events/factory.js';
3
- export { createLastMove } from './first-party/last-move/factory.js';
4
- export { createLegalMoves } from './first-party/legal-moves/factory.js';
5
- export { createMainRenderer } from './first-party/main-renderer/factory.js';
6
- export { createSelectedSquare } from './first-party/selected-square/factory.js';
7
- export type { AnyExtensionDefinition, ExtensionDefinitionId, ExtensionDefinitionPublicApi } from './types/extension.js';
8
- export type { builtInExtensionFactoryMap, BuiltInExtensionId } from './types/wrapper.js';
1
+ export { builtInExtensionFactoryMap, type BuiltInExtensionId } from './types/wrapper.js';
@@ -1,6 +1 @@
1
- export { createActiveTarget } from './first-party/active-target/factory.js';
2
- export { createBoardEvents } from './first-party/board-events/factory.js';
3
- export { createLastMove } from './first-party/last-move/factory.js';
4
- export { createLegalMoves } from './first-party/legal-moves/factory.js';
5
- export { createMainRenderer } from './first-party/main-renderer/factory.js';
6
- export { createSelectedSquare } from './first-party/selected-square/factory.js';
1
+ export { builtInExtensionFactoryMap } from './types/wrapper.js';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export { createBoard } from './wrapper/factory.js';
2
- export type { Chessboard, ChessboardExtensionInput, ChessboardInitOptions, ChessboardRuntimeSurface } from './wrapper/types.js';
2
+ export type { Chessboard, ChessboardExtensionInput, ChessboardInitOptions } from './wrapper/types.js';
3
3
  export type { MoveRequestInput, PiecePositionRecordString } from './state/board/types/input.js';
4
- export type { RuntimeStateInitOptions } from './state/types.js';
@@ -11,6 +11,7 @@ export function createInteractionController(options) {
11
11
  onEvent(context) {
12
12
  internalState.surface.onEvent(context);
13
13
  if (context.rawEvent.defaultPrevented) {
14
+ transmitTransientInput(internalState, context);
14
15
  return; // The event has been handled by the surface (extensions)
15
16
  }
16
17
  switch (context.rawEvent.type) {
@@ -28,6 +29,10 @@ export function createInteractionController(options) {
28
29
  break;
29
30
  }
30
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
+ }
31
36
  }
32
37
  };
33
38
  }
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.0-rc.0",
4
+ "version": "1.0.1",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Mirasen",
@@ -38,7 +38,11 @@
38
38
  "types": "./dist/extensions/index.d.ts",
39
39
  "import": "./dist/extensions/index.js"
40
40
  },
41
- "./unstable/*.js": {
41
+ "./extensions/build": {
42
+ "types": "./dist/extensions/build/index.d.ts",
43
+ "import": "./dist/extensions/build/index.js"
44
+ },
45
+ "./unstable/*": {
42
46
  "types": "./dist/*.d.ts",
43
47
  "import": "./dist/*.js"
44
48
  }
@@ -52,10 +56,8 @@
52
56
  "lint": "prettier --check . && eslint .",
53
57
  "check": "tsc --skipLibCheck --noEmit",
54
58
  "check:test": "tsc --project tsconfig-test.json --skipLibCheck --noEmit",
55
- "test": "echo 'No tests for now...'",
56
- "test-backup": "npm run check:test && vitest run --run",
57
- "coverage": "echo 'No tests for now...'",
58
- "coverage-backup": "vitest run --coverage",
59
+ "test": "npm run check:test && vitest run --run",
60
+ "coverage": "vitest run --coverage",
59
61
  "changeset:version": "changeset version && npm install && npm run format && git add --all",
60
62
  "changeset:publish": "changeset publish"
61
63
  },
@@ -91,7 +93,7 @@
91
93
  "vitest": "^4.0.18"
92
94
  },
93
95
  "engines": {
94
- "node": ">=22"
96
+ "node": ">=20"
95
97
  },
96
98
  "dependencies": {
97
99
  "@echecs/fen": "^2.1.0",