@mirasen/chessboard 1.1.5 → 1.2.0

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 (131) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +1 -1
  3. package/dist/animation/render/fade.d.ts +2 -3
  4. package/dist/animation/render/fade.js +6 -6
  5. package/dist/animation/render/move.d.ts +2 -3
  6. package/dist/animation/render/move.js +6 -6
  7. package/dist/animation/render/plan.d.ts +3 -4
  8. package/dist/animation/render/plan.js +5 -5
  9. package/dist/animation/render/static.d.ts +2 -3
  10. package/dist/animation/render/static.js +4 -4
  11. package/dist/animation/render/types.d.ts +10 -4
  12. package/dist/extensions/build/index.d.ts +1 -2
  13. package/dist/extensions/build/index.js +1 -1
  14. package/dist/extensions/factory/main.js +11 -7
  15. package/dist/extensions/factory/runtime.js +24 -1
  16. package/dist/extensions/first-party/active-target/factory.js +18 -11
  17. package/dist/extensions/first-party/annotations/api.d.ts +16 -0
  18. package/dist/extensions/first-party/annotations/api.js +79 -0
  19. package/dist/extensions/first-party/annotations/committed.d.ts +3 -0
  20. package/dist/extensions/first-party/annotations/committed.js +11 -0
  21. package/dist/extensions/first-party/annotations/constants.d.ts +3 -0
  22. package/dist/extensions/first-party/annotations/constants.js +54 -0
  23. package/dist/extensions/first-party/annotations/denormalize.d.ts +4 -0
  24. package/dist/extensions/first-party/annotations/denormalize.js +22 -0
  25. package/dist/extensions/first-party/annotations/factory.d.ts +3 -0
  26. package/dist/extensions/first-party/annotations/factory.js +143 -0
  27. package/dist/extensions/first-party/annotations/interaction.d.ts +8 -0
  28. package/dist/extensions/first-party/annotations/interaction.js +129 -0
  29. package/dist/extensions/first-party/annotations/invalidation.d.ts +3 -0
  30. package/dist/extensions/first-party/annotations/invalidation.js +7 -0
  31. package/dist/extensions/first-party/annotations/normalize.d.ts +12 -0
  32. package/dist/extensions/first-party/annotations/normalize.js +54 -0
  33. package/dist/extensions/first-party/annotations/render/committed.d.ts +3 -0
  34. package/dist/extensions/first-party/annotations/render/committed.js +123 -0
  35. package/dist/extensions/first-party/annotations/render/helpers/arrows.d.ts +33 -0
  36. package/dist/extensions/first-party/annotations/render/helpers/arrows.js +93 -0
  37. package/dist/extensions/first-party/annotations/render/helpers/circles.d.ts +18 -0
  38. package/dist/extensions/first-party/annotations/render/helpers/circles.js +30 -0
  39. package/dist/extensions/first-party/annotations/render/preview.d.ts +3 -0
  40. package/dist/extensions/first-party/annotations/render/preview.js +96 -0
  41. package/dist/extensions/first-party/annotations/types/internal.d.ts +77 -0
  42. package/dist/extensions/first-party/annotations/types/internal.js +1 -0
  43. package/dist/extensions/first-party/annotations/types/main.d.ts +40 -0
  44. package/dist/extensions/first-party/annotations/types/main.js +6 -0
  45. package/dist/extensions/first-party/annotations/types/public.d.ts +48 -0
  46. package/dist/extensions/first-party/annotations/types/public.js +1 -0
  47. package/dist/extensions/first-party/annotations/update.d.ts +3 -0
  48. package/dist/extensions/first-party/annotations/update.js +45 -0
  49. package/dist/extensions/first-party/auto-promote/factory.js +6 -6
  50. package/dist/extensions/first-party/board-events/factory.js +6 -6
  51. package/dist/extensions/first-party/common/helpers.d.ts +2 -1
  52. package/dist/extensions/first-party/common/helpers.js +6 -5
  53. package/dist/extensions/first-party/common/types.d.ts +2 -0
  54. package/dist/extensions/first-party/last-move/factory.js +9 -9
  55. package/dist/extensions/first-party/legal-moves/factory.js +8 -8
  56. package/dist/extensions/first-party/main-renderer/animation/factory.d.ts +2 -2
  57. package/dist/extensions/first-party/main-renderer/animation/factory.js +4 -4
  58. package/dist/extensions/first-party/main-renderer/animation/render.d.ts +1 -1
  59. package/dist/extensions/first-party/main-renderer/animation/render.js +2 -2
  60. package/dist/extensions/first-party/main-renderer/animation/types.d.ts +3 -3
  61. package/dist/extensions/first-party/main-renderer/board/factory.js +2 -2
  62. package/dist/extensions/first-party/main-renderer/board/render.d.ts +1 -1
  63. package/dist/extensions/first-party/main-renderer/board/render.js +4 -4
  64. package/dist/extensions/first-party/main-renderer/board/types.d.ts +1 -1
  65. package/dist/extensions/first-party/main-renderer/coordinates/factory.js +2 -2
  66. package/dist/extensions/first-party/main-renderer/coordinates/render.d.ts +1 -1
  67. package/dist/extensions/first-party/main-renderer/coordinates/render.js +5 -5
  68. package/dist/extensions/first-party/main-renderer/coordinates/types.d.ts +1 -1
  69. package/dist/extensions/first-party/main-renderer/drag/factory.d.ts +2 -2
  70. package/dist/extensions/first-party/main-renderer/drag/factory.js +11 -8
  71. package/dist/extensions/first-party/main-renderer/drag/render.d.ts +1 -1
  72. package/dist/extensions/first-party/main-renderer/drag/render.js +7 -6
  73. package/dist/extensions/first-party/main-renderer/drag/types.d.ts +6 -5
  74. package/dist/extensions/first-party/main-renderer/drag/update.js +2 -2
  75. package/dist/extensions/first-party/main-renderer/factory.js +9 -5
  76. package/dist/extensions/first-party/main-renderer/piece-symbols.d.ts +19 -0
  77. package/dist/extensions/first-party/main-renderer/piece-symbols.js +45 -0
  78. package/dist/extensions/first-party/main-renderer/pieces/factory.d.ts +2 -2
  79. package/dist/extensions/first-party/main-renderer/pieces/factory.js +4 -4
  80. package/dist/extensions/first-party/main-renderer/pieces/render.d.ts +1 -1
  81. package/dist/extensions/first-party/main-renderer/pieces/render.js +8 -8
  82. package/dist/extensions/first-party/main-renderer/pieces/types.d.ts +4 -4
  83. package/dist/extensions/first-party/main-renderer/types/extension.d.ts +1 -1
  84. package/dist/extensions/first-party/main-renderer/types/extension.js +8 -1
  85. package/dist/extensions/first-party/main-renderer/types/instance.d.ts +2 -0
  86. package/dist/extensions/first-party/promotion/factory.js +10 -10
  87. package/dist/extensions/first-party/selected-square/factory.js +8 -8
  88. package/dist/extensions/first-party/watermark/factory.js +8 -8
  89. package/dist/extensions/types/basic/events.d.ts +32 -3
  90. package/dist/extensions/types/basic/events.js +2 -1
  91. package/dist/extensions/types/basic/mount.d.ts +5 -2
  92. package/dist/extensions/types/basic/mount.js +0 -2
  93. package/dist/extensions/types/basic/transient-visuals.d.ts +1 -1
  94. package/dist/extensions/types/context/animation.d.ts +3 -3
  95. package/dist/extensions/types/context/events.d.ts +2 -1
  96. package/dist/extensions/types/context/update.d.ts +6 -7
  97. package/dist/extensions/types/extension.d.ts +3 -0
  98. package/dist/extensions/types/main.d.ts +1 -0
  99. package/dist/extensions/types/surface/commands.d.ts +1 -1
  100. package/dist/extensions/types/surface/main.d.ts +6 -4
  101. package/dist/extensions/types/wrapper.d.ts +3 -1
  102. package/dist/extensions/types/wrapper.js +6 -2
  103. package/dist/extensions/update.js +6 -10
  104. package/dist/index.d.ts +1 -1
  105. package/dist/render/mount.js +3 -3
  106. package/dist/render/rendering/state.js +2 -2
  107. package/dist/render/svg/factory.js +40 -25
  108. package/dist/render/svg/helpers.d.ts +8 -4
  109. package/dist/render/svg/helpers.js +27 -12
  110. package/dist/render/svg/ids.d.ts +6 -0
  111. package/dist/render/svg/ids.js +11 -0
  112. package/dist/render/types.d.ts +4 -3
  113. package/dist/render/types.js +2 -1
  114. package/dist/runtime/factory/input.d.ts +3 -1
  115. package/dist/runtime/factory/input.js +11 -2
  116. package/dist/runtime/factory/main.js +10 -4
  117. package/dist/runtime/input/adapter/event.js +2 -2
  118. package/dist/runtime/input/adapter/pointer.d.ts +2 -2
  119. package/dist/runtime/input/controller/factory.d.ts +1 -1
  120. package/dist/runtime/input/controller/factory.js +35 -15
  121. package/dist/runtime/input/controller/helpers.d.ts +1 -1
  122. package/dist/runtime/input/controller/interaction.d.ts +3 -0
  123. package/dist/runtime/input/controller/interaction.js +14 -0
  124. package/dist/runtime/input/controller/pointer.d.ts +6 -6
  125. package/dist/runtime/input/controller/pointer.js +53 -27
  126. package/dist/runtime/input/controller/transient-visuals.d.ts +2 -3
  127. package/dist/runtime/input/controller/types.d.ts +6 -5
  128. package/dist/runtime/types/main.d.ts +1 -1
  129. package/dist/wrapper/factory.js +2 -7
  130. package/dist/wrapper/types.d.ts +3 -11
  131. package/package.json +1 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @mirasen/chessboard
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 101903a: Add board annotations with square markers, free arrows, live previews, and programmatic annotation APIs.
8
+ - 101903a: Refines extension/rendering internals before third-party extension APIs stabilize.
9
+
10
+ Breaking for custom extension authors:
11
+ - SVG helper exports were simplified around generic SVG helpers: `createSvgElement`, `createSvgRootElement`, `createSvgDefsElement`, and `clearSvgElementChildren`.
12
+ - `defs` is now an extension-owned top-level `<defs>` slot under the root `<svg>`, instead of a shared `defs-root`.
13
+ - `extensionUnmountBase` now clears the extension-owned slot roots directly and no longer accepts an `extensionId`.
14
+
15
+ Board/runtime initialization is now element-first. Most consumers using direct container-based board creation should not need migration changes.
16
+
17
+ ### Patch Changes
18
+
19
+ - 4ec620b: dependabot: directory '/', update @sveltejs/kit
20
+ - 4ec620b: dependabot: directory '/', update eslint
21
+ - 4ec620b: dependabot: directory '/', update globals
22
+ - 4ec620b: dependabot: directory '/', update svelte-check
23
+ - 49760cc: dependabot: directory '/', update @sveltejs/kit
24
+ - 49760cc: dependabot: directory '/', update @types/node
25
+ - 49760cc: dependabot: directory '/', update publint
26
+ - 49760cc: dependabot: directory '/', update svelte-check
27
+ - 49760cc: dependabot: directory '/', update typescript-eslint
28
+ - 559677e: dependabot: directory '/', update @eslint/compat
29
+ - 559677e: dependabot: directory '/', update publint
30
+ - 72a1480: dependabot: directory '/', update @playwright/test
31
+ - 72a1480: dependabot: directory '/', update @types/node
32
+ - 72a1480: dependabot: directory '/', update @vitest/browser-playwright
33
+ - 72a1480: dependabot: directory '/', update @vitest/coverage-istanbul
34
+ - 72a1480: dependabot: directory '/', update playwright
35
+ - 72a1480: dependabot: directory '/', update prettier-plugin-svelte
36
+ - 72a1480: dependabot: directory '/', update publint
37
+ - 72a1480: dependabot: directory '/', update typescript-eslint
38
+ - 72a1480: dependabot: directory '/', update vitest
39
+
3
40
  ## 1.1.5
