@neta-art/cohub 8.23.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dist/board/animation.d.ts +57 -181
  2. package/dist/board/animation.js +374 -220
  3. package/dist/board/core/arrow-geometry.d.ts +33 -18
  4. package/dist/board/core/arrow-geometry.js +283 -52
  5. package/dist/board/core/draw-geometry.d.ts +22 -36
  6. package/dist/board/core/draw-geometry.js +148 -85
  7. package/dist/board/core/export-assets.d.ts +3 -6
  8. package/dist/board/core/export-assets.js +0 -2
  9. package/dist/board/core/export-plan.d.ts +12 -63
  10. package/dist/board/core/export-plan.js +55 -94
  11. package/dist/board/core/palette.d.ts +4 -21
  12. package/dist/board/core/palette.js +8 -13
  13. package/dist/board/core/scene.d.ts +33 -0
  14. package/dist/board/core/scene.js +302 -0
  15. package/dist/board/core/shape-definition.d.ts +12 -24
  16. package/dist/board/core/shape-definition.js +0 -19
  17. package/dist/board/core/shape-types.d.ts +6 -120
  18. package/dist/board/core/shape-types.js +5 -5
  19. package/dist/board/core/text-metrics.d.ts +8 -11
  20. package/dist/board/core/text-metrics.js +5 -34
  21. package/dist/board/core/tool-styles.d.ts +6 -16
  22. package/dist/board/core/tool-styles.js +17 -25
  23. package/dist/board/export/index.d.ts +10 -17
  24. package/dist/board/export/index.js +22 -21
  25. package/dist/board/export/scene.d.ts +10 -16
  26. package/dist/board/export/scene.js +17 -30
  27. package/dist/board/geometry.d.ts +27 -56
  28. package/dist/board/geometry.js +7 -67
  29. package/dist/board/headless/index.d.ts +7 -27
  30. package/dist/board/headless/index.js +20 -19
  31. package/dist/board/headless/sketch.d.ts +19 -0
  32. package/dist/board/headless/sketch.js +146 -0
  33. package/dist/board/image-key.d.ts +2 -12
  34. package/dist/board/image-key.js +3 -13
  35. package/dist/board/index.d.ts +18 -20
  36. package/dist/board/index.js +17 -17
  37. package/dist/board/media-playback.d.ts +3 -6
  38. package/dist/board/media-playback.js +6 -6
  39. package/dist/board/presets.d.ts +150 -0
  40. package/dist/board/presets.js +226 -0
  41. package/dist/board/render/board-background.d.ts +5 -3
  42. package/dist/board/render/board-background.js +8 -7
  43. package/dist/board/render/clip.d.ts +14 -0
  44. package/dist/board/render/clip.js +47 -0
  45. package/dist/board/render/index.d.ts +8 -5
  46. package/dist/board/render/index.js +7 -4
  47. package/dist/board/render/media-interaction.d.ts +4 -5
  48. package/dist/board/render/media-interaction.js +1 -2
  49. package/dist/board/render/palette.d.ts +6 -2
  50. package/dist/board/render/palette.js +7 -1
  51. package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
  52. package/dist/board/render/renderers/audio-card-renderer.js +3 -3
  53. package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
  54. package/dist/board/render/renderers/base-card-renderer.js +1 -24
  55. package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
  56. package/dist/board/render/renderers/board-renderer-registry.js +6 -7
  57. package/dist/board/render/renderers/draw-card-renderer.js +89 -56
  58. package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
  59. package/dist/board/render/renderers/effect-card-renderer.js +197 -0
  60. package/dist/board/render/renderers/far-plate.d.ts +4 -21
  61. package/dist/board/render/renderers/far-plate.js +15 -34
  62. package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
  63. package/dist/board/render/renderers/file-card-renderer.js +8 -58
  64. package/dist/board/render/renderers/frame-card-renderer.js +28 -28
  65. package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
  66. package/dist/board/render/renderers/image-card-renderer.js +0 -9
  67. package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
  68. package/dist/board/render/renderers/shape-card-renderer.js +240 -0
  69. package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
  70. package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
  71. package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
  72. package/dist/board/render/renderers/task-card-renderer.js +14 -16
  73. package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
  74. package/dist/board/render/renderers/text-card-renderer.js +56 -46
  75. package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
  76. package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
  77. package/dist/board/render/renderers/video-card-renderer.js +2 -1
  78. package/dist/board/render/sketch-host.d.ts +14 -0
  79. package/dist/board/render/sketch-host.js +216 -0
  80. package/dist/board/render/stroke.d.ts +12 -0
  81. package/dist/board/render/stroke.js +90 -0
  82. package/dist/board/render/text-measurement.d.ts +6 -26
  83. package/dist/board/render/text-measurement.js +11 -30
  84. package/dist/board/replay.d.ts +6 -31
  85. package/dist/board/replay.js +45 -203
  86. package/dist/board/runtime/board-player.d.ts +24 -0
  87. package/dist/board/runtime/board-player.js +161 -0
  88. package/dist/board/task.d.ts +2 -10
  89. package/dist/board/task.js +1 -10
  90. package/dist/chunks/environment.d.ts +2296 -3864
  91. package/dist/chunks/host-bridge.d.ts +253 -0
  92. package/dist/chunks/http.d.ts +238 -58
  93. package/dist/chunks/http.js +1061 -1034
  94. package/dist/chunks/transport.js +113 -59
  95. package/dist/chunks/websocket.d.ts +1 -1
  96. package/dist/chunks/websocket.js +1 -1
  97. package/dist/host-bridge.d.ts +54 -0
  98. package/dist/host-bridge.js +322 -0
  99. package/dist/http.d.ts +3 -3
  100. package/dist/index.d.ts +76 -193
  101. package/dist/index.js +2037 -1191
  102. package/dist/protocol/dist/board-constants.d.ts +1 -29
  103. package/dist/protocol/dist/board-constants.js +2 -115
  104. package/dist/protocol/dist/board-layout.d.ts +59 -0
  105. package/dist/protocol/dist/board-layout.js +482 -0
  106. package/dist/protocol/dist/board-model.d.ts +2052 -0
  107. package/dist/protocol/dist/board-model.js +592 -0
  108. package/dist/protocol/dist/board-patch.d.ts +86 -0
  109. package/dist/protocol/dist/board-patch.js +734 -0
  110. package/dist/protocol/dist/board-schema.d.ts +33 -0
  111. package/dist/protocol/dist/board-schema.js +59 -0
  112. package/dist/protocol/dist/board-upgrade.d.ts +1 -0
  113. package/dist/protocol/dist/board-url.d.ts +1 -3
  114. package/dist/protocol/dist/board-url.js +2 -2
  115. package/dist/protocol/dist/board.d.ts +37 -232
  116. package/dist/protocol/dist/board.js +62 -160
  117. package/dist/protocol/dist/display/actions.d.ts +1 -0
  118. package/dist/protocol/dist/display/index.d.ts +2 -0
  119. package/dist/protocol/dist/index.d.ts +11 -12
  120. package/dist/protocol/dist/model/metrics.d.ts +2 -0
  121. package/dist/protocol/dist/model/session.d.ts +2 -1
  122. package/dist/protocol/dist/realtime/types.d.ts +2 -2
  123. package/dist/protocol/dist/runtime/index.d.ts +1 -0
  124. package/dist/space-picker.d.ts +1 -0
  125. package/dist/space-picker.js +1 -0
  126. package/dist/types.d.ts +2 -5
  127. package/package.json +9 -1
  128. package/dist/board/core/connections.d.ts +0 -108
  129. package/dist/board/core/connections.js +0 -455
  130. package/dist/board/mutation.d.ts +0 -6
  131. package/dist/board/mutation.js +0 -14
  132. package/dist/board/render/connection-layer.d.ts +0 -53
  133. package/dist/board/render/connection-layer.js +0 -375
  134. package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
  135. package/dist/board/render/renderers/geo-card-renderer.js +0 -127
  136. package/dist/board/semantic-document.d.ts +0 -37
  137. package/dist/board/semantic-document.js +0 -288
  138. package/dist/board/semantic-mutation.d.ts +0 -10
  139. package/dist/board/semantic-mutation.js +0 -209
  140. package/dist/protocol/dist/board-animation.js +0 -34
  141. package/dist/protocol/dist/board-authoring.d.ts +0 -1326
  142. package/dist/protocol/dist/board-authoring.js +0 -302
  143. package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
  144. package/dist/protocol/dist/board-capability-registry.js +0 -123
  145. package/dist/protocol/dist/board-codec.d.ts +0 -3
  146. package/dist/protocol/dist/board-codec.js +0 -383
  147. package/dist/protocol/dist/board-composition.d.ts +0 -383
  148. package/dist/protocol/dist/board-composition.js +0 -310
  149. package/dist/protocol/dist/board-connection.d.ts +0 -317
  150. package/dist/protocol/dist/board-connection.js +0 -219
  151. package/dist/protocol/dist/board-document.d.ts +0 -1566
  152. package/dist/protocol/dist/board-document.js +0 -456
  153. package/dist/protocol/dist/board-effect.d.ts +0 -96
  154. package/dist/protocol/dist/board-effect.js +0 -54
  155. package/dist/protocol/dist/board-geometry.d.ts +0 -15
  156. package/dist/protocol/dist/board-geometry.js +0 -137
  157. package/dist/protocol/dist/board-node.d.ts +0 -8
  158. package/dist/protocol/dist/board-node.js +0 -252
  159. /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
