@mirasen/chessboard 0.1.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.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/assets/pieces/chessnut/ATTRIBUTION.md +13 -0
- package/assets/pieces/chessnut/LICENSE.txt +202 -0
- package/assets/pieces/chessnut/bB.svg +58 -0
- package/assets/pieces/chessnut/bK.svg +51 -0
- package/assets/pieces/chessnut/bN.svg +19 -0
- package/assets/pieces/chessnut/bP.svg +17 -0
- package/assets/pieces/chessnut/bQ.svg +97 -0
- package/assets/pieces/chessnut/bR.svg +34 -0
- package/assets/pieces/chessnut/wB.svg +61 -0
- package/assets/pieces/chessnut/wK.svg +71 -0
- package/assets/pieces/chessnut/wN.svg +19 -0
- package/assets/pieces/chessnut/wP.svg +17 -0
- package/assets/pieces/chessnut/wQ.svg +40 -0
- package/assets/pieces/chessnut/wR.svg +38 -0
- package/dist/animation/planner.d.ts +6 -0
- package/dist/animation/planner.js +128 -0
- package/dist/animation/render/fade.d.ts +7 -0
- package/dist/animation/render/fade.js +24 -0
- package/dist/animation/render/move.d.ts +7 -0
- package/dist/animation/render/move.js +28 -0
- package/dist/animation/render/plan.d.ts +14 -0
- package/dist/animation/render/plan.js +69 -0
- package/dist/animation/render/static.d.ts +7 -0
- package/dist/animation/render/static.js +21 -0
- package/dist/animation/render/types.d.ts +21 -0
- package/dist/animation/render/types.js +1 -0
- package/dist/animation/types.d.ts +46 -0
- package/dist/animation/types.js +6 -0
- package/dist/extensions/animation/factory.d.ts +3 -0
- package/dist/extensions/animation/factory.js +88 -0
- package/dist/extensions/animation/types.d.ts +11 -0
- package/dist/extensions/animation/types.js +1 -0
- package/dist/extensions/factory/main.d.ts +2 -0
- package/dist/extensions/factory/main.js +101 -0
- package/dist/extensions/factory/runtime.d.ts +5 -0
- package/dist/extensions/factory/runtime.js +43 -0
- package/dist/extensions/first-party/active-target/factory.d.ts +2 -0
- package/dist/extensions/first-party/active-target/factory.js +109 -0
- package/dist/extensions/first-party/active-target/types.d.ts +25 -0
- package/dist/extensions/first-party/active-target/types.js +15 -0
- package/dist/extensions/first-party/board-events/factory.d.ts +2 -0
- package/dist/extensions/first-party/board-events/factory.js +65 -0
- package/dist/extensions/first-party/board-events/types.d.ts +27 -0
- package/dist/extensions/first-party/board-events/types.js +2 -0
- package/dist/extensions/first-party/common/helpers.d.ts +8 -0
- package/dist/extensions/first-party/common/helpers.js +33 -0
- package/dist/extensions/first-party/common/types.d.ts +10 -0
- package/dist/extensions/first-party/common/types.js +1 -0
- package/dist/extensions/first-party/last-move/factory.d.ts +2 -0
- package/dist/extensions/first-party/last-move/factory.js +106 -0
- package/dist/extensions/first-party/last-move/types.d.ts +19 -0
- package/dist/extensions/first-party/last-move/types.js +6 -0
- package/dist/extensions/first-party/legal-moves/factory.d.ts +2 -0
- package/dist/extensions/first-party/legal-moves/factory.js +101 -0
- package/dist/extensions/first-party/legal-moves/types.d.ts +29 -0
- package/dist/extensions/first-party/legal-moves/types.js +19 -0
- package/dist/extensions/first-party/main-renderer/animation/factory.d.ts +4 -0
- package/dist/extensions/first-party/main-renderer/animation/factory.js +35 -0
- package/dist/extensions/first-party/main-renderer/animation/render.d.ts +7 -0
- package/dist/extensions/first-party/main-renderer/animation/render.js +46 -0
- package/dist/extensions/first-party/main-renderer/animation/types.d.ts +24 -0
- package/dist/extensions/first-party/main-renderer/animation/types.js +1 -0
- package/dist/extensions/first-party/main-renderer/animation/update.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/animation/update.js +40 -0
- package/dist/extensions/first-party/main-renderer/board/factory.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/board/factory.js +16 -0
- package/dist/extensions/first-party/main-renderer/board/render.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/board/render.js +24 -0
- package/dist/extensions/first-party/main-renderer/board/types.d.ts +10 -0
- package/dist/extensions/first-party/main-renderer/board/types.js +1 -0
- package/dist/extensions/first-party/main-renderer/board/update.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/board/update.js +27 -0
- package/dist/extensions/first-party/main-renderer/coordinates/factory.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/coordinates/factory.js +9 -0
- package/dist/extensions/first-party/main-renderer/coordinates/render.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/coordinates/render.js +60 -0
- package/dist/extensions/first-party/main-renderer/coordinates/types.d.ts +8 -0
- package/dist/extensions/first-party/main-renderer/coordinates/types.js +1 -0
- package/dist/extensions/first-party/main-renderer/drag/factory.d.ts +4 -0
- package/dist/extensions/first-party/main-renderer/drag/factory.js +23 -0
- package/dist/extensions/first-party/main-renderer/drag/render.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/drag/render.js +27 -0
- package/dist/extensions/first-party/main-renderer/drag/types.d.ts +16 -0
- package/dist/extensions/first-party/main-renderer/drag/types.js +1 -0
- package/dist/extensions/first-party/main-renderer/drag/update.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/drag/update.js +23 -0
- package/dist/extensions/first-party/main-renderer/factory.d.ts +2 -0
- package/dist/extensions/first-party/main-renderer/factory.js +95 -0
- package/dist/extensions/first-party/main-renderer/normalize.d.ts +4 -0
- package/dist/extensions/first-party/main-renderer/normalize.js +27 -0
- package/dist/extensions/first-party/main-renderer/pieces/factory.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/pieces/factory.js +27 -0
- package/dist/extensions/first-party/main-renderer/pieces/render.d.ts +3 -0
- package/dist/extensions/first-party/main-renderer/pieces/render.js +45 -0
- package/dist/extensions/first-party/main-renderer/pieces/suppress.d.ts +5 -0
- package/dist/extensions/first-party/main-renderer/pieces/suppress.js +9 -0
- package/dist/extensions/first-party/main-renderer/pieces/types.d.ts +20 -0
- package/dist/extensions/first-party/main-renderer/pieces/types.js +1 -0
- package/dist/extensions/first-party/main-renderer/pieces/update.d.ts +6 -0
- package/dist/extensions/first-party/main-renderer/pieces/update.js +31 -0
- package/dist/extensions/first-party/main-renderer/types/extension.d.ts +15 -0
- package/dist/extensions/first-party/main-renderer/types/extension.js +10 -0
- package/dist/extensions/first-party/main-renderer/types/input.d.ts +4 -0
- package/dist/extensions/first-party/main-renderer/types/input.js +1 -0
- package/dist/extensions/first-party/main-renderer/types/instance.d.ts +16 -0
- package/dist/extensions/first-party/main-renderer/types/instance.js +1 -0
- package/dist/extensions/first-party/main-renderer/types/internal.d.ts +17 -0
- package/dist/extensions/first-party/main-renderer/types/internal.js +30 -0
- package/dist/extensions/first-party/main-renderer/types/template.d.ts +5 -0
- package/dist/extensions/first-party/main-renderer/types/template.js +1 -0
- package/dist/extensions/first-party/selected-square/factory.d.ts +2 -0
- package/dist/extensions/first-party/selected-square/factory.js +88 -0
- package/dist/extensions/first-party/selected-square/types.d.ts +18 -0
- package/dist/extensions/first-party/selected-square/types.js +6 -0
- package/dist/extensions/index.d.ts +8 -0
- package/dist/extensions/index.js +6 -0
- package/dist/extensions/invalidation/factory.d.ts +2 -0
- package/dist/extensions/invalidation/factory.js +22 -0
- package/dist/extensions/invalidation/types.d.ts +11 -0
- package/dist/extensions/invalidation/types.js +1 -0
- package/dist/extensions/types/basic/animation.d.ts +28 -0
- package/dist/extensions/types/basic/animation.js +6 -0
- package/dist/extensions/types/basic/events.d.ts +34 -0
- package/dist/extensions/types/basic/events.js +7 -0
- package/dist/extensions/types/basic/mount.d.ts +7 -0
- package/dist/extensions/types/basic/mount.js +14 -0
- package/dist/extensions/types/basic/render.d.ts +9 -0
- package/dist/extensions/types/basic/render.js +1 -0
- package/dist/extensions/types/basic/transient-visuals.d.ts +10 -0
- package/dist/extensions/types/basic/transient-visuals.js +1 -0
- package/dist/extensions/types/basic/update.d.ts +16 -0
- package/dist/extensions/types/basic/update.js +11 -0
- package/dist/extensions/types/context/animation.d.ts +31 -0
- package/dist/extensions/types/context/animation.js +9 -0
- package/dist/extensions/types/context/render.d.ts +6 -0
- package/dist/extensions/types/context/render.js +1 -0
- package/dist/extensions/types/context/transient-visuals.d.ts +7 -0
- package/dist/extensions/types/context/transient-visuals.js +1 -0
- package/dist/extensions/types/context/ui-move.d.ts +16 -0
- package/dist/extensions/types/context/ui-move.js +1 -0
- package/dist/extensions/types/context/update.d.ts +25 -0
- package/dist/extensions/types/context/update.js +10 -0
- package/dist/extensions/types/extension.d.ts +40 -0
- package/dist/extensions/types/extension.js +1 -0
- package/dist/extensions/types/main.d.ts +44 -0
- package/dist/extensions/types/main.js +1 -0
- package/dist/extensions/types/surface/commands.d.ts +26 -0
- package/dist/extensions/types/surface/commands.js +1 -0
- package/dist/extensions/types/surface/events.d.ts +5 -0
- package/dist/extensions/types/surface/events.js +1 -0
- package/dist/extensions/types/surface/main.d.ts +10 -0
- package/dist/extensions/types/surface/main.js +1 -0
- package/dist/extensions/types/surface/transient-visuals.d.ts +4 -0
- package/dist/extensions/types/surface/transient-visuals.js +1 -0
- package/dist/extensions/types/wrapper.d.ts +26 -0
- package/dist/extensions/types/wrapper.js +11 -0
- package/dist/extensions/update.d.ts +4 -0
- package/dist/extensions/update.js +29 -0
- package/dist/helpers/util.d.ts +1 -0
- package/dist/helpers/util.js +9 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -0
- package/dist/layout/factory.d.ts +2 -0
- package/dist/layout/factory.js +33 -0
- package/dist/layout/geometry/factory.d.ts +12 -0
- package/dist/layout/geometry/factory.js +33 -0
- package/dist/layout/geometry/types.d.ts +20 -0
- package/dist/layout/geometry/types.js +1 -0
- package/dist/layout/helpers.d.ts +4 -0
- package/dist/layout/helpers.js +6 -0
- package/dist/layout/mutation.d.ts +6 -0
- package/dist/layout/mutation.js +1 -0
- package/dist/layout/reducers.d.ts +2 -0
- package/dist/layout/reducers.js +30 -0
- package/dist/layout/types.d.ts +23 -0
- package/dist/layout/types.js +1 -0
- package/dist/mutation/pipeline.d.ts +2 -0
- package/dist/mutation/pipeline.js +36 -0
- package/dist/mutation/session.d.ts +3 -0
- package/dist/mutation/session.js +66 -0
- package/dist/mutation/types.d.ts +26 -0
- package/dist/mutation/types.js +1 -0
- package/dist/render/factory.d.ts +2 -0
- package/dist/render/factory.js +117 -0
- package/dist/render/mount.d.ts +3 -0
- package/dist/render/mount.js +28 -0
- package/dist/render/rendering/animation.d.ts +13 -0
- package/dist/render/rendering/animation.js +92 -0
- package/dist/render/rendering/helpers.d.ts +3 -0
- package/dist/render/rendering/helpers.js +10 -0
- package/dist/render/rendering/state.d.ts +4 -0
- package/dist/render/rendering/state.js +48 -0
- package/dist/render/rendering/visuals.d.ts +3 -0
- package/dist/render/rendering/visuals.js +21 -0
- package/dist/render/scheduler/scheduler.d.ts +2 -0
- package/dist/render/scheduler/scheduler.js +53 -0
- package/dist/render/scheduler/types.d.ts +12 -0
- package/dist/render/scheduler/types.js +1 -0
- package/dist/render/svg/factory.d.ts +4 -0
- package/dist/render/svg/factory.js +38 -0
- package/dist/render/svg/helpers.d.ts +18 -0
- package/dist/render/svg/helpers.js +34 -0
- package/dist/render/types.d.ts +42 -0
- package/dist/render/types.js +1 -0
- package/dist/runtime/factory/helpers.d.ts +6 -0
- package/dist/runtime/factory/helpers.js +42 -0
- package/dist/runtime/factory/input.d.ts +3 -0
- package/dist/runtime/factory/input.js +98 -0
- package/dist/runtime/factory/main.d.ts +2 -0
- package/dist/runtime/factory/main.js +182 -0
- package/dist/runtime/factory/ui-move.d.ts +4 -0
- package/dist/runtime/factory/ui-move.js +84 -0
- package/dist/runtime/input/adapter/factory.d.ts +2 -0
- package/dist/runtime/input/adapter/factory.js +55 -0
- package/dist/runtime/input/adapter/helpers.d.ts +5 -0
- package/dist/runtime/input/adapter/helpers.js +23 -0
- package/dist/runtime/input/adapter/pointer.d.ts +13 -0
- package/dist/runtime/input/adapter/pointer.js +97 -0
- package/dist/runtime/input/adapter/types.d.ts +16 -0
- package/dist/runtime/input/adapter/types.js +1 -0
- package/dist/runtime/input/controller/factory.d.ts +2 -0
- package/dist/runtime/input/controller/factory.js +32 -0
- package/dist/runtime/input/controller/helpers.d.ts +3 -0
- package/dist/runtime/input/controller/helpers.js +12 -0
- package/dist/runtime/input/controller/pointer.d.ts +7 -0
- package/dist/runtime/input/controller/pointer.js +104 -0
- package/dist/runtime/input/controller/transient-visuals.d.ts +3 -0
- package/dist/runtime/input/controller/transient-visuals.js +12 -0
- package/dist/runtime/input/controller/types.d.ts +25 -0
- package/dist/runtime/input/controller/types.js +1 -0
- package/dist/runtime/layout.d.ts +2 -0
- package/dist/runtime/layout.js +10 -0
- package/dist/runtime/lifecycle.d.ts +12 -0
- package/dist/runtime/lifecycle.js +49 -0
- package/dist/runtime/mutation/extension-update.d.ts +2 -0
- package/dist/runtime/mutation/extension-update.js +26 -0
- package/dist/runtime/mutation/factory.d.ts +2 -0
- package/dist/runtime/mutation/factory.js +47 -0
- package/dist/runtime/mutation/interaction.d.ts +2 -0
- package/dist/runtime/mutation/interaction.js +34 -0
- package/dist/runtime/mutation/invalidation.d.ts +0 -0
- package/dist/runtime/mutation/invalidation.js +2 -0
- package/dist/runtime/mutation/last-move.d.ts +3 -0
- package/dist/runtime/mutation/last-move.js +19 -0
- package/dist/runtime/mutation/layout.d.ts +2 -0
- package/dist/runtime/mutation/layout.js +8 -0
- package/dist/runtime/mutation/pipeline.d.ts +22 -0
- package/dist/runtime/mutation/pipeline.js +1 -0
- package/dist/runtime/mutation/request-render.d.ts +2 -0
- package/dist/runtime/mutation/request-render.js +29 -0
- package/dist/runtime/mutation/run.d.ts +2 -0
- package/dist/runtime/mutation/run.js +3 -0
- package/dist/runtime/mutation/types.d.ts +14 -0
- package/dist/runtime/mutation/types.js +1 -0
- package/dist/runtime/types/main.d.ts +36 -0
- package/dist/runtime/types/main.js +1 -0
- package/dist/runtime/types/ui-move.d.ts +9 -0
- package/dist/runtime/types/ui-move.js +1 -0
- package/dist/state/board/check.d.ts +27 -0
- package/dist/state/board/check.js +148 -0
- package/dist/state/board/coords.d.ts +4 -0
- package/dist/state/board/coords.js +9 -0
- package/dist/state/board/denormalize.d.ts +11 -0
- package/dist/state/board/denormalize.js +77 -0
- package/dist/state/board/factory.d.ts +3 -0
- package/dist/state/board/factory.js +46 -0
- package/dist/state/board/mutation.d.ts +9 -0
- package/dist/state/board/mutation.js +1 -0
- package/dist/state/board/normalize.d.ts +8 -0
- package/dist/state/board/normalize.js +76 -0
- package/dist/state/board/piece.d.ts +3 -0
- package/dist/state/board/piece.js +11 -0
- package/dist/state/board/position.d.ts +4 -0
- package/dist/state/board/position.js +94 -0
- package/dist/state/board/reducers.d.ts +8 -0
- package/dist/state/board/reducers.js +80 -0
- package/dist/state/board/types/input.d.ts +35 -0
- package/dist/state/board/types/input.js +1 -0
- package/dist/state/board/types/internal.d.ts +54 -0
- package/dist/state/board/types/internal.js +17 -0
- package/dist/state/board/types/main.d.ts +18 -0
- package/dist/state/board/types/main.js +1 -0
- package/dist/state/board/types/output.d.ts +5 -0
- package/dist/state/board/types/output.js +1 -0
- package/dist/state/board/types/template.d.ts +23 -0
- package/dist/state/board/types/template.js +1 -0
- package/dist/state/change/factory.d.ts +2 -0
- package/dist/state/change/factory.js +28 -0
- package/dist/state/change/helpers.d.ts +8 -0
- package/dist/state/change/helpers.js +86 -0
- package/dist/state/change/mutation.d.ts +6 -0
- package/dist/state/change/mutation.js +1 -0
- package/dist/state/change/reducers.d.ts +5 -0
- package/dist/state/change/reducers.js +16 -0
- package/dist/state/change/types.d.ts +17 -0
- package/dist/state/change/types.js +1 -0
- package/dist/state/factory.d.ts +2 -0
- package/dist/state/factory.js +24 -0
- package/dist/state/interaction/factory.d.ts +2 -0
- package/dist/state/interaction/factory.js +83 -0
- package/dist/state/interaction/helpers.d.ts +4 -0
- package/dist/state/interaction/helpers.js +16 -0
- package/dist/state/interaction/movability.d.ts +5 -0
- package/dist/state/interaction/movability.js +87 -0
- package/dist/state/interaction/mutation.d.ts +11 -0
- package/dist/state/interaction/mutation.js +1 -0
- package/dist/state/interaction/normalize.d.ts +5 -0
- package/dist/state/interaction/normalize.js +50 -0
- package/dist/state/interaction/reducers.d.ts +11 -0
- package/dist/state/interaction/reducers.js +81 -0
- package/dist/state/interaction/types/input.d.ts +18 -0
- package/dist/state/interaction/types/input.js +1 -0
- package/dist/state/interaction/types/internal.d.ts +35 -0
- package/dist/state/interaction/types/internal.js +1 -0
- package/dist/state/interaction/types/main.d.ts +33 -0
- package/dist/state/interaction/types/main.js +1 -0
- package/dist/state/types.d.ts +30 -0
- package/dist/state/types.js +1 -0
- package/dist/state/view/factory.d.ts +2 -0
- package/dist/state/view/factory.js +24 -0
- package/dist/state/view/mutation.d.ts +5 -0
- package/dist/state/view/mutation.js +1 -0
- package/dist/state/view/reducers.d.ts +3 -0
- package/dist/state/view/reducers.js +6 -0
- package/dist/state/view/types/internal.d.ts +6 -0
- package/dist/state/view/types/internal.js +1 -0
- package/dist/state/view/types/main.d.ts +12 -0
- package/dist/state/view/types/main.js +1 -0
- package/dist/wrapper/factory.d.ts +3 -0
- package/dist/wrapper/factory.js +61 -0
- package/dist/wrapper/types.d.ts +56 -0
- package/dist/wrapper/types.js +1 -0
- package/package.json +98 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createBoard } from './wrapper/factory';
|
|
2
|
+
export type { Chessboard, ChessboardExtensionInput, ChessboardInitOptions, ChessboardRuntimeSurface } from './wrapper/types';
|
|
3
|
+
export type { MoveRequestInput, PiecePositionRecordString } from './state/board/types/input';
|
|
4
|
+
export type { RuntimeStateInitOptions } from './state/types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createBoard } from './wrapper/factory';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cloneDeep } from 'es-toolkit/object';
|
|
2
|
+
import { layoutRefreshGeometry } from './reducers';
|
|
3
|
+
function createLayoutInternal() {
|
|
4
|
+
return {
|
|
5
|
+
boardSize: 0,
|
|
6
|
+
orientation: 0 /* ColorCode.White */,
|
|
7
|
+
geometry: null,
|
|
8
|
+
layoutEpoch: 0
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function createLayout() {
|
|
12
|
+
const internalState = createLayoutInternal();
|
|
13
|
+
return {
|
|
14
|
+
get boardSize() {
|
|
15
|
+
return internalState.boardSize;
|
|
16
|
+
},
|
|
17
|
+
get orientation() {
|
|
18
|
+
return internalState.orientation;
|
|
19
|
+
},
|
|
20
|
+
get geometry() {
|
|
21
|
+
return internalState.geometry;
|
|
22
|
+
},
|
|
23
|
+
get layoutEpoch() {
|
|
24
|
+
return internalState.layoutEpoch;
|
|
25
|
+
},
|
|
26
|
+
refreshGeometry(options, mutationSession) {
|
|
27
|
+
return mutationSession.addMutation('layout.refreshGeometry', layoutRefreshGeometry(internalState, options));
|
|
28
|
+
},
|
|
29
|
+
getSnapshot() {
|
|
30
|
+
return cloneDeep(internalState);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ColorCode } from '../../state/board/types/internal';
|
|
2
|
+
import { RenderGeometry } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Create RenderGeometry for a given board size (in px) and orientation.
|
|
5
|
+
* - Geometry uses an SVG-local coordinate system with origin at top-left.
|
|
6
|
+
* - Orientation mapping:
|
|
7
|
+
* - 'white': files increase left→right (a..h), ranks increase bottom→top (1..8).
|
|
8
|
+
* yIndex = 7 - rank, xIndex = file
|
|
9
|
+
* - 'black': files increase right→left (a..h), ranks increase top→bottom (1..8).
|
|
10
|
+
* yIndex = rank, xIndex = 7 - file
|
|
11
|
+
*/
|
|
12
|
+
export declare function createRenderGeometry(boardSize: number, orientation: ColorCode): RenderGeometry;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fileOf, rankOf } from '../../state/board/coords';
|
|
2
|
+
/**
|
|
3
|
+
* Create RenderGeometry for a given board size (in px) and orientation.
|
|
4
|
+
* - Geometry uses an SVG-local coordinate system with origin at top-left.
|
|
5
|
+
* - Orientation mapping:
|
|
6
|
+
* - 'white': files increase left→right (a..h), ranks increase bottom→top (1..8).
|
|
7
|
+
* yIndex = 7 - rank, xIndex = file
|
|
8
|
+
* - 'black': files increase right→left (a..h), ranks increase top→bottom (1..8).
|
|
9
|
+
* yIndex = rank, xIndex = 7 - file
|
|
10
|
+
*/
|
|
11
|
+
export function createRenderGeometry(boardSize, orientation) {
|
|
12
|
+
if (!(boardSize > 0 && Number.isFinite(boardSize))) {
|
|
13
|
+
throw new RangeError(`Invalid boardSize: ${boardSize}`);
|
|
14
|
+
}
|
|
15
|
+
const squareSize = boardSize / 8;
|
|
16
|
+
const white = orientation === 0 /* ColorCode.White */;
|
|
17
|
+
return {
|
|
18
|
+
boardSize,
|
|
19
|
+
squareSize,
|
|
20
|
+
orientation,
|
|
21
|
+
squareRect(sq) {
|
|
22
|
+
const f = fileOf(sq);
|
|
23
|
+
const r = rankOf(sq);
|
|
24
|
+
const xIndex = white ? f : 7 - f;
|
|
25
|
+
const yIndex = white ? 7 - r : r;
|
|
26
|
+
return {
|
|
27
|
+
x: xIndex * squareSize,
|
|
28
|
+
y: yIndex * squareSize,
|
|
29
|
+
size: squareSize
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReadonlyDeep } from 'type-fest';
|
|
2
|
+
import { ColorCode, Square } from '../../state/board/types/internal';
|
|
3
|
+
export interface SquareRect {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
size: number;
|
|
7
|
+
}
|
|
8
|
+
export type SquareRectSnapshot = ReadonlyDeep<SquareRect>;
|
|
9
|
+
/**
|
|
10
|
+
* RenderGeometry represents the derived geometry of the rendered chessboard.
|
|
11
|
+
* It includes the board size, square size, orientation, and square rectangle mapping.
|
|
12
|
+
*
|
|
13
|
+
* It is computed from layout inputs and used by rendering and interaction code.
|
|
14
|
+
*/
|
|
15
|
+
export interface RenderGeometry {
|
|
16
|
+
readonly boardSize: number;
|
|
17
|
+
readonly squareSize: number;
|
|
18
|
+
readonly orientation: ColorCode;
|
|
19
|
+
squareRect(sq: Square): SquareRectSnapshot;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MutationSession } from '../mutation/types';
|
|
2
|
+
export type LayoutMutationPayloadByCause = {
|
|
3
|
+
'layout.refreshGeometry': undefined;
|
|
4
|
+
};
|
|
5
|
+
export type LayoutMutationCause = keyof LayoutMutationPayloadByCause;
|
|
6
|
+
export type LayoutMutationSession = MutationSession<LayoutMutationPayloadByCause>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createRenderGeometry } from './geometry/factory';
|
|
2
|
+
import { measureBoardSize } from './helpers';
|
|
3
|
+
export function layoutRefreshGeometry(state, options) {
|
|
4
|
+
const orientation = options.orientation ?? state.orientation;
|
|
5
|
+
if (orientation === null) {
|
|
6
|
+
// At this point we should decide if the geometry is invalid or we throw because we expect that the first pass must deliver the orientation?
|
|
7
|
+
// For now we throw
|
|
8
|
+
throw new Error('Orientation is expected to be provided on the first pass of layout.refreshGeometry');
|
|
9
|
+
}
|
|
10
|
+
let boardSize = options.container ? measureBoardSize(options.container) : state.boardSize;
|
|
11
|
+
if (boardSize !== null && boardSize <= 0)
|
|
12
|
+
boardSize = null;
|
|
13
|
+
const changed = boardSize !== state.boardSize || orientation !== state.orientation;
|
|
14
|
+
if (!changed) {
|
|
15
|
+
return false; // no-op, no relevant changes
|
|
16
|
+
}
|
|
17
|
+
// Update the state
|
|
18
|
+
state.boardSize = boardSize;
|
|
19
|
+
state.orientation = orientation;
|
|
20
|
+
if (boardSize === null || orientation === null) {
|
|
21
|
+
// General invalid-geometry rule. Orientation is currently guarded above,
|
|
22
|
+
// but this branch stays explicit in case null orientation is allowed later.
|
|
23
|
+
state.geometry = null;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
state.geometry = createRenderGeometry(boardSize, orientation);
|
|
27
|
+
}
|
|
28
|
+
state.layoutEpoch++;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ReadonlyDeep } from 'type-fest';
|
|
2
|
+
import { ColorCode } from '../state/board/types/internal';
|
|
3
|
+
import { RenderGeometry } from './geometry/types';
|
|
4
|
+
import { LayoutMutationSession } from './mutation';
|
|
5
|
+
export interface LayoutInternal {
|
|
6
|
+
boardSize: number | null;
|
|
7
|
+
orientation: ColorCode | null;
|
|
8
|
+
geometry: RenderGeometry | null;
|
|
9
|
+
layoutEpoch: number;
|
|
10
|
+
}
|
|
11
|
+
export type LayoutSnapshot = ReadonlyDeep<LayoutInternal>;
|
|
12
|
+
export interface LayoutRefreshOptions {
|
|
13
|
+
container?: HTMLElement;
|
|
14
|
+
orientation?: ColorCode;
|
|
15
|
+
}
|
|
16
|
+
export interface Layout {
|
|
17
|
+
readonly boardSize: number | null;
|
|
18
|
+
readonly orientation: ColorCode | null;
|
|
19
|
+
readonly geometry: RenderGeometry | null;
|
|
20
|
+
readonly layoutEpoch: number;
|
|
21
|
+
refreshGeometry(options: LayoutRefreshOptions, mutationSession: LayoutMutationSession): boolean;
|
|
22
|
+
getSnapshot(): LayoutSnapshot;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createMutationSession } from './session';
|
|
2
|
+
export function createMutationPipeline(pipes) {
|
|
3
|
+
const registeredPipes = [...pipes];
|
|
4
|
+
const session = createMutationSession();
|
|
5
|
+
let isRunning = false;
|
|
6
|
+
return {
|
|
7
|
+
getSession() {
|
|
8
|
+
return session;
|
|
9
|
+
},
|
|
10
|
+
addMutation(cause, changed, ...payload) {
|
|
11
|
+
return session.addMutation(cause, changed, ...payload);
|
|
12
|
+
},
|
|
13
|
+
run(ctx) {
|
|
14
|
+
if (isRunning) {
|
|
15
|
+
throw new Error('Cannot run mutation pipeline recursively');
|
|
16
|
+
}
|
|
17
|
+
isRunning = true;
|
|
18
|
+
// no-op if no mutations recorded
|
|
19
|
+
if (!session.hasMutation()) {
|
|
20
|
+
isRunning = false;
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
for (const pipe of registeredPipes) {
|
|
25
|
+
pipe(ctx, session);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
// Clear session even if a pipe throws
|
|
30
|
+
session.clear();
|
|
31
|
+
isRunning = false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MutationCause, MutationSession, ReadonlyMutationSession } from './types';
|
|
2
|
+
export declare function createReadonlyMutationSession<PayloadByCause extends Record<string, unknown>>(payloads: ReadonlyMap<MutationCause<PayloadByCause>, PayloadByCause[MutationCause<PayloadByCause>][] | undefined>, clone?: boolean): ReadonlyMutationSession<PayloadByCause>;
|
|
3
|
+
export declare function createMutationSession<PayloadByCause extends Record<string, unknown>>(): MutationSession<PayloadByCause>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function createReadonlyMutationSession(payloads, clone = false) {
|
|
2
|
+
const internalPayloads = clone ? new Map(payloads) : payloads;
|
|
3
|
+
return {
|
|
4
|
+
hasMutation(match) {
|
|
5
|
+
if (!match) {
|
|
6
|
+
return internalPayloads.size > 0;
|
|
7
|
+
}
|
|
8
|
+
const { causes, prefixes } = match;
|
|
9
|
+
if (causes) {
|
|
10
|
+
for (const cause of causes) {
|
|
11
|
+
if (internalPayloads.has(cause)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (prefixes) {
|
|
17
|
+
for (const prefix of prefixes) {
|
|
18
|
+
for (const cause of internalPayloads.keys()) {
|
|
19
|
+
if (cause.toString().startsWith(prefix.toString())) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
},
|
|
27
|
+
getPayloads(cause) {
|
|
28
|
+
return internalPayloads.get(cause);
|
|
29
|
+
},
|
|
30
|
+
getAll() {
|
|
31
|
+
return new Map(internalPayloads);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function createMutationSession() {
|
|
36
|
+
const payloads = new Map();
|
|
37
|
+
const readonlySnapshot = createReadonlyMutationSession(payloads);
|
|
38
|
+
return {
|
|
39
|
+
...readonlySnapshot,
|
|
40
|
+
addMutation(cause, changed, ...payload) {
|
|
41
|
+
if (!changed) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const hasPayload = payload.length > 0;
|
|
45
|
+
if (payloads.has(cause)) {
|
|
46
|
+
// We already have this cause recorded, just append payload if provided
|
|
47
|
+
if (!hasPayload)
|
|
48
|
+
return true;
|
|
49
|
+
// We already have some payloads for this cause
|
|
50
|
+
const existingPayloads = payloads.get(cause);
|
|
51
|
+
if (existingPayloads) {
|
|
52
|
+
existingPayloads.push(payload[0]);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
payloads.set(cause, [payload[0]]);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
// No existing entry for this cause, add new one
|
|
59
|
+
payloads.set(cause, hasPayload ? [payload[0]] : undefined);
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
clear() {
|
|
63
|
+
payloads.clear();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type MutationCause<PayloadByCause extends Record<string, unknown>> = Extract<keyof PayloadByCause, string>;
|
|
2
|
+
export type MutationPayload<PayloadByCause extends Record<string, unknown>> = PayloadByCause[MutationCause<PayloadByCause>];
|
|
3
|
+
type Prefixes<S extends string> = S extends `${infer Head}.${infer Tail}` ? Head | `${Head}.${Prefixes<Tail>}` : never;
|
|
4
|
+
type DotSuffix<T extends string> = `${T}.`;
|
|
5
|
+
type DottedPrefixes<S extends string> = DotSuffix<Prefixes<S>>;
|
|
6
|
+
export type MutationCausePrefixes<PayloadByCause extends Record<string, unknown>> = DottedPrefixes<MutationCause<PayloadByCause>>;
|
|
7
|
+
export interface MutationCauseMatchFilter<PayloadByCause extends Record<string, unknown>> {
|
|
8
|
+
causes?: Iterable<MutationCause<PayloadByCause>>;
|
|
9
|
+
prefixes?: Iterable<MutationCausePrefixes<PayloadByCause>>;
|
|
10
|
+
}
|
|
11
|
+
export interface ReadonlyMutationSession<PayloadByCause extends Record<string, unknown>> {
|
|
12
|
+
hasMutation(match?: MutationCauseMatchFilter<PayloadByCause>): boolean;
|
|
13
|
+
getPayloads<Cause extends MutationCause<PayloadByCause>>(cause: Cause): PayloadByCause[Cause][] | undefined;
|
|
14
|
+
getAll<TargetPayloadByCause extends PayloadByCause>(): ReadonlyMap<MutationCause<TargetPayloadByCause>, TargetPayloadByCause[MutationCause<TargetPayloadByCause>][] | undefined>;
|
|
15
|
+
}
|
|
16
|
+
export interface MutationSession<PayloadByCause extends Record<string, unknown>> extends ReadonlyMutationSession<PayloadByCause> {
|
|
17
|
+
addMutation<Cause extends MutationCause<PayloadByCause>>(cause: Cause, changed: boolean, ...payload: PayloadByCause[Cause] extends undefined ? [] : [payload: PayloadByCause[Cause]]): boolean;
|
|
18
|
+
clear(): void;
|
|
19
|
+
}
|
|
20
|
+
export type MutationPipe<PayloadByCause extends Record<string, unknown>, Context> = (ctx: Context, mutationSession: MutationSession<PayloadByCause>) => void;
|
|
21
|
+
export interface MutationPipeline<PayloadByCause extends Record<string, unknown>, Context> {
|
|
22
|
+
getSession(): MutationSession<PayloadByCause>;
|
|
23
|
+
addMutation<Cause extends MutationCause<PayloadByCause>>(cause: Cause, changed: boolean, ...payload: PayloadByCause[Cause] extends undefined ? [] : [payload: PayloadByCause[Cause]]): boolean;
|
|
24
|
+
run(ctx: Context): boolean;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { renderMount, renderUnmount } from './mount';
|
|
2
|
+
import { performAnimationCleanup, performAnimationPass } from './rendering/animation';
|
|
3
|
+
import { validateIsMounted } from './rendering/helpers';
|
|
4
|
+
import { performRenderPass } from './rendering/state';
|
|
5
|
+
import { performRenderTransientVisualsPass } from './rendering/visuals';
|
|
6
|
+
import { createScheduler } from './scheduler/scheduler';
|
|
7
|
+
import { allocateExtensionSlotRoots, createSvgRoots } from './svg/factory';
|
|
8
|
+
function createRenderInternal(options) {
|
|
9
|
+
const svgRoots = createSvgRoots(options);
|
|
10
|
+
const scheduler = createScheduler({
|
|
11
|
+
render: options.performRender
|
|
12
|
+
});
|
|
13
|
+
const extensions = new Map();
|
|
14
|
+
for (const extensionSystemExt of options.sharedDataFromExtensionSystem.extensions.values()) {
|
|
15
|
+
const extensionInternal = {
|
|
16
|
+
id: extensionSystemExt.id,
|
|
17
|
+
extension: extensionSystemExt,
|
|
18
|
+
render: {
|
|
19
|
+
slots: allocateExtensionSlotRoots(svgRoots, extensionSystemExt.id, extensionSystemExt.definition.slots)
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
extensions.set(extensionInternal.id, extensionInternal);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
container: null,
|
|
26
|
+
currentFrame: null,
|
|
27
|
+
svgRoots,
|
|
28
|
+
scheduler,
|
|
29
|
+
extensions,
|
|
30
|
+
transientVisualsSubscribers: options.sharedDataFromExtensionSystem.transientVisualsSubscribers
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function performRender(state, options) {
|
|
34
|
+
// First we check and run renderPass, then deferred animation cleanup.
|
|
35
|
+
if (options.stateRequest) {
|
|
36
|
+
performRenderPass(state, options.stateRequest);
|
|
37
|
+
performAnimationCleanup(state);
|
|
38
|
+
}
|
|
39
|
+
// Then we check and run renderAnimation,
|
|
40
|
+
if (options.animationRequest) {
|
|
41
|
+
const nextRequest = performAnimationPass(state);
|
|
42
|
+
if (nextRequest.requestRenderAnimation) {
|
|
43
|
+
options.requestRenderAnimation();
|
|
44
|
+
}
|
|
45
|
+
if (nextRequest.requestRender) {
|
|
46
|
+
options.requestRender();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Finally we run renderVisuals.
|
|
50
|
+
if (options.visualsRequest) {
|
|
51
|
+
performRenderTransientVisualsPass(state, options.visualsRequest);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function createRenderSystem(options) {
|
|
55
|
+
let pendingStateRequest = null;
|
|
56
|
+
let pendingAnimationRequest = null;
|
|
57
|
+
let pendingVisualsRequest = null;
|
|
58
|
+
function performRenderClosure() {
|
|
59
|
+
const stateRequest = pendingStateRequest;
|
|
60
|
+
pendingStateRequest = null;
|
|
61
|
+
const animationRequest = pendingAnimationRequest;
|
|
62
|
+
pendingAnimationRequest = null;
|
|
63
|
+
const visualsRequest = pendingVisualsRequest;
|
|
64
|
+
pendingVisualsRequest = null;
|
|
65
|
+
performRender(internalState, {
|
|
66
|
+
stateRequest: stateRequest,
|
|
67
|
+
animationRequest: animationRequest,
|
|
68
|
+
requestRenderAnimation: () => {
|
|
69
|
+
pendingAnimationRequest = pendingAnimationRequest ?? true;
|
|
70
|
+
internalState.scheduler.schedule();
|
|
71
|
+
},
|
|
72
|
+
requestRender: () => {
|
|
73
|
+
pendingStateRequest = pendingStateRequest ?? internalState.currentFrame;
|
|
74
|
+
internalState.scheduler.schedule();
|
|
75
|
+
},
|
|
76
|
+
visualsRequest: visualsRequest
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const internalState = createRenderInternal({
|
|
80
|
+
...options,
|
|
81
|
+
performRender: performRenderClosure
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
extensions: internalState.extensions,
|
|
85
|
+
requestRender(request) {
|
|
86
|
+
validateIsMounted(internalState);
|
|
87
|
+
pendingStateRequest = request ?? internalState.currentFrame;
|
|
88
|
+
internalState.scheduler.schedule();
|
|
89
|
+
},
|
|
90
|
+
requestRenderAnimation() {
|
|
91
|
+
validateIsMounted(internalState);
|
|
92
|
+
pendingAnimationRequest = true;
|
|
93
|
+
internalState.scheduler.schedule();
|
|
94
|
+
},
|
|
95
|
+
requestRenderVisuals(request) {
|
|
96
|
+
validateIsMounted(internalState);
|
|
97
|
+
pendingVisualsRequest = request;
|
|
98
|
+
internalState.scheduler.schedule();
|
|
99
|
+
},
|
|
100
|
+
mount(element) {
|
|
101
|
+
renderMount(internalState, element);
|
|
102
|
+
},
|
|
103
|
+
unmount() {
|
|
104
|
+
renderUnmount(internalState);
|
|
105
|
+
internalState.scheduler.cancel();
|
|
106
|
+
pendingStateRequest = null;
|
|
107
|
+
pendingAnimationRequest = null;
|
|
108
|
+
pendingVisualsRequest = null;
|
|
109
|
+
},
|
|
110
|
+
get isMounted() {
|
|
111
|
+
return internalState.container !== null;
|
|
112
|
+
},
|
|
113
|
+
get container() {
|
|
114
|
+
return internalState.container;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { validateIsMounted, validateIsNotMounted } from './rendering/helpers';
|
|
2
|
+
import { clearElementChildren } from './svg/helpers';
|
|
3
|
+
export function renderMount(state, element) {
|
|
4
|
+
validateIsNotMounted(state);
|
|
5
|
+
state.container = element;
|
|
6
|
+
// mount our svg root to the container
|
|
7
|
+
element.appendChild(state.svgRoots.svgRoot);
|
|
8
|
+
// Now call onMount for all extensions with their slot roots
|
|
9
|
+
for (const extensionRec of state.extensions.values()) {
|
|
10
|
+
const slotRoots = extensionRec.render.slots;
|
|
11
|
+
extensionRec.extension.instance.mount({ slotRoots });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function renderUnmount(state) {
|
|
15
|
+
validateIsMounted(state);
|
|
16
|
+
for (const extensionRec of state.extensions.values()) {
|
|
17
|
+
extensionRec.extension.instance.unmount();
|
|
18
|
+
// clear the slot roots
|
|
19
|
+
for (const slotRoot of Object.values(extensionRec.render.slots)) {
|
|
20
|
+
clearElementChildren(slotRoot);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// remove our svg root from the container
|
|
24
|
+
state.svgRoots.svgRoot.remove();
|
|
25
|
+
state.container = null;
|
|
26
|
+
// Now cleanup the render state
|
|
27
|
+
state.currentFrame = null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RenderSystemInternal } from '../types';
|
|
2
|
+
export interface RenderAnimationResult {
|
|
3
|
+
requestRenderAnimation: boolean;
|
|
4
|
+
requestRender: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Runs deferred physical cleanup for all sessions that were marked pendingCleanup
|
|
8
|
+
* in a previous animation pass. Called at the end of performRenderPass (after stable
|
|
9
|
+
* base ownership has been rendered) so that animation SVG is never removed before
|
|
10
|
+
* the unsuppressed base piece layer is rendered.
|
|
11
|
+
*/
|
|
12
|
+
export declare function performAnimationCleanup(state: RenderSystemInternal): void;
|
|
13
|
+
export declare function performAnimationPass(state: RenderSystemInternal): RenderAnimationResult;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { validateIsMounted } from './helpers';
|
|
2
|
+
/**
|
|
3
|
+
* Runs deferred physical cleanup for all sessions that were marked pendingCleanup
|
|
4
|
+
* in a previous animation pass. Called at the end of performRenderPass (after stable
|
|
5
|
+
* base ownership has been rendered) so that animation SVG is never removed before
|
|
6
|
+
* the unsuppressed base piece layer is rendered.
|
|
7
|
+
*/
|
|
8
|
+
export function performAnimationCleanup(state) {
|
|
9
|
+
const currentFrame = state.currentFrame;
|
|
10
|
+
if (!currentFrame) {
|
|
11
|
+
throw new Error('performAnimationCleanup() called but no current render frame found. render() must be called before performAnimationCleanup().');
|
|
12
|
+
}
|
|
13
|
+
for (const extensionRec of state.extensions.values()) {
|
|
14
|
+
const pendingSessions = extensionRec.extension.animation
|
|
15
|
+
.getAll(['ended', 'cancelled'])
|
|
16
|
+
.filter((s) => s.pendingCleanup);
|
|
17
|
+
if (pendingSessions.length === 0)
|
|
18
|
+
continue;
|
|
19
|
+
const context = {
|
|
20
|
+
currentFrame,
|
|
21
|
+
invalidation: extensionRec.extension.invalidation,
|
|
22
|
+
finishedSessions: pendingSessions
|
|
23
|
+
};
|
|
24
|
+
extensionRec.extension.instance.cleanAnimation?.(context);
|
|
25
|
+
extensionRec.extension.animation.remove(pendingSessions.map((s) => s.id));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function performAnimationPass(state) {
|
|
29
|
+
validateIsMounted(state);
|
|
30
|
+
let requestRenderAnimation = false;
|
|
31
|
+
let requestRender = false;
|
|
32
|
+
const currentFrame = state.currentFrame;
|
|
33
|
+
if (!currentFrame) {
|
|
34
|
+
throw new Error('renderAnimation() called but no current render frame found. render() must be called before renderAnimation().');
|
|
35
|
+
}
|
|
36
|
+
for (const extensionRec of state.extensions.values()) {
|
|
37
|
+
// Prepare submitted sessions
|
|
38
|
+
const submittedSessions = extensionRec.extension.animation.getAll('submitted');
|
|
39
|
+
if (submittedSessions.length > 0) {
|
|
40
|
+
const context = {
|
|
41
|
+
currentFrame: currentFrame,
|
|
42
|
+
invalidation: extensionRec.extension.invalidation,
|
|
43
|
+
submittedSessions: submittedSessions
|
|
44
|
+
};
|
|
45
|
+
extensionRec.extension.instance.prepareAnimation?.(context);
|
|
46
|
+
submittedSessions.forEach((session) => {
|
|
47
|
+
session.setStatus('active');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Render active sessions
|
|
51
|
+
const activeSessions = extensionRec.extension.animation.getAll('active');
|
|
52
|
+
if (activeSessions.length > 0) {
|
|
53
|
+
const context = {
|
|
54
|
+
currentFrame: currentFrame,
|
|
55
|
+
invalidation: extensionRec.extension.invalidation,
|
|
56
|
+
activeSessions: activeSessions
|
|
57
|
+
};
|
|
58
|
+
extensionRec.extension.instance.renderAnimation?.(context);
|
|
59
|
+
}
|
|
60
|
+
// Detect newly terminal sessions: active sessions that have elapsed their duration
|
|
61
|
+
const currentTime = performance.now();
|
|
62
|
+
const newlyEndedSessions = activeSessions.filter((session) => session.startTime + session.duration <= currentTime);
|
|
63
|
+
newlyEndedSessions.forEach((session) => {
|
|
64
|
+
session.setStatus('ended');
|
|
65
|
+
});
|
|
66
|
+
// Also collect cancelled sessions not yet marked pendingCleanup
|
|
67
|
+
const cancelledSessions = extensionRec.extension.animation
|
|
68
|
+
.getAll('cancelled')
|
|
69
|
+
.filter((s) => !s.pendingCleanup);
|
|
70
|
+
const terminalSessions = [...newlyEndedSessions, ...cancelledSessions];
|
|
71
|
+
if (terminalSessions.length > 0) {
|
|
72
|
+
// Mark for deferred physical cleanup — actual removal happens in performAnimationCleanup,
|
|
73
|
+
// which runs after performRenderPass so stable base ownership is rendered first.
|
|
74
|
+
terminalSessions.forEach((session) => session.markPendingCleanup());
|
|
75
|
+
const context = {
|
|
76
|
+
currentFrame,
|
|
77
|
+
invalidation: extensionRec.extension.invalidation,
|
|
78
|
+
finishedSessions: terminalSessions
|
|
79
|
+
};
|
|
80
|
+
extensionRec.extension.instance.onAnimationFinished?.(context);
|
|
81
|
+
// Invariant B: guarantee a state render is requested so performAnimationCleanup will run.
|
|
82
|
+
requestRender = true;
|
|
83
|
+
}
|
|
84
|
+
requestRenderAnimation =
|
|
85
|
+
requestRenderAnimation || extensionRec.extension.animation.getAll('active').length > 0;
|
|
86
|
+
requestRender = requestRender || extensionRec.extension.invalidation.dirtyLayers !== 0;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
requestRenderAnimation,
|
|
90
|
+
requestRender
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function validateIsMounted(state) {
|
|
2
|
+
if (!state.container) {
|
|
3
|
+
throw new Error('Render is not mounted to any container. Please mount before requesting renders.');
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export function validateIsNotMounted(state) {
|
|
7
|
+
if (state.container) {
|
|
8
|
+
throw new Error('Render is already mounted to a container. Please unmount before performing this action.');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { RenderFrameSnapshot } from '../../extensions/types/basic/render';
|
|
2
|
+
import { RenderSystemInternal } from '../types';
|
|
3
|
+
export declare function checkNeedsRender(state: RenderSystemInternal): boolean;
|
|
4
|
+
export declare function performRenderPass(state: RenderSystemInternal, request: RenderFrameSnapshot | null): void;
|