4
41
 
5
42
  ### Patch Changes
package/README.md CHANGED
@@ -16,7 +16,7 @@ A framework-agnostic TypeScript chessboard platform with real chess interaction
16
16
 
17
17
  ## Using React?
18
18
 
19
- Use [`@mirasen/react-chessboard`](https://github.com/mirasen-io/react-chessboard) for a React component with the same built-in interaction, promotion, animation, and chess.js integration.
19
+ Use [`@mirasen/react-chessboard`](https://www.npmjs.com/package/@mirasen/react-chessboard) for a React component with the same built-in interaction, promotion, animation, and chess.js integration.
20
20
 
21
21
  ## Why this exists
22
22
 
@@ -1,7 +1,6 @@
1
- import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
2
1
  import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
3
2
  import type { AnimationTrackFade } from '../types.js';
4
- import type { PreparedFadeNode } from './types.js';
5
- export declare function prepareFadeTrack(track: AnimationTrackFade, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedFadeNode;
3
+ import type { PieceHrefResolver, PreparedFadeNode } from './types.js';
4
+ export declare function prepareFadeTrack(track: AnimationTrackFade, geometry: SceneRenderGeometry, resolveHref: PieceHrefResolver, slot: SVGGElement): PreparedFadeNode;
6
5
  export declare function renderFadeTrack(node: PreparedFadeNode, progress: number): void;
7
6
  export declare function cleanFadeTrack(node: PreparedFadeNode): void;
@@ -1,12 +1,12 @@
1
- import { createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
2
- export function prepareFadeTrack(track, geometry, pieceUrls, layer) {
1
+ import { createSvgElement, updateSvgElementAttributes } from '../../render/svg/helpers.js';
2
+ export function prepareFadeTrack(track, geometry, resolveHref, slot) {
3
3
  const r = geometry.getSquareRect(track.sq);
4
- const url = pieceUrls[track.pieceCode];
4
+ const href = resolveHref(track.pieceCode);
5
5
  const initialOpacity = track.effect === 'fade-in' ? '0' : '1';
6
- const root = createSvgElement(layer, 'image', {
6
+ const root = createSvgElement(slot, 'use', {
7
7
  'data-chessboard-id': `animation-fade-${track.id}`,
8
8
  'data-animation-effect': track.effect,
9
- href: url,
9
+ href,
10
10
  x: r.x.toString(),
11
11
  y: r.y.toString(),
12
12
  width: r.width.toString(),
@@ -17,7 +17,7 @@ export function prepareFadeTrack(track, geometry, pieceUrls, layer) {
17
17
  }
18
18
  export function renderFadeTrack(node, progress) {
19
19
  const opacity = node.effect === 'fade-in' ? progress : 1 - progress;
20
- updateElementAttributes(node.root, { opacity: opacity.toString() });
20
+ updateSvgElementAttributes(node.root, { opacity: opacity.toString() });
21
21
  }
22
22
  export function cleanFadeTrack(node) {
23
23
  node.root.remove();
@@ -1,7 +1,6 @@
1
- import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
2
1
  import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
3
2
  import type { AnimationTrackMove } from '../types.js';
4
- import type { PreparedMoveNode } from './types.js';
5
- export declare function prepareMoveTrack(track: AnimationTrackMove, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedMoveNode;
3
+ import type { PieceHrefResolver, PreparedMoveNode } from './types.js';
4
+ export declare function prepareMoveTrack(track: AnimationTrackMove, geometry: SceneRenderGeometry, resolveHref: PieceHrefResolver, slot: SVGGElement): PreparedMoveNode;
6
5
  export declare function renderMoveTrack(node: PreparedMoveNode, geometry: SceneRenderGeometry, progress: number): void;
7
6
  export declare function cleanMoveTrack(node: PreparedMoveNode): void;
@@ -1,11 +1,11 @@
1
- import { createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
2
- export function prepareMoveTrack(track, geometry, pieceUrls, layer) {
1
+ import { createSvgElement, updateSvgElementAttributes } from '../../render/svg/helpers.js';
2
+ export function prepareMoveTrack(track, geometry, resolveHref, slot) {
3
3
  const r = geometry.getSquareRect(track.fromSq);
4
- const url = pieceUrls[track.pieceCode];
5
- const root = createSvgElement(layer, 'image', {
4
+ const href = resolveHref(track.pieceCode);
5
+ const root = createSvgElement(slot, 'use', {
6
6
  'data-chessboard-id': `animation-move-${track.id}`,
7
7
  'data-animation-effect': 'move',
8
- href: url,
8
+ href,
9
9
  x: r.x.toString(),
10
10
  y: r.y.toString(),
11
11
  width: r.width.toString(),
@@ -18,7 +18,7 @@ export function renderMoveTrack(node, geometry, progress) {
18
18
  const to = geometry.getSquareRect(node.toSq);
19
19
  const x = from.x + (to.x - from.x) * progress;
20
20
  const y = from.y + (to.y - from.y) * progress;
21
- updateElementAttributes(node.root, {
21
+ updateSvgElementAttributes(node.root, {
22
22
  x: x.toString(),
23
23
  y: y.toString()
24
24
  });
@@ -1,12 +1,11 @@
1
- import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
2
1
  import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
3
2
  import type { AnimationPlan } from '../types.js';
4
- import type { PreparedNodeMap } from './types.js';
3
+ import type { PieceHrefResolver, PreparedNodeMap } from './types.js';
5
4
  /**
6
- * Prepare all tracks in the plan — creates SVG nodes in `layer` for each track.
5
+ * Prepare all tracks in the plan — creates SVG nodes in `slot` for each track.
7
6
  * Returns a map keyed by track id for use in render/clean passes.
8
7
  */
9
- export declare function prepareAnimationPlan(plan: AnimationPlan, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedNodeMap;
8
+ export declare function prepareAnimationPlan(plan: AnimationPlan, geometry: SceneRenderGeometry, resolveHref: PieceHrefResolver, slot: SVGGElement): PreparedNodeMap;
10
9
  export declare function renderAnimationPlan(nodes: PreparedNodeMap, geometry: SceneRenderGeometry, progress: number): void;
11
10
  /**
12
11
  * Clean up all track nodes — removes SVG elements from the DOM.
@@ -2,23 +2,23 @@ import { cleanFadeTrack, prepareFadeTrack, renderFadeTrack } from './fade.js';
2
2
  import { cleanMoveTrack, prepareMoveTrack, renderMoveTrack } from './move.js';
3
3
  import { cleanStaticTrack, prepareStaticTrack, renderStaticTrack } from './static.js';
4
4
  /**
5
- * Prepare all tracks in the plan — creates SVG nodes in `layer` for each track.
5
+ * Prepare all tracks in the plan — creates SVG nodes in `slot` for each track.
6
6
  * Returns a map keyed by track id for use in render/clean passes.
7
7
  */
8
- export function prepareAnimationPlan(plan, geometry, pieceUrls, layer) {
8
+ export function prepareAnimationPlan(plan, geometry, resolveHref, slot) {
9
9
  const nodes = new Map();
10
10
  for (const track of plan.tracks) {
11
11
  let node;
12
12
  switch (track.effect) {
13
13
  case 'move':
14
- node = prepareMoveTrack(track, geometry, pieceUrls, layer);
14
+ node = prepareMoveTrack(track, geometry, resolveHref, slot);
15
15
  break;
16
16
  case 'fade-in':
17
17
  case 'fade-out':
18
- node = prepareFadeTrack(track, geometry, pieceUrls, layer);
18
+ node = prepareFadeTrack(track, geometry, resolveHref, slot);
19
19
  break;
20
20
  case 'static':
21
- node = prepareStaticTrack(track, geometry, pieceUrls, layer);
21
+ node = prepareStaticTrack(track, geometry, resolveHref, slot);
22
22
  break;
23
23
  default:
24
24
  throw new RangeError(`Unsupported track effect: ${track}`);
@@ -1,7 +1,6 @@
1
- import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
2
1
  import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
3
2
  import type { AnimationTrackStatic } from '../types.js';
4
- import type { PreparedStaticNode } from './types.js';
5
- export declare function prepareStaticTrack(track: AnimationTrackStatic, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedStaticNode;
3
+ import type { PieceHrefResolver, PreparedStaticNode } from './types.js';
4
+ export declare function prepareStaticTrack(track: AnimationTrackStatic, geometry: SceneRenderGeometry, resolveHref: PieceHrefResolver, slot: SVGGElement): PreparedStaticNode;
6
5
  export declare function renderStaticTrack(_node: PreparedStaticNode): void;
7
6
  export declare function cleanStaticTrack(node: PreparedStaticNode): void;
@@ -1,11 +1,11 @@
1
1
  import { createSvgElement } from '../../render/svg/helpers.js';
2
- export function prepareStaticTrack(track, geometry, pieceUrls, layer) {
2
+ export function prepareStaticTrack(track, geometry, resolveHref, slot) {
3
3
  const r = geometry.getSquareRect(track.sq);
4
- const url = pieceUrls[track.pieceCode];
5
- const root = createSvgElement(layer, 'image', {
4
+ const href = resolveHref(track.pieceCode);
5
+ const root = createSvgElement(slot, 'use', {
6
6
  'data-chessboard-id': `animation-static-${track.id}`,
7
7
  'data-animation-effect': 'static',
8
- href: url,
8
+ href,
9
9
  x: r.x.toString(),
10
10
  y: r.y.toString(),
11
11
  width: r.width.toString(),
@@ -1,15 +1,21 @@
1
+ import type { NonEmptyPieceCode } from '../../state/board/types/internal.js';
1
2
  import type { AnimationTrack, AnimationTrackFade, AnimationTrackMove, AnimationTrackStatic } from '../types.js';
2
- /** Prepared node for a move track — holds the SVG image and its track. */
3
+ /**
4
+ * A function that resolves a piece code to a href string for <use> elements.
5
+ * Decoupled from main-renderer internals — any conforming callback works.
6
+ */
7
+ export type PieceHrefResolver = (pieceCode: NonEmptyPieceCode) => string;
8
+ /** Prepared node for a move track — holds the SVG use element and its track. */
3
9
  export interface PreparedMoveNode extends AnimationTrackMove {
4
- root: SVGImageElement;
10
+ root: SVGUseElement;
5
11
  }
6
12
  /** Prepared node for a fade-in / fade-out track. */
7
13
  export interface PreparedFadeNode extends AnimationTrackFade {
8
- root: SVGImageElement;
14
+ root: SVGUseElement;
9
15
  }
10
16
  /** Prepared node for a static (captured piece) track. */
11
17
  export interface PreparedStaticNode extends AnimationTrackStatic {
12
- root: SVGImageElement;
18
+ root: SVGUseElement;
13
19
  }
14
20
  export type PreparedTrackNode = PreparedMoveNode | PreparedFadeNode | PreparedStaticNode;
15
21
  /** Keyed map from track id → prepared node, for fast lookup during render/clean. */
@@ -1,5 +1,5 @@
1
1
  export { cleanAnimationPlan, prepareAnimationPlan, renderAnimationPlan } from '../../animation/render/plan.js';
2
- export { clearElementChildren, createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
2
+ export { clearSvgElementChildren, createSvgElement, createSvgRootElement, updateSvgElementAttributes } from '../../render/svg/helpers.js';
3
3
  export type { RuntimeReadonlyMutationSession } from '../../runtime/mutation/types.js';
4
4
  export { isNonEmptyPieceCode } from '../../state/board/check.js';
5
5
  export { fileOf, rankOf, squareOf } from '../../state/board/coords.js';
@@ -7,7 +7,6 @@ export { denormalizeMove } from '../../state/board/denormalize.js';
7
7
  export { normalizePiece } from '../../state/board/normalize.js';
8
8
  export { fromPieceCode, toPieceCode } from '../../state/board/piece.js';
9
9
  export type { ColorCode, EmptyPieceCode, NonEmptyPieceCode, PieceCode, RoleCode, RolePromotionCode, Square, SquareFile, SquareRank } from '../../state/board/types/internal.js';
10
- export type { MoveOutput } from '../../state/board/types/output.js';
11
10
  export type { MovabilityModeCode } from '../../state/interaction/types/internal.js';
12
11
  export { extensionCreateInternalBase, extensionDestroyBase, extensionIsDestroyedBase, extensionIsMountedBase, extensionMountBase, extensionUnmountBase } from '../first-party/common/helpers.js';
13
12
  export type { ExtensionInternalBase, OpaqueColor } from '../first-party/common/types.js';
@@ -1,5 +1,5 @@
1
1
  export { cleanAnimationPlan, prepareAnimationPlan, renderAnimationPlan } from '../../animation/render/plan.js';
2
- export { clearElementChildren, createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
2
+ export { clearSvgElementChildren, createSvgElement, createSvgRootElement, updateSvgElementAttributes } from '../../render/svg/helpers.js';
3
3
  export { isNonEmptyPieceCode } from '../../state/board/check.js';
4
4
  export { fileOf, rankOf, squareOf } from '../../state/board/coords.js';
5
5
  export { denormalizeMove } from '../../state/board/denormalize.js';
@@ -1,9 +1,11 @@
1
1
  import assert from '@ktarmyshov/assert';
2
+ import { createSvgIdResolver } from '../../render/svg/ids.js';
2
3
  import { createExtensionAnimationController } from '../animation/factory.js';
3
4
  import { createExtensionInvalidationState } from '../invalidation/factory.js';
4
5
  import { extensionSystemUpdateState, extensionSystemUpdateUIMoveRequest } from '../update.js';
5
6
  import { createExtensionRuntimeSurface } from './runtime.js';
6
7
  function createExtensionSystemInternal(getInternalState, options) {
8
+ const svgIds = createSvgIdResolver();
7
9
  const extensions = new Map();
8
10
  const extensionsArray = options.extensions ?? [];
9
11
  for (const extensionDef of extensionsArray) {
@@ -14,7 +16,8 @@ function createExtensionSystemInternal(getInternalState, options) {
14
16
  throw new Error(`Duplicate extension id found: ${extensionDef.id}`);
15
17
  }
16
18
  const instance = extensionDef.createInstance({
17
- runtimeSurface: createExtensionRuntimeSurface(getInternalState, options.extensionRuntimeSurfaceCommands, options.extensionRuntimeSurfaceEvents, extensionDef)
19
+ runtimeSurface: createExtensionRuntimeSurface(getInternalState, options.extensionRuntimeSurfaceCommands, options.extensionRuntimeSurfaceEvents, extensionDef),
20
+ svgIds
18
21
  });
19
22
  const record = {
20
23
  id: extensionDef.id,
@@ -93,23 +96,24 @@ export function createExtensionSystem(options) {
93
96
  assert(extensionRec.instance.completeDrag, `Extension instance does not have a completeDrag handler for completing drag session with owner ${session.owner}`);
94
97
  extensionRec.instance.completeDrag(session);
95
98
  },
99
+ cancelDrag(session) {
100
+ const extensionRec = internalState.extensions.get(session.owner);
101
+ assert(extensionRec, `Extension record not found for cancelling drag session with owner ${session.owner}`);
102
+ extensionRec.instance.cancelDrag?.(session);
103
+ },
96
104
  onUnmount() {
97
105
  internalState.transientVisualsSubscribers.clear();
98
106
  internalState.eventSubscribers.clear();
99
107
  internalState.currentFrame = null;
100
108
  for (const extensionRec of internalState.extensions.values()) {
101
- extensionRec.invalidation.clear();
102
109
  extensionRec.animation.clear();
103
110
  }
104
111
  },
105
112
  onDestroy() {
106
- // We assume that the onUnmout was already called by runtime, but let's still validate
113
+ // We assume that the onUnmount was already called by runtime, but let's still validate
107
114
  for (const extensionRec of internalState.extensions.values()) {
108
115
  extensionRec.instance.destroy?.();
109
- const onUnmountCalled = [
110
- extensionRec.invalidation.dirtyLayers === 0,
111
- extensionRec.animation.getAll().length === 0
112
- ].every(Boolean);
116
+ const onUnmountCalled = extensionRec.animation.getAll().length === 0;
113
117
  if (!onUnmountCalled) {
114
118
  throw new Error(`Extension ${extensionRec.id} was not properly unmounted before destroy. Please make sure to call onUnmount before onDestroy to allow the extension to clean up its state and resources.`);
115
119
  }
@@ -80,11 +80,34 @@ function createExtensionRuntimeSurfaceCommands(getInternalState, runtimeSurfaceC
80
80
  }
81
81
  };
82
82
  }
83
+ function createExtensionRuntimeSurfaceInvalidation(getInternalState, extensionDef) {
84
+ function getInvalidation() {
85
+ const internalState = getInternalState();
86
+ const record = internalState.extensions.get(extensionDef.id);
87
+ assert(record, 'Extension record not found for invalidation');
88
+ return record.invalidation;
89
+ }
90
+ return {
91
+ get dirtyLayers() {
92
+ return getInvalidation().dirtyLayers;
93
+ },
94
+ markDirty(layers) {
95
+ getInvalidation().markDirty(layers);
96
+ },
97
+ clearDirty(layers) {
98
+ getInvalidation().clearDirty(layers);
99
+ },
100
+ clear() {
101
+ getInvalidation().clear();
102
+ }
103
+ };
104
+ }
83
105
  export function createExtensionRuntimeSurface(getInternalState, commands, events, extensionDef) {
84
106
  return {
85
107
  commands: createExtensionRuntimeSurfaceCommands(getInternalState, commands, extensionDef),
86
108
  animation: createExtensionRuntimeSurfaceAnimation(getInternalState, extensionDef),
87
109
  transientVisuals: createExtensionRuntimeSurfaceTransientVisuals(getInternalState, extensionDef),
88
- events: createExtensionRuntimeSurfaceEvents(getInternalState, events, extensionDef)
110
+ events: createExtensionRuntimeSurfaceEvents(getInternalState, events, extensionDef),
111
+ invalidation: createExtensionRuntimeSurfaceInvalidation(getInternalState, extensionDef)
89
112
  };
90
113
  }
@@ -1,6 +1,6 @@
1
1
  import assert from '@ktarmyshov/assert';
2
2
  import { toMerged } from 'es-toolkit';
3
- import { createSvgElement, updateElementAttributes } from '../../../render/svg/helpers.js';
3
+ import { createSvgElement, updateSvgElementAttributes } from '../../../render/svg/helpers.js';
4
4
  import { isUpdateContextRenderable } from '../../types/context/update.js';
5
5
  import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
6
6
  import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
@@ -9,14 +9,14 @@ export function createActiveTarget(config = {}) {
9
9
  return {
10
10
  id: EXTENSION_ID,
11
11
  slots: EXTENSION_SLOTS,
12
- createInstance() {
13
- return createActiveTargetInstance(mergedConfig);
12
+ createInstance(options) {
13
+ return createActiveTargetInstance(options, mergedConfig);
14
14
  }
15
15
  };
16
16
  }
17
- function createActiveTargetInternal(config) {
17
+ function createActiveTargetInternal(options, config) {
18
18
  return {
19
- ...extensionCreateInternalBase(),
19
+ ...extensionCreateInternalBase(options),
20
20
  svgRect: null,
21
21
  svgCircle: null,
22
22
  config
@@ -26,8 +26,8 @@ function extensionClean(state) {
26
26
  state.svgRect = null;
27
27
  state.svgCircle = null;
28
28
  }
29
- function createActiveTargetInstance(config) {
30
- const internalState = createActiveTargetInternal(config);
29
+ function createActiveTargetInstance(options, config) {
30
+ const internalState = createActiveTargetInternal(options, config);
31
31
  return {
32
32
  id: EXTENSION_ID,
33
33
  mount(env) {
@@ -38,7 +38,11 @@ function createActiveTargetInstance(config) {
38
38
  causes: ['layout.refreshGeometry'],
39
39
  // we really need almost all: setDrag, updateTarget, clear, clearActive, so just take all interaction mutations
40
40
  prefixes: ['state.interaction.']
41
- }) && isUpdateContextRenderable(context);
41
+ }) &&
42
+ isUpdateContextRenderable(context) &&
43
+ (context.currentFrame.state.interaction.dragSession?.type === 'lifted-piece-drag' ||
44
+ context.currentFrame.state.interaction.dragSession?.type === 'release-targeting' ||
45
+ context.currentFrame.state.interaction.dragSession === null);
42
46
  if (!needsRender) {
43
47
  return; // no-op
44
48
  }
@@ -48,7 +52,10 @@ function createActiveTargetInstance(config) {
48
52
  assert(context.invalidation.dirtyLayers !== 0, 'Render should only be called when there are dirty layers');
49
53
  // For AI: `release-targeting` is also represented by `dragSession`.
50
54
  // `dragSession` here is the active interaction session, not only lifted-piece drag.
51
- const square = context.currentFrame.state.interaction.dragSession?.targetSquare;
55
+ const square = context.currentFrame.state.interaction.dragSession?.type === 'lifted-piece-drag' ||
56
+ context.currentFrame.state.interaction.dragSession?.type === 'release-targeting'
57
+ ? context.currentFrame.state.interaction.dragSession.targetSquare
58
+ : null;
52
59
  if (square === undefined || square === null) {
53
60
  if (internalState.svgRect !== null) {
54
61
  internalState.svgRect.remove();
@@ -92,9 +99,9 @@ function createActiveTargetInstance(config) {
92
99
  });
93
100
  }
94
101
  else {
95
- updateElementAttributes(internalState.svgRect, rectAttributes);
102
+ updateSvgElementAttributes(internalState.svgRect, rectAttributes);
96
103
  assert(internalState.svgCircle, 'svgCircle should be available if svgRect is available');
97
- updateElementAttributes(internalState.svgCircle, haloAttributes);
104
+ updateSvgElementAttributes(internalState.svgCircle, haloAttributes);
98
105
  }
99
106
  },
100
107
  unmount() {
@@ -0,0 +1,16 @@
1
+ import type { SquareString } from '../../../state/board/types/input.js';
2
+ import type { AnnotationsStateInternal } from './types/main.js';
3
+ import type { AnnotationsDrawModifier, ArrowAnnotationPublic, ArrowAnnotationValuePublic, CircleAnnotationPublic, CircleAnnotationValuePublic } from './types/public.js';
4
+ export declare function annotationsGetCircles(state: AnnotationsStateInternal): CircleAnnotationPublic[];
5
+ export declare function annotationsGetArrows(state: AnnotationsStateInternal): ArrowAnnotationPublic[];
6
+ export declare function annotationsSetCircles(state: AnnotationsStateInternal, circles: CircleAnnotationPublic[]): void;
7
+ export declare function annotationsSetArrows(state: AnnotationsStateInternal, arrows: ArrowAnnotationPublic[]): void;
8
+ export declare function annotationsSetCircle(state: AnnotationsStateInternal, square: SquareString, annotation: CircleAnnotationValuePublic | null): void;
9
+ export declare function annotationsSetArrow(state: AnnotationsStateInternal, from: SquareString, to: SquareString, annotation: ArrowAnnotationValuePublic | null): void;
10
+ export declare function annotationsClear(state: AnnotationsStateInternal): void;
11
+ export declare function annotationsSetClearOnCoreInteraction(state: AnnotationsStateInternal, value: boolean): void;
12
+ export declare function annotationsGetClearOnCoreInteraction(state: AnnotationsStateInternal): boolean;
13
+ export declare function annotationsSetDrawButton(state: AnnotationsStateInternal, value: number): void;
14
+ export declare function annotationsGetDrawButton(state: AnnotationsStateInternal): number;
15
+ export declare function annotationsSetDrawModifier(state: AnnotationsStateInternal, value: AnnotationsDrawModifier | null): void;
16
+ export declare function annotationsGetDrawModifier(state: AnnotationsStateInternal): AnnotationsDrawModifier | null;
@@ -0,0 +1,79 @@
1
+ import { normalizeSquare } from '../../../state/board/normalize.js';
2
+ import { denormalizeArrows, denormalizeCircles } from './denormalize.js';
3
+ import { markCommittedDirtyAndRequestRender } from './invalidation.js';
4
+ import { arrowAnnotationKey, normalizeArrowAnnotation, normalizeCircleAnnotation } from './normalize.js';
5
+ export function annotationsGetCircles(state) {
6
+ return denormalizeCircles(state.annotations.circles);
7
+ }
8
+ export function annotationsGetArrows(state) {
9
+ return denormalizeArrows(state.annotations.arrows);
10
+ }
11
+ export function annotationsSetCircles(state, circles) {
12
+ state.annotations.circles.clear();
13
+ for (const pub of circles) {
14
+ const circle = normalizeCircleAnnotation(pub);
15
+ state.annotations.circles.set(circle.key, circle);
16
+ }
17
+ markCommittedDirtyAndRequestRender(state);
18
+ }
19
+ export function annotationsSetArrows(state, arrows) {
20
+ // Validate all entries before mutating state (atomic behavior)
21
+ const normalized = arrows.map((pub) => normalizeArrowAnnotation(pub));
22
+ state.annotations.arrows.clear();
23
+ for (const arrow of normalized) {
24
+ state.annotations.arrows.set(arrow.key, arrow);
25
+ }
26
+ markCommittedDirtyAndRequestRender(state);
27
+ }
28
+ export function annotationsSetCircle(state, square, annotation) {
29
+ const key = normalizeSquare(square);
30
+ if (annotation === null) {
31
+ state.annotations.circles.delete(key);
32
+ }
33
+ else {
34
+ const normalizedAnnotation = normalizeCircleAnnotation({
35
+ square,
36
+ color: annotation.color
37
+ });
38
+ state.annotations.circles.set(key, normalizedAnnotation);
39
+ }
40
+ markCommittedDirtyAndRequestRender(state);
41
+ }
42
+ export function annotationsSetArrow(state, from, to, annotation) {
43
+ const key = arrowAnnotationKey(from, to);
44
+ if (annotation === null) {
45
+ state.annotations.arrows.delete(key);
46
+ }
47
+ else {
48
+ const normalizedArrow = normalizeArrowAnnotation({
49
+ from,
50
+ to,
51
+ color: annotation.color
52
+ });
53
+ state.annotations.arrows.set(key, normalizedArrow);
54
+ }
55
+ markCommittedDirtyAndRequestRender(state);
56
+ }
57
+ export function annotationsClear(state) {
58
+ state.annotations.circles.clear();
59
+ state.annotations.arrows.clear();
60
+ markCommittedDirtyAndRequestRender(state);
61
+ }
62
+ export function annotationsSetClearOnCoreInteraction(state, value) {
63
+ state.config.clearOnCoreInteraction = value;
64
+ }
65
+ export function annotationsGetClearOnCoreInteraction(state) {
66
+ return state.config.clearOnCoreInteraction;
67
+ }
68
+ export function annotationsSetDrawButton(state, value) {
69
+ state.config.drawButton = value;
70
+ }
71
+ export function annotationsGetDrawButton(state) {
72
+ return state.config.drawButton;
73
+ }
74
+ export function annotationsSetDrawModifier(state, value) {
75
+ state.config.drawModifier = value;
76
+ }
77
+ export function annotationsGetDrawModifier(state) {
78
+ return state.config.drawModifier;
79
+ }
@@ -0,0 +1,3 @@
1
+ import type { AnnotationsStateInternal } from './types/main.js';
2
+ export declare function hasCommittedAnnotations(state: AnnotationsStateInternal): boolean;
3
+ export declare function clearCommittedAnnotations(state: AnnotationsStateInternal): boolean;
@@ -0,0 +1,11 @@
1
+ export function hasCommittedAnnotations(state) {
2
+ return state.annotations.circles.size > 0 || state.annotations.arrows.size > 0;
3
+ }
4
+ export function clearCommittedAnnotations(state) {
5
+ if (state.annotations.circles.size === 0 && state.annotations.arrows.size === 0) {
6
+ return false;
7
+ }
8
+ state.annotations.circles.clear();
9
+ state.annotations.arrows.clear();
10
+ return true;
11
+ }
@@ -0,0 +1,3 @@
1
+ import type { AnnotationsConfig, VisualConfig } from './types/internal.js';
2
+ export declare const DEFAULT_CONFIG: AnnotationsConfig;
3
+ export declare const VISUAL_CONFIG: VisualConfig;
@@ -0,0 +1,54 @@
1
+ export const DEFAULT_CONFIG = {
2
+ clearOnCoreInteraction: true,
3
+ colors: {
4
+ none: '#15781B',
5
+ ctrl: '#882020',
6
+ shift: '#e68f00',
7
+ alt: '#003088',
8
+ meta: '#6f2da8'
9
+ },
10
+ drawButton: 2,
11
+ drawModifier: null
12
+ };
13
+ export const VISUAL_CONFIG = {
14
+ circle: {
15
+ committed: {
16
+ strokeWidth: 0.0625,
17
+ radius: 0.46875,
18
+ opacity: 1
19
+ },
20
+ previewAdd: {
21
+ strokeWidth: 0.05,
22
+ radius: 0.46875,
23
+ opacity: 0.8
24
+ },
25
+ previewRemoveOpacity: 0.25
26
+ },
27
+ arrow: {
28
+ committed: {
29
+ strokeWidth: 0.15625,
30
+ startOffset: 0,
31
+ endOffset: 0.15625,
32
+ opacity: 0.75,
33
+ markerWidth: 4,
34
+ markerHeight: 4,
35
+ markerRefX: 2.05,
36
+ markerRefY: 2,
37
+ markerViewBox: '0 0 4 4',
38
+ markerPathD: 'M0,0 V4 L3,2 Z'
39
+ },
40
+ previewAdd: {
41
+ strokeWidth: 0.11,
42
+ startOffset: 0,
43
+ endOffset: 0.15625,
44
+ opacity: 0.55,
45
+ markerWidth: 4,
46
+ markerHeight: 4,
47
+ markerRefX: 2.05,
48
+ markerRefY: 2,
49
+ markerViewBox: '0 0 4 4',
50
+ markerPathD: 'M0,0 V4 L3,2 Z'
51
+ },
52
+ previewRemoveOpacity: 0.25
53
+ }
54
+ };
@@ -0,0 +1,4 @@
1
+ import type { ArrowAnnotation, ArrowAnnotationKey, CircleAnnotation, CircleAnnotationKey } from './types/internal.js';
2
+ import type { ArrowAnnotationPublic, CircleAnnotationPublic } from './types/public.js';
3
+ export declare function denormalizeCircles(circles: Map<CircleAnnotationKey, CircleAnnotation>): CircleAnnotationPublic[];
4
+ export declare function denormalizeArrows(arrows: Map<ArrowAnnotationKey, ArrowAnnotation>): ArrowAnnotationPublic[];
@@ -0,0 +1,22 @@
1
+ import { denormalizeSquare } from '../../../state/board/denormalize.js';
2
+ export function denormalizeCircles(circles) {
3
+ const result = [];
4
+ for (const circle of circles.values()) {
5
+ result.push({
6
+ square: denormalizeSquare(circle.square),
7
+ color: circle.color
8
+ });
9
+ }
10
+ return result;
11
+ }
12
+ export function denormalizeArrows(arrows) {
13
+ const result = [];
14
+ for (const arrow of arrows.values()) {
15
+ result.push({
16
+ from: denormalizeSquare(arrow.from),
17
+ to: denormalizeSquare(arrow.to),
18
+ color: arrow.color
19
+ });
20
+ }
21
+ return result;
22
+ }
@@ -0,0 +1,3 @@
1
+ import type { AnnotationsDefinition } from './types/main.js';
2
+ import type { AnnotationsInitOptions } from './types/public.js';
3
+ export declare function createAnnotations(options?: AnnotationsInitOptions): AnnotationsDefinition;