@mirasen/chessboard 1.2.2 → 1.2.3

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,15 @@
1
1
  # @mirasen/chessboard
2
2
 
3
+ ## 1.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - d8df60e: Fix stale lifted-piece drag cleanup when pointer capture is lost.
8
+
9
+ Fast drag gestures released outside the board could leave an active lifted-piece drag session alive if the browser delivered `lostpointercapture` instead of a normal `pointerup`. The input adapter now handles `lostpointercapture`, clears stale pointer tracking, and routes the event through the interaction controller so active drag sessions are cancelled cleanly.
10
+
11
+ This prevents dragged pieces from remaining visually attached to the pointer after an outside-board release.
12
+
3
13
  ## 1.2.2
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -319,16 +319,35 @@ board = createBoard({
319
319
 
320
320
  Configured built-in extensions can be provided either as declarative `{ builtin, options }` objects or through the built-in extension factory map. If you use custom piece assets, pass the same `pieceUrls` map to renderer and promotion when you want the board and promotion UI to use the same piece set.
321
321
 
322
- ### Default visual identity
322
+ ### Default visual identity and attribution
323
323
 
324
324
  The default first-party extension baseline includes a subtle Mirasen board watermark.
325
325
 
326
326
  If you want full control over the visual baseline, use an explicit extension list and omit the `watermark` extension.
327
327
 
328
+ Attribution is not required by the MIT license, but it is appreciated. If Mirasen Chessboard helps your project, please consider keeping the default watermark or linking to `@mirasen/chessboard`.
329
+
328
330
  ## Documentation
329
331
 
330
332
  See [Documentation](./documentation.md) for the documentation hub.
331
333
 
334
+ ## Versioning and breaking changes
335
+
336
+ `@mirasen/chessboard` follows semantic versioning for public API changes.
337
+
338
+ Breaking public API changes are released as major versions.
339
+
340
+ We do not add backward-compatibility aliases or runtime compatibility bridges by default. When a public contract changes, the new API is kept explicit and clean, and the required migration is documented in the release notes / changelog.
341
+
342
+ Migration notes for breaking changes are included directly in the corresponding changelog entry, so users can see:
343
+
344
+ - what changed
345
+ - why it changed
346
+ - what old usage looked like
347
+ - what new usage should look like
348
+
349
+ See the [CHANGELOG](./CHANGELOG.md) for release history and migration notes.
350
+
332
351
  ## Architecture
333
352
 
334
353
  `@mirasen/chessboard` is built around explicit boundaries:
@@ -68,7 +68,7 @@ function createLegalMovesInstance(options, config) {
68
68
  'stroke-opacity': internalState.config.captureTarget.color.opacity.toString(),
69
69
  'stroke-width': (geometry.squareSize * internalState.config.captureTarget.strokeWidthRatio).toString(),
70
70
  fill: 'none'
71
- }; // TODO: Remove, this is just for visual test
71
+ };
72
72
  for (const [square, destination] of interaction.activeDestinations) {
73
73
  const rect = geometry.getSquareRect(square);
74
74
  const circleX = rect.x + geometry.squareSize / 2;
@@ -1,13 +1,15 @@
1
1
  import type { Square } from '../../../state/board/types/internal.js';
2
2
  import type { ScenePoint } from '../basic/transient-visuals.js';
3
+ declare const ALL_SCENE_POINTER_EVENT_TYPES_ARRAY: readonly ["pointercancel", "pointerdown", "pointerenter", "pointerleave", "pointermove", "pointerout", "pointerover", "pointerrawupdate", "pointerup", "contextmenu", "lostpointercapture"];
4
+ export type ScenePointerEventType = (typeof ALL_SCENE_POINTER_EVENT_TYPES_ARRAY)[number];
5
+ export declare const ALL_SCENE_POINTER_EVENT_TYPES: ReadonlySet<string>;
3
6
  export interface ScenePointerEvent {
4
- readonly type: 'pointercancel' | 'pointerdown' | 'pointerenter' | 'pointerleave' | 'pointermove' | 'pointerout' | 'pointerover' | 'pointerrawupdate' | 'pointerup' | 'contextmenu';
7
+ readonly type: ScenePointerEventType;
5
8
  readonly point: ScenePoint;
6
9
  readonly clampedPoint: ScenePoint;
7
10
  readonly boardClampedPoint: ScenePoint | null;
8
11
  readonly targetSquare: Square | null;
9
12
  }
10
- export declare const ALL_SCENE_POINTER_EVENT_TYPES: ReadonlySet<string>;
11
13
  export type SceneEvent = ScenePointerEvent;
12
14
  export type RuntimeInteractionActionStartLiftedDrag = {
13
15
  type: 'startLiftedDrag';
@@ -38,3 +40,4 @@ export type RuntimeInteractionActionCancelInteraction = {
38
40
  type: 'cancelInteraction';
39
41
  };
40
42
  export type RuntimeInteractionAction = RuntimeInteractionActionStartLiftedDrag | RuntimeInteractionActionStartReleaseTargetingDrag | RuntimeInteractionActionCompleteCoreDrag | RuntimeInteractionActionCompleteExtensionDrag | RuntimeInteractionActionUpdateDragSessionCurrentTarget | RuntimeInteractionActionCancelActiveInteraction | RuntimeInteractionActionCancelInteraction;
43
+ export {};
@@ -1,4 +1,4 @@
1
- export const ALL_SCENE_POINTER_EVENT_TYPES = new Set([
1
+ const ALL_SCENE_POINTER_EVENT_TYPES_ARRAY = [
2
2
  'pointercancel',
3
3
  'pointerdown',
4
4
  'pointerenter',
@@ -8,5 +8,7 @@ export const ALL_SCENE_POINTER_EVENT_TYPES = new Set([
8
8
  'pointerover',
9
9
  'pointerrawupdate',
10
10
  'pointerup',
11
- 'contextmenu'
12
- ]);
11
+ 'contextmenu',
12
+ 'lostpointercapture'
13
+ ];
14
+ export const ALL_SCENE_POINTER_EVENT_TYPES = new Set(ALL_SCENE_POINTER_EVENT_TYPES_ARRAY);
@@ -195,11 +195,6 @@ export function createRuntime(options) {
195
195
  internalState = createRuntimeInternal(optionsInternal);
196
196
  // Initial creation, so we mark board position as mutated to trigger mutation pipeline
197
197
  internalState.mutation.addMutation('state.board.setPosition', true);
198
- // We will have two different interfaces here
199
- // One is returned, one is for controller - with different methods
200
- // At the moment our assumption that controller will use Runtime but not opposite direction
201
- // So we can construct these two objects separately so they would not be huge!
202
- // TODO: const inputControllerSurface = createRuntimeInputControllerSurface(internalState);
203
198
  // Initial run to process initial mutations and set up previousContext for the next runs
204
199
  internalStatus = 'unmounted';
205
200
  internalState.mutation.run(internalState);
@@ -6,4 +6,5 @@ export declare function releaseCapture(state: InputAdapterInternal): void;
6
6
  export declare function adapterOnPointerDown(state: InputAdapterInternal, e: PointerEvent): void;
7
7
  export declare function adapterOnPointerUp(state: InputAdapterInternal, e: PointerEvent): void;
8
8
  export declare function adapterOnPointerCancel(state: InputAdapterInternal, e: PointerEvent): void;
9
+ export declare function adapterOnLostPointerCapture(state: InputAdapterInternal, e: PointerEvent): void;
9
10
  export declare function pointerEventDestroy(state: InputAdapterInternal): void;
@@ -24,6 +24,9 @@ function pointerEventAdapterHandler(state, e) {
24
24
  case 'pointercancel':
25
25
  adapterOnPointerCancel(state, e);
26
26
  break;
27
+ case 'lostpointercapture':
28
+ adapterOnLostPointerCapture(state, e);
29
+ break;
27
30
  }
28
31
  }
29
32
  /** Release capture for the currently tracked pointer and clear tracking state. */
@@ -56,6 +59,12 @@ export function adapterOnPointerCancel(state, e) {
56
59
  return;
57
60
  releaseCapture(state);
58
61
  }
62
+ export function adapterOnLostPointerCapture(state, e) {
63
+ assert(e.type === 'lostpointercapture', `Expected lostpointercapture event, got ${e.type}`);
64
+ if (e.pointerId !== state.activePointerId)
65
+ return;
66
+ releaseCapture(state);
67
+ }
59
68
  export function pointerEventDestroy(state) {
60
69
  if (state.activePointerId !== null) {
61
70
  releaseCapture(state);
@@ -2,5 +2,6 @@ export const NEED_EVENT_TYPES = new Set([
2
2
  'pointerdown',
3
3
  'pointermove',
4
4
  'pointerup',
5
- 'pointercancel'
5
+ 'pointercancel',
6
+ 'lostpointercapture'
6
7
  ]);
@@ -1,4 +1,4 @@
1
- import { determineActionPointerCancel, determineActionPointerDown, determineActionPointerMove, determineActionPointerUp } from './pointer.js';
1
+ import { determineActionLostPointerCapture, determineActionPointerCancel, determineActionPointerDown, determineActionPointerMove, determineActionPointerUp } from './pointer.js';
2
2
  export function determineRuntimeInteractionAction(state, context) {
3
3
  switch (context.rawEvent.type) {
4
4
  case 'pointerdown':
@@ -9,6 +9,8 @@ export function determineRuntimeInteractionAction(state, context) {
9
9
  return determineActionPointerUp(state, context);
10
10
  case 'pointercancel':
11
11
  return determineActionPointerCancel(state, context);
12
+ case 'lostpointercapture':
13
+ return determineActionLostPointerCapture(state, context);
12
14
  }
13
15
  return null;
14
16
  }
@@ -4,3 +4,4 @@ export declare function determineActionPointerDown(state: InteractionControllerI
4
4
  export declare function determineActionPointerMove(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
5
5
  export declare function determineActionPointerUp(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
6
6
  export declare function determineActionPointerCancel(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
7
+ export declare function determineActionLostPointerCapture(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
@@ -129,3 +129,15 @@ export function determineActionPointerCancel(state, context) {
129
129
  type: 'cancelActiveInteraction'
130
130
  };
131
131
  }
132
+ export function determineActionLostPointerCapture(state, context) {
133
+ assert(context.rawEvent.type === 'lostpointercapture', 'determineActionLostPointerCapture should only be called for lostpointercapture events');
134
+ const interaction = state.surface.getInteractionStateSnapshot();
135
+ if (!interaction.dragSession) {
136
+ // No active drag session, so nothing to do on lost pointer capture
137
+ return null;
138
+ }
139
+ // Cancel the active interaction on lost pointer capture
140
+ return {
141
+ type: 'cancelActiveInteraction'
142
+ };
143
+ }
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.2.2",
4
+ "version": "1.2.3",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Mirasen",
@@ -16,8 +16,13 @@
16
16
  "typescript",
17
17
  "framework-agnostic",
18
18
  "svg",
19
- "extensions",
20
- "chessboard-platform"
19
+ "drag-and-drop",
20
+ "click-to-move",
21
+ "animation",
22
+ "annotations",
23
+ "chessjs",
24
+ "legal-moves",
25
+ "touch"
21
26
  ],
22
27
  "homepage": "https://mirasen.io/chessboard",
23
28
  "repository": {
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- export {};
2
- // TODO: Mark dirty layers based on upstream changes.