@mirasen/chessboard 1.1.5 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +61 -13
  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 -2
@@ -0,0 +1,123 @@
1
+ import assert from '@ktarmyshov/assert';
2
+ import { VISUAL_CONFIG } from '../constants.js';
3
+ import { arrowAnnotationKeyNormalized } from '../normalize.js';
4
+ import { createAnnotationArrowSvg, removeAnnotationArrowSvg, updateAnnotationArrowSvg } from './helpers/arrows.js';
5
+ import { createAnnotationCircleSvg, removeAnnotationCircleSvg, updateAnnotationCircleSvg } from './helpers/circles.js';
6
+ export function renderCommittedAnnotations(state, geometry) {
7
+ assert(state.slotRoots, 'Expected slotRoots to be initialized before rendering annotations');
8
+ renderCommittedCircles(state, geometry);
9
+ renderCommittedArrows(state, geometry);
10
+ }
11
+ function getSuppressedCircleKey(state) {
12
+ const gesture = state.activeDrawGesture;
13
+ const target = state.activeDrawPreviewTarget;
14
+ if (gesture === null || target === null)
15
+ return null;
16
+ if (target !== gesture.sourceSquare)
17
+ return null;
18
+ const existing = state.annotations.circles.get(target);
19
+ if (existing === undefined || existing.color !== gesture.color)
20
+ return null;
21
+ return target;
22
+ }
23
+ function getSuppressedArrowKey(state) {
24
+ const gesture = state.activeDrawGesture;
25
+ const target = state.activeDrawPreviewTarget;
26
+ if (gesture === null || target === null)
27
+ return null;
28
+ if (target === gesture.sourceSquare)
29
+ return null;
30
+ const key = arrowAnnotationKeyNormalized(gesture.sourceSquare, target);
31
+ const existing = state.annotations.arrows.get(key);
32
+ if (existing === undefined || existing.color !== gesture.color)
33
+ return null;
34
+ return key;
35
+ }
36
+ function renderCommittedCircles(state, geometry) {
37
+ const { committed } = VISUAL_CONFIG.circle;
38
+ const suppressedKey = getSuppressedCircleKey(state);
39
+ const seenKeys = new Set();
40
+ for (const [key, circle] of state.annotations.circles) {
41
+ if (key === suppressedKey)
42
+ continue;
43
+ seenKeys.add(key);
44
+ const params = {
45
+ parent: state.slotRoots.overPieces,
46
+ dataId: `annotation-circle-committed-${key}`,
47
+ square: circle.square,
48
+ color: circle.color,
49
+ radius: committed.radius,
50
+ strokeWidth: committed.strokeWidth,
51
+ opacity: committed.opacity,
52
+ geometry
53
+ };
54
+ const existing = state.svg.svgCircles.get(key);
55
+ if (existing) {
56
+ updateAnnotationCircleSvg(existing, params);
57
+ }
58
+ else {
59
+ const rendered = createAnnotationCircleSvg(params);
60
+ state.svg.svgCircles.set(key, rendered);
61
+ }
62
+ }
63
+ for (const [key, rendered] of state.svg.svgCircles) {
64
+ if (!seenKeys.has(key)) {
65
+ removeAnnotationCircleSvg(rendered);
66
+ state.svg.svgCircles.delete(key);
67
+ }
68
+ }
69
+ }
70
+ function renderCommittedArrows(state, geometry) {
71
+ const { committed } = VISUAL_CONFIG.arrow;
72
+ const suppressedKey = getSuppressedArrowKey(state);
73
+ const seenKeys = new Set();
74
+ for (const [key, arrow] of state.annotations.arrows) {
75
+ if (key === suppressedKey)
76
+ continue;
77
+ seenKeys.add(key);
78
+ const fromRect = geometry.getSquareRect(arrow.from);
79
+ const toRect = geometry.getSquareRect(arrow.to);
80
+ const fromCx = fromRect.x + fromRect.width / 2;
81
+ const fromCy = fromRect.y + fromRect.height / 2;
82
+ const toCx = toRect.x + toRect.width / 2;
83
+ const toCy = toRect.y + toRect.height / 2;
84
+ const dx = toCx - fromCx;
85
+ const dy = toCy - fromCy;
86
+ const dist = Math.sqrt(dx * dx + dy * dy);
87
+ if (dist === 0) {
88
+ seenKeys.delete(key);
89
+ continue;
90
+ }
91
+ const markerId = state.svgIds.makeId('annotations', `arrowhead-${key}`);
92
+ const markerHref = `url(${state.svgIds.makeHref('annotations', `arrowhead-${key}`)})`;
93
+ const params = {
94
+ defsRoot: state.slotRoots.defs,
95
+ visualRoot: state.slotRoots.overPieces,
96
+ markerDataId: `annotation-arrowhead-committed-${key}`,
97
+ markerPathDataId: `annotation-arrowhead-path-committed-${key}`,
98
+ lineDataId: `annotation-arrow-committed-${key}`,
99
+ markerId,
100
+ markerHref,
101
+ from: arrow.from,
102
+ to: arrow.to,
103
+ color: arrow.color,
104
+ opacity: committed.opacity,
105
+ visual: committed,
106
+ geometry
107
+ };
108
+ const existing = state.svg.svgArrows.get(key);
109
+ if (existing) {
110
+ updateAnnotationArrowSvg(existing, params);
111
+ }
112
+ else {
113
+ const rendered = createAnnotationArrowSvg(params);
114
+ state.svg.svgArrows.set(key, rendered);
115
+ }
116
+ }
117
+ for (const [key, rendered] of state.svg.svgArrows) {
118
+ if (!seenKeys.has(key)) {
119
+ removeAnnotationArrowSvg(rendered);
120
+ state.svg.svgArrows.delete(key);
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,33 @@
1
+ import type { SceneRenderGeometry } from '../../../../../layout/geometry/types.js';
2
+ import type { Square } from '../../../../../state/board/types/internal.js';
3
+ import type { RenderedAnnotationArrowSvg } from '../../types/main.js';
4
+ export interface AnnotationArrowSvgVisualConfig {
5
+ readonly strokeWidth: number;
6
+ readonly startOffset: number;
7
+ readonly endOffset: number;
8
+ readonly opacity: number;
9
+ readonly markerWidth: number;
10
+ readonly markerHeight: number;
11
+ readonly markerRefX: number;
12
+ readonly markerRefY: number;
13
+ readonly markerViewBox: string;
14
+ readonly markerPathD: string;
15
+ }
16
+ export interface AnnotationArrowSvgParams {
17
+ readonly defsRoot: SVGDefsElement;
18
+ readonly visualRoot: SVGGElement;
19
+ readonly markerDataId: string;
20
+ readonly markerPathDataId: string;
21
+ readonly lineDataId: string;
22
+ readonly markerId: string;
23
+ readonly markerHref: string;
24
+ readonly from: Square;
25
+ readonly to: Square;
26
+ readonly color: string;
27
+ readonly opacity: number;
28
+ readonly visual: AnnotationArrowSvgVisualConfig;
29
+ readonly geometry: SceneRenderGeometry;
30
+ }
31
+ export declare function createAnnotationArrowSvg(params: AnnotationArrowSvgParams): RenderedAnnotationArrowSvg;
32
+ export declare function updateAnnotationArrowSvg(rendered: RenderedAnnotationArrowSvg, params: AnnotationArrowSvgParams): void;
33
+ export declare function removeAnnotationArrowSvg(rendered: RenderedAnnotationArrowSvg): void;
@@ -0,0 +1,93 @@
1
+ import { createSvgElement, updateSvgElementAttributes } from '../../../../../render/svg/helpers.js';
2
+ function computeArrowGeometry(params) {
3
+ const { from, to, visual, geometry } = params;
4
+ const squareSize = geometry.squareSize;
5
+ const fromRect = geometry.getSquareRect(from);
6
+ const toRect = geometry.getSquareRect(to);
7
+ const fromCx = fromRect.x + fromRect.width / 2;
8
+ const fromCy = fromRect.y + fromRect.height / 2;
9
+ const toCx = toRect.x + toRect.width / 2;
10
+ const toCy = toRect.y + toRect.height / 2;
11
+ const dx = toCx - fromCx;
12
+ const dy = toCy - fromCy;
13
+ const dist = Math.sqrt(dx * dx + dy * dy);
14
+ const ux = dx / dist;
15
+ const uy = dy / dist;
16
+ const absoluteStartOffset = squareSize * visual.startOffset;
17
+ const absoluteEndOffset = squareSize * visual.endOffset;
18
+ return {
19
+ x1: (fromCx + ux * absoluteStartOffset).toString(),
20
+ y1: (fromCy + uy * absoluteStartOffset).toString(),
21
+ x2: (toCx - ux * absoluteEndOffset).toString(),
22
+ y2: (toCy - uy * absoluteEndOffset).toString(),
23
+ strokeWidth: (squareSize * visual.strokeWidth).toString()
24
+ };
25
+ }
26
+ export function createAnnotationArrowSvg(params) {
27
+ const { defsRoot, visualRoot, markerDataId, markerPathDataId, lineDataId, markerId, markerHref, color, opacity, visual } = params;
28
+ const geo = computeArrowGeometry(params);
29
+ const marker = createSvgElement(defsRoot, 'marker', {
30
+ 'data-chessboard-id': markerDataId,
31
+ id: markerId,
32
+ markerUnits: 'strokeWidth',
33
+ markerWidth: visual.markerWidth.toString(),
34
+ markerHeight: visual.markerHeight.toString(),
35
+ refX: visual.markerRefX.toString(),
36
+ refY: visual.markerRefY.toString(),
37
+ orient: 'auto',
38
+ overflow: 'visible',
39
+ viewBox: visual.markerViewBox
40
+ });
41
+ const markerPath = createSvgElement(marker, 'path', {
42
+ 'data-chessboard-id': markerPathDataId,
43
+ d: visual.markerPathD,
44
+ fill: color
45
+ });
46
+ const line = createSvgElement(visualRoot, 'line', {
47
+ 'data-chessboard-id': lineDataId,
48
+ x1: geo.x1,
49
+ y1: geo.y1,
50
+ x2: geo.x2,
51
+ y2: geo.y2,
52
+ stroke: color,
53
+ 'stroke-width': geo.strokeWidth,
54
+ 'stroke-linecap': 'round',
55
+ opacity: opacity.toString(),
56
+ 'marker-end': markerHref
57
+ });
58
+ return { line, marker, markerPath };
59
+ }
60
+ export function updateAnnotationArrowSvg(rendered, params) {
61
+ const { markerId, markerHref, color, opacity, visual } = params;
62
+ const geo = computeArrowGeometry(params);
63
+ updateSvgElementAttributes(rendered.marker, {
64
+ id: markerId,
65
+ markerUnits: 'strokeWidth',
66
+ markerWidth: visual.markerWidth.toString(),
67
+ markerHeight: visual.markerHeight.toString(),
68
+ refX: visual.markerRefX.toString(),
69
+ refY: visual.markerRefY.toString(),
70
+ orient: 'auto',
71
+ overflow: 'visible',
72
+ viewBox: visual.markerViewBox
73
+ });
74
+ updateSvgElementAttributes(rendered.markerPath, {
75
+ d: visual.markerPathD,
76
+ fill: color
77
+ });
78
+ updateSvgElementAttributes(rendered.line, {
79
+ x1: geo.x1,
80
+ y1: geo.y1,
81
+ x2: geo.x2,
82
+ y2: geo.y2,
83
+ stroke: color,
84
+ 'stroke-width': geo.strokeWidth,
85
+ 'stroke-linecap': 'round',
86
+ opacity: opacity.toString(),
87
+ 'marker-end': markerHref
88
+ });
89
+ }
90
+ export function removeAnnotationArrowSvg(rendered) {
91
+ rendered.line.remove();
92
+ rendered.marker.remove();
93
+ }
@@ -0,0 +1,18 @@
1
+ import type { SceneRenderGeometry } from '../../../../../layout/geometry/types.js';
2
+ import type { Square } from '../../../../../state/board/types/internal.js';
3
+ import type { RenderedAnnotationCircleSvg } from '../../types/main.js';
4
+ export interface AnnotationCircleSvgAttributeParams {
5
+ readonly square: Square;
6
+ readonly color: string;
7
+ readonly radius: number;
8
+ readonly strokeWidth: number;
9
+ readonly opacity: number;
10
+ readonly geometry: SceneRenderGeometry;
11
+ }
12
+ export interface AnnotationCircleSvgCreateParams extends AnnotationCircleSvgAttributeParams {
13
+ readonly parent: SVGGElement;
14
+ readonly dataId: string;
15
+ }
16
+ export declare function createAnnotationCircleSvg(params: AnnotationCircleSvgCreateParams): RenderedAnnotationCircleSvg;
17
+ export declare function updateAnnotationCircleSvg(rendered: RenderedAnnotationCircleSvg, params: AnnotationCircleSvgAttributeParams): void;
18
+ export declare function removeAnnotationCircleSvg(rendered: RenderedAnnotationCircleSvg): void;
@@ -0,0 +1,30 @@
1
+ import { createSvgElement, updateSvgElementAttributes } from '../../../../../render/svg/helpers.js';
2
+ function computeCircleAttributes(params) {
3
+ const { square, color, radius, strokeWidth, opacity, geometry } = params;
4
+ const squareSize = geometry.squareSize;
5
+ const rect = geometry.getSquareRect(square);
6
+ return {
7
+ cx: (rect.x + rect.width / 2).toString(),
8
+ cy: (rect.y + rect.height / 2).toString(),
9
+ r: (squareSize * radius).toString(),
10
+ fill: 'none',
11
+ stroke: color,
12
+ 'stroke-width': (squareSize * strokeWidth).toString(),
13
+ opacity: opacity.toString()
14
+ };
15
+ }
16
+ export function createAnnotationCircleSvg(params) {
17
+ const attributes = computeCircleAttributes(params);
18
+ const circle = createSvgElement(params.parent, 'circle', {
19
+ 'data-chessboard-id': params.dataId,
20
+ ...attributes
21
+ });
22
+ return { circle };
23
+ }
24
+ export function updateAnnotationCircleSvg(rendered, params) {
25
+ const attributes = computeCircleAttributes(params);
26
+ updateSvgElementAttributes(rendered.circle, attributes);
27
+ }
28
+ export function removeAnnotationCircleSvg(rendered) {
29
+ rendered.circle.remove();
30
+ }
@@ -0,0 +1,3 @@
1
+ import type { SceneRenderGeometry } from '../../../../layout/geometry/types.js';
2
+ import type { AnnotationsStateInternal } from '../types/main.js';
3
+ export declare function renderPreviewAnnotations(state: AnnotationsStateInternal, geometry: SceneRenderGeometry): void;
@@ -0,0 +1,96 @@
1
+ import assert from '@ktarmyshov/assert';
2
+ import { VISUAL_CONFIG } from '../constants.js';
3
+ import { arrowAnnotationKeyNormalized } from '../normalize.js';
4
+ import { createAnnotationArrowSvg, removeAnnotationArrowSvg, updateAnnotationArrowSvg } from './helpers/arrows.js';
5
+ import { createAnnotationCircleSvg, removeAnnotationCircleSvg, updateAnnotationCircleSvg } from './helpers/circles.js';
6
+ export function renderPreviewAnnotations(state, geometry) {
7
+ assert(state.slotRoots, 'Expected slotRoots to be initialized before rendering preview');
8
+ const gesture = state.activeDrawGesture;
9
+ const target = state.activeDrawPreviewTarget;
10
+ // No preview needed: clear any existing preview DOM
11
+ if (gesture === null || target === null) {
12
+ clearPreviewCircle(state);
13
+ clearPreviewArrow(state);
14
+ return;
15
+ }
16
+ const source = gesture.sourceSquare;
17
+ const color = gesture.color;
18
+ if (target === source) {
19
+ // Circle preview
20
+ clearPreviewArrow(state);
21
+ renderPreviewCircle(state, geometry, source, color);
22
+ }
23
+ else {
24
+ // Arrow preview
25
+ clearPreviewCircle(state);
26
+ renderPreviewArrowSvg(state, geometry, source, target, color);
27
+ }
28
+ }
29
+ function renderPreviewCircle(state, geometry, square, color) {
30
+ const { previewAdd, previewRemoveOpacity } = VISUAL_CONFIG.circle;
31
+ // Determine add vs remove style
32
+ const existingCircle = state.annotations.circles.get(square);
33
+ const isRemove = existingCircle !== undefined && existingCircle.color === color;
34
+ const opacity = isRemove ? previewRemoveOpacity : previewAdd.opacity;
35
+ assert(state.slotRoots, 'Expected slotRoots to be initialized before rendering preview');
36
+ const params = {
37
+ parent: state.slotRoots.drag,
38
+ dataId: 'annotation-circle-preview',
39
+ square,
40
+ color,
41
+ radius: previewAdd.radius,
42
+ strokeWidth: previewAdd.strokeWidth,
43
+ opacity,
44
+ geometry
45
+ };
46
+ if (state.previewSvg.circle) {
47
+ updateAnnotationCircleSvg(state.previewSvg.circle, params);
48
+ }
49
+ else {
50
+ state.previewSvg.circle = createAnnotationCircleSvg(params);
51
+ }
52
+ }
53
+ function renderPreviewArrowSvg(state, geometry, from, to, color) {
54
+ const { committed, previewAdd, previewRemoveOpacity } = VISUAL_CONFIG.arrow;
55
+ // Determine add vs remove style
56
+ const arrowKey = arrowAnnotationKeyNormalized(from, to);
57
+ const existingArrow = state.annotations.arrows.get(arrowKey);
58
+ const isRemove = existingArrow !== undefined && existingArrow.color === color;
59
+ const visual = isRemove ? committed : previewAdd;
60
+ const opacity = isRemove ? previewRemoveOpacity : previewAdd.opacity;
61
+ const markerId = state.svgIds.makeId('annotations', 'arrowhead-preview');
62
+ const markerHref = `url(${state.svgIds.makeHref('annotations', 'arrowhead-preview')})`;
63
+ const params = {
64
+ defsRoot: state.slotRoots.defs,
65
+ visualRoot: state.slotRoots.drag,
66
+ markerDataId: 'annotation-arrowhead-preview',
67
+ markerPathDataId: 'annotation-arrowhead-path-preview',
68
+ lineDataId: 'annotation-arrow-preview',
69
+ markerId,
70
+ markerHref,
71
+ from,
72
+ to,
73
+ color,
74
+ opacity,
75
+ visual,
76
+ geometry
77
+ };
78
+ if (state.previewSvg.arrow) {
79
+ updateAnnotationArrowSvg(state.previewSvg.arrow, params);
80
+ }
81
+ else {
82
+ state.previewSvg.arrow = createAnnotationArrowSvg(params);
83
+ }
84
+ }
85
+ function clearPreviewCircle(state) {
86
+ if (state.previewSvg.circle) {
87
+ removeAnnotationCircleSvg(state.previewSvg.circle);
88
+ state.previewSvg.circle = null;
89
+ }
90
+ }
91
+ function clearPreviewArrow(state) {
92
+ if (state.previewSvg.arrow) {
93
+ removeAnnotationArrowSvg(state.previewSvg.arrow);
94
+ state.previewSvg.arrow = null;
95
+ }
96
+ }
@@ -0,0 +1,77 @@
1
+ import type { Square } from '../../../../state/board/types/internal.js';
2
+ import type { AnnotationsDrawModifier } from './public.js';
3
+ /** Annotation color as a CSS-compatible color string. */
4
+ export type AnnotationColor = string;
5
+ export type CircleAnnotationKey = Square;
6
+ export type ArrowAnnotationKey = number;
7
+ export interface CircleAnnotation {
8
+ readonly key: CircleAnnotationKey;
9
+ readonly square: Square;
10
+ readonly color: AnnotationColor;
11
+ }
12
+ export interface ArrowAnnotation {
13
+ readonly key: ArrowAnnotationKey;
14
+ readonly from: Square;
15
+ readonly to: Square;
16
+ readonly color: AnnotationColor;
17
+ }
18
+ export interface AnnotationModifierColorConfig {
19
+ readonly none: AnnotationColor;
20
+ readonly ctrl: AnnotationColor;
21
+ readonly shift: AnnotationColor;
22
+ readonly alt: AnnotationColor;
23
+ readonly meta: AnnotationColor;
24
+ }
25
+ export interface AnnotationsConfig {
26
+ clearOnCoreInteraction: boolean;
27
+ drawButton: number;
28
+ drawModifier: AnnotationsDrawModifier | null;
29
+ readonly colors: AnnotationModifierColorConfig;
30
+ }
31
+ export interface ActiveDrawGesture {
32
+ readonly sourceSquare: Square;
33
+ readonly color: AnnotationColor;
34
+ }
35
+ export interface VisualConfigCircleAnnotation {
36
+ strokeWidth: number;
37
+ radius: number;
38
+ opacity: number;
39
+ }
40
+ export interface VisualConfigArrowAnnotation {
41
+ strokeWidth: number;
42
+ startOffset: number;
43
+ endOffset: number;
44
+ opacity: number;
45
+ markerWidth: number;
46
+ markerHeight: number;
47
+ markerRefX: number;
48
+ markerRefY: number;
49
+ markerViewBox: string;
50
+ markerPathD: string;
51
+ }
52
+ export interface VisualConfigCircle {
53
+ readonly committed: VisualConfigCircleAnnotation;
54
+ readonly previewAdd: VisualConfigCircleAnnotation;
55
+ /**
56
+ * Remove preview reuses previewAdd definitions
57
+ * and overrides only opacity.
58
+ */
59
+ readonly previewRemoveOpacity: number;
60
+ }
61
+ export interface VisualConfigArrow {
62
+ readonly committed: VisualConfigArrowAnnotation;
63
+ readonly previewAdd: VisualConfigArrowAnnotation;
64
+ /**
65
+ * Remove preview reuses committed arrow definitions
66
+ * and overrides only opacity.
67
+ */
68
+ readonly previewRemoveOpacity: number;
69
+ }
70
+ export interface VisualConfig {
71
+ readonly circle: VisualConfigCircle;
72
+ readonly arrow: VisualConfigArrow;
73
+ }
74
+ export declare const enum DirtyLayer {
75
+ COMMITTED = 1,
76
+ PREVIEW = 2
77
+ }
@@ -0,0 +1,40 @@
1
+ import type { Square } from '../../../../state/board/types/internal.js';
2
+ import type { ExtensionDefinition, ExtensionInstance } from '../../../types/extension.js';
3
+ import type { ExtensionRuntimeSurface } from '../../../types/surface/main.js';
4
+ import type { ExtensionInternalBase } from '../../common/types.js';
5
+ import type { ActiveDrawGesture, AnnotationsConfig, ArrowAnnotation, ArrowAnnotationKey, CircleAnnotation, CircleAnnotationKey } from './internal.js';
6
+ import type { AnnotationsPublicAPI } from './public.js';
7
+ export declare const EXTENSION_SLOTS: readonly ["defs", "overPieces", "drag"];
8
+ export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
9
+ export declare const EXTENSION_ID: "annotations";
10
+ export type AnnotationsDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, AnnotationsPublicAPI>;
11
+ export type AnnotationsInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, AnnotationsPublicAPI>;
12
+ export interface RenderedAnnotationCircleSvg {
13
+ readonly circle: SVGCircleElement;
14
+ }
15
+ export interface RenderedAnnotationArrowSvg {
16
+ readonly line: SVGLineElement;
17
+ readonly marker: SVGMarkerElement;
18
+ readonly markerPath: SVGPathElement;
19
+ }
20
+ export interface AnnotationsStateInternalSvg {
21
+ readonly svgCircles: Map<CircleAnnotationKey, RenderedAnnotationCircleSvg>;
22
+ readonly svgArrows: Map<ArrowAnnotationKey, RenderedAnnotationArrowSvg>;
23
+ }
24
+ export interface AnnotationsStateInternalAnnotations {
25
+ readonly circles: Map<CircleAnnotationKey, CircleAnnotation>;
26
+ readonly arrows: Map<ArrowAnnotationKey, ArrowAnnotation>;
27
+ }
28
+ export interface AnnotationsStateInternalPreviewSvg {
29
+ circle: RenderedAnnotationCircleSvg | null;
30
+ arrow: RenderedAnnotationArrowSvg | null;
31
+ }
32
+ export interface AnnotationsStateInternal extends ExtensionInternalBase<ExtensionSlotsType> {
33
+ readonly runtimeSurface: ExtensionRuntimeSurface;
34
+ readonly svg: AnnotationsStateInternalSvg;
35
+ readonly annotations: AnnotationsStateInternalAnnotations;
36
+ readonly config: AnnotationsConfig;
37
+ activeDrawGesture: ActiveDrawGesture | null;
38
+ activeDrawPreviewTarget: Square | null;
39
+ readonly previewSvg: AnnotationsStateInternalPreviewSvg;
40
+ }
@@ -0,0 +1,6 @@
1
+ export const EXTENSION_SLOTS = [
2
+ 'defs',
3
+ 'overPieces',
4
+ 'drag'
5
+ ];
6
+ export const EXTENSION_ID = 'annotations';
@@ -0,0 +1,48 @@
1
+ import type { SquareString } from '../../../../state/board/types/input.js';
2
+ /** The annotation color resolved from user input modifiers. */
3
+ export type AnnotationColorPublic = string;
4
+ export interface CircleAnnotationPublic {
5
+ readonly square: SquareString;
6
+ readonly color: AnnotationColorPublic;
7
+ }
8
+ export interface ArrowAnnotationPublic {
9
+ readonly from: SquareString;
10
+ readonly to: SquareString;
11
+ readonly color: AnnotationColorPublic;
12
+ }
13
+ export interface AnnotationsInitOptionsConfigModifierColor {
14
+ none?: AnnotationColorPublic;
15
+ ctrl?: AnnotationColorPublic;
16
+ shift?: AnnotationColorPublic;
17
+ alt?: AnnotationColorPublic;
18
+ meta?: AnnotationColorPublic;
19
+ }
20
+ export type AnnotationsDrawModifier = Exclude<keyof AnnotationsInitOptionsConfigModifierColor, 'none'>;
21
+ export interface AnnotationsInitOptionsConfig {
22
+ clearOnCoreInteraction?: boolean;
23
+ drawButton?: number;
24
+ drawModifier?: AnnotationsDrawModifier | null;
25
+ colors?: AnnotationsInitOptionsConfigModifierColor;
26
+ }
27
+ export interface AnnotationsInitOptionsAnnotations {
28
+ circles?: CircleAnnotationPublic[];
29
+ arrows?: ArrowAnnotationPublic[];
30
+ }
31
+ export interface AnnotationsInitOptions {
32
+ config?: AnnotationsInitOptionsConfig;
33
+ annotations?: AnnotationsInitOptionsAnnotations;
34
+ }
35
+ export type CircleAnnotationValuePublic = Omit<CircleAnnotationPublic, 'square'>;
36
+ export type ArrowAnnotationValuePublic = Omit<ArrowAnnotationPublic, 'from' | 'to'>;
37
+ export interface AnnotationsPublicAPI {
38
+ getCircles(): CircleAnnotationPublic[];
39
+ getArrows(): ArrowAnnotationPublic[];
40
+ setCircles(circles: CircleAnnotationPublic[]): void;
41
+ setArrows(arrows: ArrowAnnotationPublic[]): void;
42
+ circle(square: SquareString, annotation: CircleAnnotationValuePublic | null): void;
43
+ arrow(from: SquareString, to: SquareString, annotation: ArrowAnnotationValuePublic | null): void;
44
+ clear(): void;
45
+ clearOnCoreInteraction: boolean;
46
+ drawButton: number;
47
+ drawModifier: AnnotationsDrawModifier | null;
48
+ }
@@ -0,0 +1,3 @@
1
+ import type { ExtensionUpdateContext } from '../../types/context/update.js';
2
+ import type { AnnotationsStateInternal } from './types/main.js';
3
+ export declare function annotationsOnUpdate(state: AnnotationsStateInternal, context: ExtensionUpdateContext): void;
@@ -0,0 +1,45 @@
1
+ import { isUpdateContextRenderable } from '../../types/context/update.js';
2
+ import { clearCommittedAnnotations, hasCommittedAnnotations } from './committed.js';
3
+ import { EXTENSION_ID } from './types/main.js';
4
+ export function annotationsOnUpdate(state, context) {
5
+ if (state.config.clearOnCoreInteraction &&
6
+ hasCommittedAnnotations(state) &&
7
+ context.mutation.hasMutation({
8
+ causes: ['runtime.interaction.completeCoreDragTo']
9
+ })) {
10
+ clearCommittedAnnotations(state);
11
+ context.invalidation.markDirty(1 /* DirtyLayer.COMMITTED */);
12
+ }
13
+ if (context.mutation.hasMutation({
14
+ causes: ['layout.refreshGeometry']
15
+ }) &&
16
+ isUpdateContextRenderable(context)) {
17
+ context.invalidation.markDirty(1 /* DirtyLayer.COMMITTED */);
18
+ if (state.activeDrawPreviewTarget !== null ||
19
+ state.previewSvg.circle !== null ||
20
+ state.previewSvg.arrow !== null) {
21
+ context.invalidation.markDirty(2 /* DirtyLayer.PREVIEW */);
22
+ }
23
+ }
24
+ // Preview target tracking from ext:draw drag session
25
+ let nextPreviewTarget = null;
26
+ if (state.activeDrawGesture !== null) {
27
+ const dragSession = context.currentFrame.state.interaction.dragSession;
28
+ if (dragSession &&
29
+ dragSession.type === 'ext:draw' &&
30
+ 'owner' in dragSession &&
31
+ dragSession.owner === EXTENSION_ID) {
32
+ nextPreviewTarget = dragSession.targetSquare;
33
+ }
34
+ }
35
+ if (nextPreviewTarget !== state.activeDrawPreviewTarget) {
36
+ state.activeDrawPreviewTarget = nextPreviewTarget;
37
+ context.invalidation.markDirty(2 /* DirtyLayer.PREVIEW */);
38
+ context.invalidation.markDirty(1 /* DirtyLayer.COMMITTED */);
39
+ }
40
+ else if (nextPreviewTarget === null &&
41
+ (state.previewSvg.circle !== null || state.previewSvg.arrow !== null)) {
42
+ context.invalidation.markDirty(2 /* DirtyLayer.PREVIEW */);
43
+ context.invalidation.markDirty(1 /* DirtyLayer.COMMITTED */);
44
+ }
45
+ }
@@ -4,14 +4,14 @@ export function createAutoPromote() {
4
4
  return {
5
5
  id: EXTENSION_ID,
6
6
  slots: EXTENSION_SLOTS,
7
- createInstance() {
8
- return createAutoPromoteInstance();
7
+ createInstance(options) {
8
+ return createAutoPromoteInstance(options);
9
9
  }
10
10
  };
11
11
  }
12
- function createAutoPromoteInternal() {
12
+ function createAutoPromoteInternal(options) {
13
13
  return {
14
- ...extensionCreateInternalBase(),
14
+ ...extensionCreateInternalBase(options),
15
15
  toQueen: false
16
16
  };
17
17
  }
@@ -25,8 +25,8 @@ function createAutoPromoteInstancePublic(state) {
25
25
  }
26
26
  };
27
27
  }
28
- function createAutoPromoteInstance() {
29
- const internalState = createAutoPromoteInternal();
28
+ function createAutoPromoteInstance(options) {
29
+ const internalState = createAutoPromoteInternal(options);
30
30
  const publicInterface = createAutoPromoteInstancePublic(internalState);
31
31
  return {
32
32
  id: EXTENSION_ID,