@@ -1,23 +1,38 @@
1
- import { BoardArrowItem, BoardFrame } from "../../protocol/dist/board-document.js";
1
+ import { BoardArrowAnchor, BoardArrowItem } from "../../protocol/dist/board-model.js";
2
+ import { BoardFrame, BoardLayout, BoardMatrix } from "../../protocol/dist/board-layout.js";
3
+ import "../../protocol/dist/index.js";
2
4
  import { Rect, WorldPoint } from "../geometry.js";
3
5
  //#region src/board/core/arrow-geometry.d.ts
6
+ declare const ARROW_BINDING_GAP = 4;
7
+ type ResolvedArrowEnd = {
8
+ point: WorldPoint;
9
+ normal: WorldPoint;
10
+ item?: string;
11
+ };
4
12
  type ResolvedArrow = {
5
- start: WorldPoint;
6
- end: WorldPoint;
7
- /** Quadratic control point (already bent); the midpoint when bend is 0. */
8
- control: WorldPoint;
13
+ start: ResolvedArrowEnd;
14
+ end: ResolvedArrowEnd;
15
+ path: WorldPoint[];
16
+ mid: WorldPoint;
17
+ };
18
+ type ArrowFrameLookup = (id: string) => BoardFrame | undefined;
19
+ declare function resolveAnchor(anchor: BoardArrowAnchor, frame: BoardFrame, toward: WorldPoint, gap?: number): {
20
+ point: WorldPoint;
21
+ normal: WorldPoint;
9
22
  };
10
- /** Resolve an arrow's endpoints and its bent control point. */
11
- declare function resolveArrow(item: BoardArrowItem): ResolvedArrow;
12
- /** Sample an arrow's quadratic curve into world points. */
13
- declare function sampleArrow(resolved: ResolvedArrow, segments: number): WorldPoint[];
14
- /** World bounds of an arrow, padded for its stroke. */
15
- declare function arrowBounds(item: BoardArrowItem): Rect;
16
- /** The frame an arrow should carry, derived from its endpoints. */
17
- declare function arrowFrame(item: BoardArrowItem): BoardFrame;
18
- /** Distance from a world point to an arrow's curve (hit testing). */
19
- declare function distanceToArrow(item: BoardArrowItem, point: WorldPoint): number;
20
- /** Translate an arrow by a delta, keeping its frame consistent. */
21
- declare function translateArrow(item: BoardArrowItem, dx: number, dy: number): BoardArrowItem;
23
+ declare function resolveArrow(item: BoardArrowItem, matrix: BoardMatrix, getFrame: ArrowFrameLookup): ResolvedArrow;
24
+ declare function resolveSceneArrow(item: BoardArrowItem & {
25
+ id: string;
26
+ }, scene: {
27
+ layout: BoardLayout;
28
+ get(id: string): {
29
+ frame: BoardFrame;
30
+ } | undefined;
31
+ }): ResolvedArrow;
32
+ declare function pathMidpoint(path: readonly WorldPoint[]): WorldPoint;
33
+ declare function pathPointAt(path: readonly WorldPoint[], fraction: number): WorldPoint;
34
+ declare function arrowPathBounds(resolved: ResolvedArrow, strokeWidth?: number): Rect;
35
+ declare function distanceToArrow(resolved: ResolvedArrow, point: WorldPoint): number;
36
+ declare function arrowHitRadius(strokeWidth?: number): number;
22
37
  //#endregion
23
- export { ResolvedArrow, arrowBounds, arrowFrame, distanceToArrow, resolveArrow, sampleArrow, translateArrow };
38
+ export { ARROW_BINDING_GAP, ArrowFrameLookup, ResolvedArrow, ResolvedArrowEnd, arrowHitRadius, arrowPathBounds, distanceToArrow, pathMidpoint, pathPointAt, resolveAnchor, resolveArrow, resolveSceneArrow };
@@ -1,48 +1,283 @@
1
- import { boardArrowBounds, boardArrowFrame, boardResolveArrow, boardSampleResolvedArrow } from "../../protocol/dist/board-geometry.js";
1
+ import { BOARD_ARROW_STROKE_SIZE } from "../../protocol/dist/board-constants.js";
2
+ import { applyMatrix, autoAnchorSide, bendControl, framePoint, isArrowBinding, sideAnchor } from "../../protocol/dist/board-layout.js";
2
3
  import { worldPoint } from "../geometry.js";
3
4
  //#region src/board/core/arrow-geometry.ts
4
- /**
5
- * Free-arrow geometry.
6
- *
7
- * An arrow is a standalone annotation stroke between two world points — it does
8
- * not relate nodes. Node relations are `BoardConnection`s, which resolve their
9
- * geometry from the nodes they join (see ./connections). Keeping the two apart is
10
- * what makes each one simple: an arrow owns absolute coordinates and needs no
11
- * lookup, while a connection owns no coordinates at all.
12
- */
13
- /** Resolve an arrow's endpoints and its bent control point. */
14
- function resolveArrow(item) {
15
- const resolved = boardResolveArrow(item);
5
+ const ARROW_BINDING_GAP = 4;
6
+ const CURVE_SEGMENTS = 20;
7
+ const ORTHOGONAL_SNAP = .5;
8
+ const SIDE_NORMALS = {
9
+ top: worldPoint(0, -1),
10
+ right: worldPoint(1, 0),
11
+ bottom: worldPoint(0, 1),
12
+ left: worldPoint(-1, 0)
13
+ };
14
+ function rotate(vector, degrees) {
15
+ if (!degrees) return vector;
16
+ const radians = degrees * Math.PI / 180;
17
+ const cos = Math.cos(radians);
18
+ const sin = Math.sin(radians);
19
+ return worldPoint(vector.x * cos - vector.y * sin, vector.x * sin + vector.y * cos);
20
+ }
21
+ function nearestSide(x, y) {
22
+ const distances = [
23
+ ["left", x],
24
+ ["right", 1 - x],
25
+ ["top", y],
26
+ ["bottom", 1 - y]
27
+ ];
28
+ distances.sort((a, b) => a[1] - b[1]);
29
+ return distances[0]?.[0] ?? "right";
30
+ }
31
+ function center(frame) {
32
+ return worldPoint(frame.x + frame.width / 2, frame.y + frame.height / 2);
33
+ }
34
+ function resolveAnchor(anchor, frame, toward, gap = 4) {
35
+ let side;
36
+ let normalized;
37
+ if (anchor === "auto") {
38
+ side = autoAnchorSide(frame, toward);
39
+ normalized = sideAnchor(side, .5);
40
+ } else if ("side" in anchor) {
41
+ side = anchor.side;
42
+ normalized = sideAnchor(side, anchor.offset);
43
+ } else {
44
+ normalized = anchor;
45
+ side = nearestSide(anchor.x, anchor.y);
46
+ }
47
+ const base = framePoint(frame, normalized.x, normalized.y);
48
+ const normal = rotate(SIDE_NORMALS[side], frame.rotation);
16
49
  return {
17
- start: worldPoint(resolved.start.x, resolved.start.y),
18
- end: worldPoint(resolved.end.x, resolved.end.y),
19
- control: worldPoint(resolved.control.x, resolved.control.y)
50
+ point: worldPoint(base.x + normal.x * gap, base.y + normal.y * gap),
51
+ normal
20
52
  };
21
53
  }
22
- /** Sample an arrow's quadratic curve into world points. */
23
- function sampleArrow(resolved, segments) {
24
- return boardSampleResolvedArrow(resolved, segments).map((point) => worldPoint(point.x, point.y));
54
+ function toWorld(matrix, point) {
55
+ const mapped = applyMatrix(matrix, point);
56
+ return worldPoint(mapped.x, mapped.y);
25
57
  }
26
- /** World bounds of an arrow, padded for its stroke. */
27
- function arrowBounds(item) {
28
- return boardArrowBounds(item);
58
+ function resolveEnd(end, other, matrix, getFrame) {
59
+ const origin = toWorld(matrix, {
60
+ x: 0,
61
+ y: 0
62
+ });
63
+ const otherPoint = (() => {
64
+ if (!isArrowBinding(other)) return toWorld(matrix, other);
65
+ const frame = getFrame(other.item);
66
+ return frame ? center(frame) : origin;
67
+ })();
68
+ if (isArrowBinding(end)) {
69
+ const frame = getFrame(end.item);
70
+ if (frame) return {
71
+ ...resolveAnchor(end.anchor, frame, otherPoint),
72
+ item: end.item
73
+ };
74
+ }
75
+ const point = isArrowBinding(end) ? origin : toWorld(matrix, end);
76
+ const dx = point.x - otherPoint.x;
77
+ const dy = point.y - otherPoint.y;
78
+ const length = Math.hypot(dx, dy) || 1;
79
+ return {
80
+ point,
81
+ normal: worldPoint(dx / length, dy / length)
82
+ };
29
83
  }
30
- /** The frame an arrow should carry, derived from its endpoints. */
31
- function arrowFrame(item) {
32
- return boardArrowFrame(item);
84
+ function quadratic(start, control, end) {
85
+ const out = [];
86
+ for (let index = 0; index <= CURVE_SEGMENTS; index += 1) {
87
+ const t = index / CURVE_SEGMENTS;
88
+ const mt = 1 - t;
89
+ out.push(worldPoint(mt * mt * start.x + 2 * mt * t * control.x + t * t * end.x, mt * mt * start.y + 2 * mt * t * control.y + t * t * end.y));
90
+ }
91
+ return out;
33
92
  }
34
- /** Distance from a world point to an arrow's curve (hit testing). */
35
- function distanceToArrow(item, point) {
36
- const samples = sampleArrow(resolveArrow(item), 16);
37
- let min = Number.POSITIVE_INFINITY;
38
- for (let index = 0; index < samples.length - 1; index += 1) {
39
- const from = samples[index];
40
- const to = samples[index + 1];
41
- if (!from || !to) continue;
42
- const distance = segmentDistance(point, from, to);
43
- if (distance < min) min = distance;
93
+ function orthogonal(start, end) {
94
+ const horizontalStart = Math.abs(start.normal.x) > Math.abs(start.normal.y);
95
+ const horizontalEnd = Math.abs(end.normal.x) > Math.abs(end.normal.y);
96
+ const a = start.point;
97
+ const b = end.point;
98
+ if (horizontalStart && horizontalEnd) {
99
+ const midX = a.x + (b.x - a.x) * ORTHOGONAL_SNAP;
100
+ return [
101
+ a,
102
+ worldPoint(midX, a.y),
103
+ worldPoint(midX, b.y),
104
+ b
105
+ ];
44
106
  }
45
- return min;
107
+ if (!horizontalStart && !horizontalEnd) {
108
+ const midY = a.y + (b.y - a.y) * ORTHOGONAL_SNAP;
109
+ return [
110
+ a,
111
+ worldPoint(a.x, midY),
112
+ worldPoint(b.x, midY),
113
+ b
114
+ ];
115
+ }
116
+ return horizontalStart ? [
117
+ a,
118
+ worldPoint(b.x, a.y),
119
+ b
120
+ ] : [
121
+ a,
122
+ worldPoint(a.x, b.y),
123
+ b
124
+ ];
125
+ }
126
+ function curveControl(start, end, bend) {
127
+ if (bend) return bendControl(start.point, end.point, bend);
128
+ const mid = worldPoint((start.point.x + end.point.x) / 2, (start.point.y + end.point.y) / 2);
129
+ const normals = [start, end].filter((entry) => entry.item).map((entry) => entry.normal);
130
+ if (!normals.length) return mid;
131
+ const nx = normals.reduce((sum, normal) => sum + normal.x, 0) / normals.length;
132
+ const ny = normals.reduce((sum, normal) => sum + normal.y, 0) / normals.length;
133
+ const magnitude = Math.hypot(nx, ny);
134
+ if (magnitude < 1e-4) return mid;
135
+ const length = Math.hypot(end.point.x - start.point.x, end.point.y - start.point.y);
136
+ const strength = Math.min(length * .18, 96);
137
+ return worldPoint(mid.x + nx / magnitude * strength, mid.y + ny / magnitude * strength);
138
+ }
139
+ function selfLoop(frame) {
140
+ const size = Math.max(Math.min(frame.width, frame.height) * .45, 24);
141
+ const right = frame.x + frame.width + 4;
142
+ const top = frame.y - 4;
143
+ const midY = frame.y + frame.height * .3;
144
+ const midX = frame.x + frame.width * .7;
145
+ return [
146
+ worldPoint(midX, top),
147
+ worldPoint(midX, top - size),
148
+ worldPoint(right + size, top - size),
149
+ worldPoint(right + size, midY),
150
+ worldPoint(right, midY)
151
+ ];
152
+ }
153
+ function resolveArrow(item, matrix, getFrame) {
154
+ const { props } = item;
155
+ if (isArrowBinding(props.start) && isArrowBinding(props.end) && props.start.item === props.end.item) {
156
+ const frame = getFrame(props.start.item);
157
+ if (frame) {
158
+ const path = selfLoop(frame);
159
+ const first = path[0];
160
+ const last = path[path.length - 1];
161
+ return {
162
+ start: {
163
+ point: first,
164
+ normal: worldPoint(0, -1),
165
+ item: props.start.item
166
+ },
167
+ end: {
168
+ point: last,
169
+ normal: worldPoint(1, 0),
170
+ item: props.end.item
171
+ },
172
+ path,
173
+ mid: path[2]
174
+ };
175
+ }
176
+ }
177
+ const start = resolveEnd(props.start, props.end, matrix, getFrame);
178
+ const end = resolveEnd(props.end, props.start, matrix, getFrame);
179
+ let path;
180
+ if (props.waypoints.length) path = [
181
+ start.point,
182
+ ...props.waypoints.map((point) => toWorld(matrix, point)),
183
+ end.point
184
+ ];
185
+ else if (props.route === "orthogonal") path = orthogonal(start, end);
186
+ else if (props.route === "straight" && !props.bend) path = [start.point, end.point];
187
+ else path = quadratic(start.point, curveControl(start, end, props.bend), end.point);
188
+ return {
189
+ start,
190
+ end,
191
+ path,
192
+ mid: pathMidpoint(path)
193
+ };
194
+ }
195
+ function resolveSceneArrow(item, scene) {
196
+ const getFrame = (id) => scene.get(id)?.frame;
197
+ const matrix = scene.layout.matrix(item.id);
198
+ const key = arrowKey(item, matrix, getFrame);
199
+ const cached = resolvedCache.get(item);
200
+ if (cached && cached.key === key) return cached.resolved;
201
+ const resolved = resolveArrow(item, matrix, getFrame);
202
+ resolvedCache.set(item, {
203
+ key,
204
+ resolved
205
+ });
206
+ return resolved;
207
+ }
208
+ const resolvedCache = /* @__PURE__ */ new WeakMap();
209
+ function frameKey(frame) {
210
+ return frame ? `${frame.x},${frame.y},${frame.width},${frame.height},${frame.rotation}` : "-";
211
+ }
212
+ function arrowKey(item, matrix, getFrame) {
213
+ const { start, end } = item.props;
214
+ return `${matrix.join(",")}|${isArrowBinding(start) ? frameKey(getFrame(start.item)) : ""}|${isArrowBinding(end) ? frameKey(getFrame(end.item)) : ""}`;
215
+ }
216
+ function pathMidpoint(path) {
217
+ if (path.length === 0) return worldPoint(0, 0);
218
+ if (path.length === 1) return path[0];
219
+ let total = 0;
220
+ for (let index = 0; index < path.length - 1; index += 1) {
221
+ const from = path[index];
222
+ const to = path[index + 1];
223
+ total += Math.hypot(to.x - from.x, to.y - from.y);
224
+ }
225
+ let travelled = 0;
226
+ const half = total / 2;
227
+ for (let index = 0; index < path.length - 1; index += 1) {
228
+ const from = path[index];
229
+ const to = path[index + 1];
230
+ const segment = Math.hypot(to.x - from.x, to.y - from.y);
231
+ if (travelled + segment >= half) {
232
+ const t = segment > 0 ? (half - travelled) / segment : 0;
233
+ return worldPoint(from.x + (to.x - from.x) * t, from.y + (to.y - from.y) * t);
234
+ }
235
+ travelled += segment;
236
+ }
237
+ return path[path.length - 1];
238
+ }
239
+ function pathPointAt(path, fraction) {
240
+ if (path.length < 2) return path[0] ?? worldPoint(0, 0);
241
+ const lengths = [];
242
+ let total = 0;
243
+ for (let index = 0; index < path.length - 1; index += 1) {
244
+ const from = path[index];
245
+ const to = path[index + 1];
246
+ const length = Math.hypot(to.x - from.x, to.y - from.y);
247
+ lengths.push(length);
248
+ total += length;
249
+ }
250
+ let remaining = Math.min(1, Math.max(0, fraction)) * total;
251
+ for (let index = 0; index < lengths.length; index += 1) {
252
+ const length = lengths[index];
253
+ if (remaining <= length) {
254
+ const from = path[index];
255
+ const to = path[index + 1];
256
+ const t = length > 0 ? remaining / length : 0;
257
+ return worldPoint(from.x + (to.x - from.x) * t, from.y + (to.y - from.y) * t);
258
+ }
259
+ remaining -= length;
260
+ }
261
+ return path[path.length - 1];
262
+ }
263
+ function arrowPathBounds(resolved, strokeWidth = BOARD_ARROW_STROKE_SIZE) {
264
+ let minX = Number.POSITIVE_INFINITY;
265
+ let minY = Number.POSITIVE_INFINITY;
266
+ let maxX = Number.NEGATIVE_INFINITY;
267
+ let maxY = Number.NEGATIVE_INFINITY;
268
+ for (const point of resolved.path) {
269
+ minX = Math.min(minX, point.x);
270
+ minY = Math.min(minY, point.y);
271
+ maxX = Math.max(maxX, point.x);
272
+ maxY = Math.max(maxY, point.y);
273
+ }
274
+ const pad = Math.max(8, strokeWidth * 2);
275
+ return {
276
+ x: minX - pad,
277
+ y: minY - pad,
278
+ width: Math.max(1, maxX - minX + pad * 2),
279
+ height: Math.max(1, maxY - minY + pad * 2)
280
+ };
46
281
  }
47
282
  function segmentDistance(p, a, b) {
48
283
  const dx = b.x - a.x;
@@ -52,21 +287,17 @@ function segmentDistance(p, a, b) {
52
287
  const t = Math.min(1, Math.max(0, ((p.x - a.x) * dx + (p.y - a.y) * dy) / lengthSq));
53
288
  return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
54
289
  }
55
- /** Translate an arrow by a delta, keeping its frame consistent. */
56
- function translateArrow(item, dx, dy) {
57
- const move = (point) => ({
58
- x: point.x + dx,
59
- y: point.y + dy
60
- });
61
- const next = {
62
- ...item,
63
- start: move(item.start),
64
- end: move(item.end)
65
- };
66
- return {
67
- ...next,
68
- frame: arrowFrame(next)
69
- };
290
+ function distanceToArrow(resolved, point) {
291
+ let min = Number.POSITIVE_INFINITY;
292
+ for (let index = 0; index < resolved.path.length - 1; index += 1) {
293
+ const from = resolved.path[index];
294
+ const to = resolved.path[index + 1];
295
+ if (from && to) min = Math.min(min, segmentDistance(point, from, to));
296
+ }
297
+ return min;
298
+ }
299
+ function arrowHitRadius(strokeWidth = BOARD_ARROW_STROKE_SIZE) {
300
+ return Math.max(8, strokeWidth * 2.5);
70
301
  }
71
302
  //#endregion
72
- export { arrowBounds, arrowFrame, distanceToArrow, resolveArrow, sampleArrow, translateArrow };
303
+ export { ARROW_BINDING_GAP, arrowHitRadius, arrowPathBounds, distanceToArrow, pathMidpoint, pathPointAt, resolveAnchor, resolveArrow, resolveSceneArrow };
@@ -1,49 +1,35 @@
1
- import { DrawPoint } from "../../protocol/dist/board-document.js";
2
- import { boardDrawBounds, boardDrawSampleRadius } from "../../protocol/dist/board-geometry.js";
1
+ import { BoardDrawPoint } from "../../protocol/dist/board-model.js";
2
+ import { drawPointsBounds, drawSampleRadius } from "../../protocol/dist/board-layout.js";
3
3
  import "../../protocol/dist/index.js";
4
4
  import { WorldPoint } from "../geometry.js";
5
5
  //#region src/board/core/draw-geometry.d.ts
6
- /** Radius of a sample in world units given the stroke size and pressure. */
7
- declare const sampleRadius: typeof boardDrawSampleRadius;
8
- /** Axis-aligned bounds of a stroke in its local space, padded by stroke width. */
9
- declare const computeDrawBounds: typeof boardDrawBounds;
10
- /**
11
- * Ramer–Douglas–Peucker simplification. Reduces point count for low-zoom
12
- * rendering without touching the persisted raw samples. Returns indices into
13
- * the input so callers can keep pressure alongside the simplified path.
14
- */
15
- declare function simplifyDrawIndices(points: DrawPoint[], tolerance: number): number[];
16
- /**
17
- * Build a closed outline for callers that need a path representation.
18
- * Interactive rendering uses `buildStrokeRibbonGeometry` below because a single
19
- * outline is unsafe when a freehand path folds back over itself.
20
- */
21
- declare function buildStrokeOutline(points: DrawPoint[], size: number): Array<{
6
+ declare const sampleRadius: typeof drawSampleRadius;
7
+ declare const computeDrawBounds: typeof drawPointsBounds;
8
+ declare function simplifyDrawIndices(points: BoardDrawPoint[], tolerance: number): number[];
9
+ declare function trimDrawPoints(points: readonly BoardDrawPoint[], trim: number | undefined): readonly BoardDrawPoint[];
10
+ declare function buildStrokeOutline(points: BoardDrawPoint[], size: number): Array<{
22
11
  x: number;
23
12
  y: number;
24
13
  }>;
25
- /** Whether a sample needs a round join rather than the neighboring segment caps. */
26
- declare function isStrokeCorner(points: readonly DrawPoint[], index: number): boolean;
14
+ declare function isStrokeCorner(points: readonly BoardDrawPoint[], index: number): boolean;
27
15
  type StrokeRibbonGeometry = {
28
16
  positions: Float32Array;
29
17
  indices: Uint32Array;
30
18
  uvs: Float32Array;
31
- /** Normalized distance along the centerline for reveal animations. */
32
19
  progress: Float32Array;
33
20
  };
34
- /**
35
- * Tessellate a freehand stroke as independent, convex primitives.
36
- *
37
- * A whole-path polygon is deliberately avoided: a path that folds back can make
38
- * its outline self-intersect, and GPU polygon triangulation then creates a large
39
- * accidental fill. Segment quads plus round point joins overlap safely instead.
40
- */
41
- declare function buildStrokeRibbonGeometry(points: readonly DrawPoint[], size: number): StrokeRibbonGeometry;
42
- /**
43
- * Distance from a world point to the stroke's polyline, in the shape's local
44
- * space. Used for hit testing: a hit registers within half the stroke width plus
45
- * a small tolerance. `local` is the point expressed in the draw item's frame.
46
- */
47
- declare function distanceToStroke(points: DrawPoint[], local: WorldPoint): number;
21
+ type StrokeRibbonMesh = {
22
+ positions: Float32Array;
23
+ indices: Uint32Array;
24
+ distances: Float32Array;
25
+ length: number;
26
+ };
27
+ type StrokeRibbonBuilder = {
28
+ build(points: readonly BoardDrawPoint[]): StrokeRibbonMesh;
29
+ };
30
+ /** A growing stroke re-emits only its last two samples. */
31
+ declare function createStrokeRibbonBuilder(size: number): StrokeRibbonBuilder;
32
+ declare function buildStrokeRibbonGeometry(points: readonly BoardDrawPoint[], size: number): StrokeRibbonGeometry;
33
+ declare function distanceToStroke(points: BoardDrawPoint[], local: WorldPoint): number;
48
34
  //#endregion
49
- export { StrokeRibbonGeometry, buildStrokeOutline, buildStrokeRibbonGeometry, computeDrawBounds, distanceToStroke, isStrokeCorner, sampleRadius, simplifyDrawIndices };
35
+ export { StrokeRibbonBuilder, StrokeRibbonGeometry, StrokeRibbonMesh, buildStrokeOutline, buildStrokeRibbonGeometry, computeDrawBounds, createStrokeRibbonBuilder, distanceToStroke, isStrokeCorner, sampleRadius, simplifyDrawIndices, trimDrawPoints };