@oh-just-another/scene 0.57.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 (129) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/a11y.d.ts +23 -0
  6. package/dist/a11y.d.ts.map +1 -0
  7. package/dist/a11y.js +37 -0
  8. package/dist/a11y.js.map +1 -0
  9. package/dist/anchors.d.ts +120 -0
  10. package/dist/anchors.d.ts.map +1 -0
  11. package/dist/anchors.js +322 -0
  12. package/dist/anchors.js.map +1 -0
  13. package/dist/annotation-geometry.d.ts +11 -0
  14. package/dist/annotation-geometry.d.ts.map +1 -0
  15. package/dist/annotation-geometry.js +19 -0
  16. package/dist/annotation-geometry.js.map +1 -0
  17. package/dist/annotation.d.ts +32 -0
  18. package/dist/annotation.d.ts.map +1 -0
  19. package/dist/annotation.js +2 -0
  20. package/dist/annotation.js.map +1 -0
  21. package/dist/constants.d.ts +186 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +186 -0
  24. package/dist/constants.js.map +1 -0
  25. package/dist/container.d.ts +112 -0
  26. package/dist/container.d.ts.map +1 -0
  27. package/dist/container.js +227 -0
  28. package/dist/container.js.map +1 -0
  29. package/dist/diff.d.ts +22 -0
  30. package/dist/diff.d.ts.map +1 -0
  31. package/dist/diff.js +18 -0
  32. package/dist/diff.js.map +1 -0
  33. package/dist/edge-curve.d.ts +37 -0
  34. package/dist/edge-curve.d.ts.map +1 -0
  35. package/dist/edge-curve.js +72 -0
  36. package/dist/edge-curve.js.map +1 -0
  37. package/dist/edge-geometry.d.ts +96 -0
  38. package/dist/edge-geometry.d.ts.map +1 -0
  39. package/dist/edge-geometry.js +467 -0
  40. package/dist/edge-geometry.js.map +1 -0
  41. package/dist/edge.d.ts +206 -0
  42. package/dist/edge.d.ts.map +1 -0
  43. package/dist/edge.js +2 -0
  44. package/dist/edge.js.map +1 -0
  45. package/dist/elbow-link.d.ts +23 -0
  46. package/dist/elbow-link.d.ts.map +1 -0
  47. package/dist/elbow-link.js +419 -0
  48. package/dist/elbow-link.js.map +1 -0
  49. package/dist/elbow-router.d.ts +20 -0
  50. package/dist/elbow-router.d.ts.map +1 -0
  51. package/dist/elbow-router.js +218 -0
  52. package/dist/elbow-router.js.map +1 -0
  53. package/dist/file.d.ts +41 -0
  54. package/dist/file.d.ts.map +1 -0
  55. package/dist/file.js +13 -0
  56. package/dist/file.js.map +1 -0
  57. package/dist/heading.d.ts +42 -0
  58. package/dist/heading.d.ts.map +1 -0
  59. package/dist/heading.js +77 -0
  60. package/dist/heading.js.map +1 -0
  61. package/dist/index.d.ts +47 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +40 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/layer.d.ts +18 -0
  66. package/dist/layer.d.ts.map +1 -0
  67. package/dist/layer.js +2 -0
  68. package/dist/layer.js.map +1 -0
  69. package/dist/layout-registry.d.ts +35 -0
  70. package/dist/layout-registry.d.ts.map +1 -0
  71. package/dist/layout-registry.js +13 -0
  72. package/dist/layout-registry.js.map +1 -0
  73. package/dist/layout.d.ts +128 -0
  74. package/dist/layout.d.ts.map +1 -0
  75. package/dist/layout.js +453 -0
  76. package/dist/layout.js.map +1 -0
  77. package/dist/operations.d.ts +32 -0
  78. package/dist/operations.d.ts.map +1 -0
  79. package/dist/operations.js +105 -0
  80. package/dist/operations.js.map +1 -0
  81. package/dist/outline.d.ts +36 -0
  82. package/dist/outline.d.ts.map +1 -0
  83. package/dist/outline.js +129 -0
  84. package/dist/outline.js.map +1 -0
  85. package/dist/patch.d.ts +57 -0
  86. package/dist/patch.d.ts.map +1 -0
  87. package/dist/patch.js +47 -0
  88. package/dist/patch.js.map +1 -0
  89. package/dist/queries.d.ts +115 -0
  90. package/dist/queries.d.ts.map +1 -0
  91. package/dist/queries.js +403 -0
  92. package/dist/queries.js.map +1 -0
  93. package/dist/render-bounds.d.ts +36 -0
  94. package/dist/render-bounds.d.ts.map +1 -0
  95. package/dist/render-bounds.js +36 -0
  96. package/dist/render-bounds.js.map +1 -0
  97. package/dist/scene.d.ts +80 -0
  98. package/dist/scene.d.ts.map +1 -0
  99. package/dist/scene.js +129 -0
  100. package/dist/scene.js.map +1 -0
  101. package/dist/shape.d.ts +306 -0
  102. package/dist/shape.d.ts.map +1 -0
  103. package/dist/shape.js +192 -0
  104. package/dist/shape.js.map +1 -0
  105. package/dist/snap.d.ts +88 -0
  106. package/dist/snap.d.ts.map +1 -0
  107. package/dist/snap.js +128 -0
  108. package/dist/snap.js.map +1 -0
  109. package/dist/spatial.d.ts +38 -0
  110. package/dist/spatial.d.ts.map +1 -0
  111. package/dist/spatial.js +95 -0
  112. package/dist/spatial.js.map +1 -0
  113. package/dist/style.d.ts +87 -0
  114. package/dist/style.d.ts.map +1 -0
  115. package/dist/style.js +43 -0
  116. package/dist/style.js.map +1 -0
  117. package/dist/text-measure.d.ts +29 -0
  118. package/dist/text-measure.d.ts.map +1 -0
  119. package/dist/text-measure.js +8 -0
  120. package/dist/text-measure.js.map +1 -0
  121. package/dist/three-way-merge.d.ts +59 -0
  122. package/dist/three-way-merge.d.ts.map +1 -0
  123. package/dist/three-way-merge.js +108 -0
  124. package/dist/three-way-merge.js.map +1 -0
  125. package/dist/viewport.d.ts +78 -0
  126. package/dist/viewport.d.ts.map +1 -0
  127. package/dist/viewport.js +62 -0
  128. package/dist/viewport.js.map +1 -0
  129. package/package.json +54 -0
@@ -0,0 +1,322 @@
1
+ import { isContainer } from "./container.js";
2
+ import { getElementLocalBounds, isPolygon } from "./shape.js";
3
+ const EMPTY_ANCHOR_EXCLUDE = new Set();
4
+ /**
5
+ * The 9 canonical named anchors every built-in shape exposes. They map to
6
+ * the local bounding box of a shape — four corners, four edge centres, and
7
+ * the geometric centre.
8
+ *
9
+ * top-left ─── top ─── top-right
10
+ * │ │ │
11
+ * left ─── center ─── right
12
+ * │ │ │
13
+ * bottom-left ─ bottom ─ bottom-right
14
+ *
15
+ * `top` / `right` / `bottom` / `left` are the edge centres; `top-left` etc.
16
+ * are the corners. Hosts may add custom anchors per shape via the
17
+ * `anchors` field on `ElementBase`.
18
+ */
19
+ export const STANDARD_ANCHOR_RATIOS = {
20
+ "top-left": { x: 0, y: 0 },
21
+ top: { x: 0.5, y: 0 },
22
+ "top-right": { x: 1, y: 0 },
23
+ right: { x: 1, y: 0.5 },
24
+ "bottom-right": { x: 1, y: 1 },
25
+ bottom: { x: 0.5, y: 1 },
26
+ "bottom-left": { x: 0, y: 1 },
27
+ left: { x: 0, y: 0.5 },
28
+ center: { x: 0.5, y: 0.5 },
29
+ };
30
+ /** All 9 standard anchor names, in clockwise-from-top-left order. */
31
+ export const STANDARD_ANCHORS = [
32
+ "top-left",
33
+ "top",
34
+ "top-right",
35
+ "right",
36
+ "bottom-right",
37
+ "bottom",
38
+ "bottom-left",
39
+ "left",
40
+ "center",
41
+ ];
42
+ /**
43
+ * Resolve a named anchor to its *local-space* position inside the shape's
44
+ * bounding box. Returns `undefined` if the name isn't a standard anchor
45
+ * and isn't declared in the shape's own `anchors` map.
46
+ *
47
+ * Hosts looking up an `AnchorRef` should call `getAnchorLocal` /
48
+ * `getAnchorWorld` instead — those handle every `AnchorRef` kind.
49
+ */
50
+ export const getNamedAnchorLocal = (shape, name) => {
51
+ return lookupNamed(shape, name);
52
+ };
53
+ /**
54
+ * Resolve any `AnchorRef` to a point in the shape's local coordinate
55
+ * space (before `position` / `rotation` / `scale`). Throws on an unknown
56
+ * named anchor — callers either pass a `ratio` / `absolute` ref or use a
57
+ * registered standard / custom name.
58
+ */
59
+ export const getAnchorLocal = (shape, anchor) => {
60
+ return resolveAnchorRefLocal(shape, anchor);
61
+ };
62
+ /**
63
+ * Resolve any `AnchorRef` to a world-space point. Applies the shape's
64
+ * `position` + `rotation` + `scale` to the local resolution from
65
+ * `getAnchorLocal`.
66
+ */
67
+ export const getAnchorWorld = (shape, anchor) => {
68
+ const local = getAnchorLocal(shape, anchor);
69
+ const sx = local.x * shape.scale.x;
70
+ const sy = local.y * shape.scale.y;
71
+ const cos = Math.cos(shape.rotation);
72
+ const sin = Math.sin(shape.rotation);
73
+ return {
74
+ x: shape.position.x + (sx * cos - sy * sin),
75
+ y: shape.position.y + (sx * sin + sy * cos),
76
+ };
77
+ };
78
+ /**
79
+ * World-space outward unit normal at an anchor — the direction "away from
80
+ * the shape" used to push port dots a few pixels off the edge and to seed
81
+ * a link's exit stub.
82
+ *
83
+ * Computed as the direction from the shape's geometric centre to the
84
+ * anchor's world point, so it automatically respects rotation / non-
85
+ * uniform scale and works for cardinal ports, polygon edge midpoints, and
86
+ * corners alike. Returns `{ x: 0, y: -1 }` (up) as a stable fallback when
87
+ * the anchor coincides with the centre (e.g. a custom centre anchor),
88
+ * where "outward" is undefined.
89
+ */
90
+ export const getAnchorOutwardNormal = (shape, anchor) => {
91
+ const point = getAnchorWorld(shape, anchor);
92
+ const centre = getAnchorWorld(shape, { kind: "named", name: "center" });
93
+ const dx = point.x - centre.x;
94
+ const dy = point.y - centre.y;
95
+ const len = Math.hypot(dx, dy);
96
+ if (len < 1e-6)
97
+ return { x: 0, y: -1 };
98
+ return { x: dx / len, y: dy / len };
99
+ };
100
+ /**
101
+ * Find the anchor on `shape` whose world position is closest to
102
+ * `worldPoint`. Returns the canonical `AnchorRef` you can persist on an
103
+ * `LinkEndpoint` so future renders stay locked to that port.
104
+ *
105
+ * Useful when committing an edge whose endpoint dropped near a shape:
106
+ * snap the drop position to the nearest of the 9 standard anchors (plus
107
+ * custom ones) so the edge tracks the shape on subsequent edits.
108
+ */
109
+ export const findNearestAnchor = (shape, worldPoint, excludeNames = EMPTY_ANCHOR_EXCLUDE) => {
110
+ let best = null;
111
+ for (const [name, _local] of geometryDefaultAnchorsLocal(shape)) {
112
+ void _local;
113
+ if (excludeNames.has(name))
114
+ continue;
115
+ const ref = defaultAnchorRef(name);
116
+ const world = getAnchorWorld(shape, ref);
117
+ const dx = world.x - worldPoint.x;
118
+ const dy = world.y - worldPoint.y;
119
+ const distance = Math.hypot(dx, dy);
120
+ if (best === null || distance < best.distance) {
121
+ best = { ref, world, distance };
122
+ }
123
+ }
124
+ // `geometryDefaultAnchorsLocal` normally yields the 4 cardinal ports
125
+ // (or a polygon's edge midpoints), so `best` is set in practice. The
126
+ // only empty case is a degenerate polygon (<2 points) with no custom
127
+ // anchors — fall back to the geometric centre so the caller always
128
+ // gets a usable ref.
129
+ if (!best) {
130
+ return {
131
+ ref: { kind: "named", name: "center" },
132
+ world: getAnchorWorld(shape, { kind: "named", name: "center" }),
133
+ distance: Infinity,
134
+ };
135
+ }
136
+ return best;
137
+ };
138
+ /**
139
+ * Map a default-anchor key (from `geometryDefaultAnchorsLocal`) to the
140
+ * canonical `AnchorRef` to persist on a link endpoint. Polygon edge
141
+ * midpoints (`edge-<n>`) become an `edge` ref pinned to the edge midpoint
142
+ * (`t = 0.5`) so the endpoint tracks the real sloped edge; everything
143
+ * else (cardinal names, custom names) is a `named` ref.
144
+ */
145
+ const defaultAnchorRef = (key) => {
146
+ const m = /^edge-(\d+)$/.exec(key);
147
+ if (m)
148
+ return { kind: "edge", index: Number(m[1]), t: 0.5 };
149
+ return { kind: "named", name: key };
150
+ };
151
+ /**
152
+ * Materialise the full anchor table for a shape — every standard anchor
153
+ * plus every custom one declared in `shape.anchors`. Custom entries with
154
+ * the same name as a standard anchor override the standard placement.
155
+ * Returns *local-space* coordinates.
156
+ *
157
+ * Renderers that draw port-dots use this to enumerate the dots once per
158
+ * frame instead of resolving each name individually.
159
+ */
160
+ /**
161
+ * Anchor names that should not be offered as snap targets for the
162
+ * given shape, used by the edge-draw snap engine and the hover port-
163
+ * dot overlay. Containers omit `center` because that point lives
164
+ * inside the drop-zone, where a snap-to-anchor would compete with
165
+ * the reparent / drop gesture. Hosts can still target the centre
166
+ * explicitly via `kind: "anchor", anchor: { kind: "named", name:
167
+ * "center" }` — only the auto snap is filtered.
168
+ */
169
+ export const snapExcludedAnchors = (shape) => {
170
+ if (isContainer(shape))
171
+ return CONTAINER_SNAP_EXCLUDED;
172
+ return EMPTY_ANCHOR_EXCLUDE;
173
+ };
174
+ const CONTAINER_SNAP_EXCLUDED = new Set(["center"]);
175
+ export const listAnchorsLocal = (shape) => {
176
+ const out = new Map();
177
+ const b = getElementLocalBounds(shape);
178
+ for (const name of STANDARD_ANCHORS) {
179
+ const ratio = STANDARD_ANCHOR_RATIOS[name];
180
+ out.set(name, { x: b.x + b.width * ratio.x, y: b.y + b.height * ratio.y });
181
+ }
182
+ if (shape.anchors) {
183
+ for (const [name, ref] of Object.entries(shape.anchors)) {
184
+ out.set(name, resolveAnchorRefLocal(shape, ref));
185
+ }
186
+ }
187
+ return out;
188
+ };
189
+ /**
190
+ * Geometry-aware default connection points for a shape, in *local* space.
191
+ *
192
+ * Unlike `listAnchorsLocal` (which always returns the 9 AABB-relative
193
+ * standard anchors), this returns the points the editor offers as the
194
+ * default snap/draw targets, derived from the shape's *real* geometry:
195
+ *
196
+ * - **polygon** — the midpoint of every edge (works for triangle,
197
+ * hexagon, …; points sit on the actual sloped edges, not the bbox).
198
+ * - **rectangle / ellipse / everything else** — the four bbox edge
199
+ * centres (`top` / `right` / `bottom` / `left`). For an axis-aligned
200
+ * rectangle these are the real edge midpoints; for an ellipse the
201
+ * cardinal points already lie on the curve.
202
+ *
203
+ * Corners and the centre are excluded — the centre is never a magnet
204
+ * target, and corner ports are visual noise for the default set
205
+ * (templates can still declare custom anchors there via `shape.anchors`).
206
+ *
207
+ * A shape's own `shape.anchors` entries are merged on top (and override a
208
+ * default with the same name), so a template can fully customise the port
209
+ * layout while non-templated shapes get sensible geometry-aware defaults.
210
+ *
211
+ * Returns a name→local-point map. Default names are the polygon edge
212
+ * indices (`edge-0`, `edge-1`, …) or the cardinal names (`top` / `right` /
213
+ * `bottom` / `left`).
214
+ */
215
+ export const geometryDefaultAnchorsLocal = (shape) => {
216
+ const out = new Map();
217
+ if (isPolygon(shape)) {
218
+ addPolygonEdgeMidpoints(shape, out);
219
+ }
220
+ else {
221
+ const b = getElementLocalBounds(shape);
222
+ for (const name of CARDINAL_ANCHORS) {
223
+ const ratio = STANDARD_ANCHOR_RATIOS[name];
224
+ out.set(name, { x: b.x + b.width * ratio.x, y: b.y + b.height * ratio.y });
225
+ }
226
+ }
227
+ if (shape.anchors) {
228
+ for (const [name, ref] of Object.entries(shape.anchors)) {
229
+ out.set(name, resolveAnchorRefLocal(shape, ref));
230
+ }
231
+ }
232
+ return out;
233
+ };
234
+ /** The four bbox edge centres — the default port set for non-polygon shapes. */
235
+ export const CARDINAL_ANCHORS = ["top", "right", "bottom", "left"];
236
+ // --- internal ---
237
+ /**
238
+ * Insert the midpoint of every polygon edge into `out`, keyed `edge-0`,
239
+ * `edge-1`, … in vertex order. A polygon with N vertices has N edges
240
+ * (the last wraps back to the first). Degenerate polygons (<2 points)
241
+ * contribute nothing.
242
+ */
243
+ const addPolygonEdgeMidpoints = (shape, out) => {
244
+ const pts = shape.points;
245
+ if (pts.length < 2)
246
+ return;
247
+ for (let i = 0; i < pts.length; i++) {
248
+ const a = pts[i];
249
+ const b = pts[(i + 1) % pts.length];
250
+ if (a === undefined || b === undefined)
251
+ continue;
252
+ out.set(`edge-${i}`, { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });
253
+ }
254
+ };
255
+ const resolveAnchorRefLocal = (shape, anchor) => {
256
+ switch (anchor.kind) {
257
+ case "named": {
258
+ const point = lookupNamed(shape, anchor.name);
259
+ if (!point) {
260
+ throw new Error(`Unknown anchor "${anchor.name}" on shape ${shape.id} (type ${shape.type}). ` +
261
+ `Standard names: ${STANDARD_ANCHORS.join(", ")}; custom anchors live on shape.anchors.`);
262
+ }
263
+ return point;
264
+ }
265
+ case "ratio": {
266
+ const b = getElementLocalBounds(shape);
267
+ return { x: b.x + b.width * anchor.position.x, y: b.y + b.height * anchor.position.y };
268
+ }
269
+ case "absolute": {
270
+ const b = getElementLocalBounds(shape);
271
+ return { x: b.x + anchor.offset.x, y: b.y + anchor.offset.y };
272
+ }
273
+ case "edge": {
274
+ return resolveEdgeRefLocal(shape, anchor.index, anchor.t);
275
+ }
276
+ }
277
+ };
278
+ /**
279
+ * Resolve an `edge` AnchorRef to local space: the point at parameter `t`
280
+ * along polygon edge `index` (wrapping). For a non-polygon shape (or a
281
+ * degenerate polygon) there are no real edges, so fall back to the
282
+ * geometric centre — keeps the resolver total without throwing.
283
+ */
284
+ const resolveEdgeRefLocal = (shape, index, t) => {
285
+ if (isPolygon(shape) && shape.points.length >= 2) {
286
+ const pts = shape.points;
287
+ const n = pts.length;
288
+ // Wrap a possibly out-of-range / negative index into [0, n).
289
+ const i = ((index % n) + n) % n;
290
+ const a = pts[i];
291
+ const b = pts[(i + 1) % n];
292
+ if (a !== undefined && b !== undefined) {
293
+ const tc = t < 0 ? 0 : t > 1 ? 1 : t;
294
+ return { x: a.x + (b.x - a.x) * tc, y: a.y + (b.y - a.y) * tc };
295
+ }
296
+ }
297
+ const bounds = getElementLocalBounds(shape);
298
+ return { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 };
299
+ };
300
+ const lookupNamed = (shape, name) => {
301
+ const custom = shape.anchors?.[name];
302
+ if (custom !== undefined) {
303
+ // Custom entry — resolve recursively (custom may itself be a ratio /
304
+ // absolute ref). Recursion is bounded: anchors don't reference each
305
+ // other by name (`kind: "named"` chains are not followed here).
306
+ if (custom.kind === "named")
307
+ return lookupNamed(shape, custom.name);
308
+ return resolveAnchorRefLocal(shape, custom);
309
+ }
310
+ if (isPolygon(shape) && name.startsWith("edge-")) {
311
+ const index = parseInt(name.slice(5), 10);
312
+ if (!isNaN(index))
313
+ return resolveEdgeRefLocal(shape, index, 0.5);
314
+ }
315
+ const standard = STANDARD_ANCHOR_RATIOS[name];
316
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `name` is cast from a broader type; a non-standard name yields undefined at runtime.
317
+ if (!standard)
318
+ return undefined;
319
+ const b = getElementLocalBounds(shape);
320
+ return { x: b.x + b.width * standard.x, y: b.y + b.height * standard.y };
321
+ };
322
+ //# sourceMappingURL=anchors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchors.js","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Vec2 } from "@oh-just-another/types";
2
+ import type { Annotation } from "./annotation.js";
3
+ import type { Scene } from "./scene.js";
4
+ /**
5
+ * World-space position where the pin should render. When the annotation
6
+ * is anchored to a shape, the stored `position` is added to the shape's
7
+ * world position (so the pin follows shape moves). When the shape is
8
+ * gone — or the annotation is a free-floater — `position` is used as-is.
9
+ */
10
+ export declare const getAnnotationWorldPosition: (scene: Scene, annotation: Annotation) => Vec2;
11
+ //# sourceMappingURL=annotation-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotation-geometry.d.ts","sourceRoot":"","sources":["../src/annotation-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,KAAK,EAAE,YAAY,UAAU,KAAG,IAQjF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { getElement } from "./queries.js";
2
+ /**
3
+ * World-space position where the pin should render. When the annotation
4
+ * is anchored to a shape, the stored `position` is added to the shape's
5
+ * world position (so the pin follows shape moves). When the shape is
6
+ * gone — or the annotation is a free-floater — `position` is used as-is.
7
+ */
8
+ export const getAnnotationWorldPosition = (scene, annotation) => {
9
+ if (annotation.elementId === null)
10
+ return annotation.position;
11
+ const shape = getElement(scene, annotation.elementId);
12
+ if (!shape)
13
+ return annotation.position;
14
+ return {
15
+ x: shape.position.x + annotation.position.x,
16
+ y: shape.position.y + annotation.position.y,
17
+ };
18
+ };
19
+ //# sourceMappingURL=annotation-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotation-geometry.js","sourceRoot":"","sources":["../src/annotation-geometry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,KAAY,EAAE,UAAsB,EAAQ,EAAE;IACvF,IAAI,UAAU,CAAC,SAAS,KAAK,IAAI;QAAE,OAAO,UAAU,CAAC,QAAQ,CAAC;IAC9D,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC,QAAQ,CAAC;IACvC,OAAO;QACL,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;KAC5C,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { AnnotationId, CommentId, ElementId, Vec2 } from "@oh-just-another/types";
2
+ /**
3
+ * Threaded comment inside an annotation. `body` is plain text — host can
4
+ * render mentions / markdown / etc. on top. `createdAt` is an ISO-8601
5
+ * string for serialization stability across timezones.
6
+ */
7
+ export interface Comment {
8
+ readonly id: CommentId;
9
+ readonly authorId: string;
10
+ readonly authorName: string;
11
+ readonly body: string;
12
+ readonly createdAt: string;
13
+ }
14
+ /**
15
+ * Pin + comment thread anchored either to a free world-space position
16
+ * or to a specific shape. When `elementId` is set, the position is
17
+ * resolved as `shape.position + offset`; if the shape moves, the pin
18
+ * follows it. `position` is the world-space (or local-to-shape when
19
+ * `elementId` is set) offset.
20
+ *
21
+ * `resolved` is a UI-only flag: marks the thread as closed. Doesn't
22
+ * delete history.
23
+ */
24
+ export interface Annotation {
25
+ readonly id: AnnotationId;
26
+ readonly elementId: ElementId | null;
27
+ readonly position: Vec2;
28
+ readonly resolved: boolean;
29
+ readonly thread: readonly Comment[];
30
+ readonly createdAt: string;
31
+ }
32
+ //# sourceMappingURL=annotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotation.d.ts","sourceRoot":"","sources":["../src/annotation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEvF;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=annotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotation.js","sourceRoot":"","sources":["../src/annotation.ts"],"names":[],"mappings":""}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Tunable thresholds for the scene-level helpers (snap engine, hit-test
3
+ * cheap-cull). Keep magic numbers here so hosts can re-tune the engine
4
+ * without touching the algorithm code.
5
+ */
6
+ /**
7
+ * Half-side of the bounding box used by `isProbeNearElement` to cheap-cull
8
+ * snap candidates. Shapes farther than this from the probe (plus the
9
+ * snap threshold cushion) are skipped without the full anchor walk.
10
+ *
11
+ * The default of 1000 world units covers any typical editor shape; bump
12
+ * it if hosts work with very large diagrams where the cheap-cull starts
13
+ * to over-prune real candidates.
14
+ */
15
+ export declare const SNAP_PROBE_CULL_RADIUS = 1000;
16
+ /**
17
+ * Fallback grid spacing (world units) used by snap-to-grid when the
18
+ * viewport has no `gridSize` (grid hidden) but snapping is still on.
19
+ * Lets the user keep clean grid-aligned geometry without a visible
20
+ * grid. Matches the app's default `gridSize` of 20. Range: 4–64.
21
+ */
22
+ export declare const DEFAULT_GRID_SPACING = 20;
23
+ /**
24
+ * Padding (world units) the elbow router inflates obstacle bboxes
25
+ * by before searching. Larger values keep edges visibly clear of
26
+ * shapes; smaller values let the router squeeze through tight
27
+ * spaces. 20 px matches the gridSize default for diagrams that
28
+ * snap to a 20-unit grid.
29
+ */
30
+ export declare const ELBOW_OBSTACLE_MARGIN = 20;
31
+ /**
32
+ * Epsilon used to decide whether an axis-aligned segment runs
33
+ * *along* an obstacle boundary (allowed) or *through* it (blocked).
34
+ * A degenerate small value catches floating-point fuzz from
35
+ * `inflate` arithmetic without admitting real crossings.
36
+ */
37
+ export declare const ELBOW_OBSTACLE_INTERIOR_EPSILON = 0.5;
38
+ /**
39
+ * Per-turn cost added in the elbow A* so the router minimises BENDS first,
40
+ * distance second (lexicographic — far larger than any plausible canvas
41
+ * distance). Keeps routes stable: small shape moves no longer flip between
42
+ * equal-distance alternatives, and the path takes the fewest corners.
43
+ */
44
+ export declare const ELBOW_BEND_PENALTY = 100000;
45
+ /**
46
+ * Hysteresis band (world px) for the C-wrap side choice in `wrapRoute`.
47
+ * When the connector must wrap around the union of its two bound shapes,
48
+ * the over/under (or left/right) side is picked from the endpoints'
49
+ * midpoint vs the union centre. Within this band of the centre the
50
+ * previous side is kept (read from `edge.routedPoints`), so a small
51
+ * back-and-forth drag doesn't thrash; the side only switches once the
52
+ * midpoint moves this far past the centre. Larger = stickier.
53
+ * Range: 8–48.
54
+ */
55
+ export declare const ELBOW_WRAP_HYSTERESIS = 24;
56
+ /**
57
+ * Perf cap for "avoid obstacles" routing. The A* grid scales with the
58
+ * number of obstacle corners, and the route is recomputed every frame
59
+ * while a shape is dragged, so above this many scene shapes we skip
60
+ * whole-scene avoidance and fall back to the cheap two-box elbow (the
61
+ * link still keeps clear of its own ends). Range: 60–300.
62
+ */
63
+ export declare const ELBOW_AVOID_MAX_OBSTACLES = 150;
64
+ /**
65
+ * Length (world px) of the fixed, non-movable terminal segment an elbow
66
+ * connector always leaves at each end before its first bend — the endpoint
67
+ * is pushed out this far along its exit heading so the connector departs/
68
+ * arrives perpendicular to the edge and there's buffer room to draw the
69
+ * arrowhead. Must stay ≥ ELBOW_OBSTACLE_MARGIN so the pushed-out point
70
+ * sits outside the inflated obstacle the A* router avoids. Larger → more
71
+ * breathing room before the first bend. Range: 16–40.
72
+ */
73
+ export declare const ELBOW_TERMINAL_BUFFER = 30;
74
+ /**
75
+ * --- Self-loop connectors (a link whose both ends bind to the SAME element) ---
76
+ *
77
+ * A self-loop is routed OUTSIDE the element so it reads as a loop/arc instead of
78
+ * a flat line on (or across) the shape.
79
+ *
80
+ * - `SELF_LOOP_SIZE` — how far (world px) the loop bows out past the element's
81
+ * edge. Fixed for every element. Range: 24–80.
82
+ * - `SELF_LOOP_SPREAD` — when both ends resolve to the SAME point (centre /
83
+ * floating / same anchor) the two exit points are spread this far apart along
84
+ * the edge so the loop has width. Clamped to a third of the side. Range: 12–40.
85
+ * - `SELF_LOOP_CURVE_ARM_FACTOR` — control-arm length for a curved self-loop as a
86
+ * multiple of `SELF_LOOP_SIZE`; larger = rounder, more pronounced arc. 2.2
87
+ * gives a clean teardrop. Range: 1.5–3.
88
+ */
89
+ export declare const SELF_LOOP_SIZE = 40;
90
+ export declare const SELF_LOOP_SPREAD = 24;
91
+ export declare const SELF_LOOP_CURVE_ARM_FACTOR = 2.2;
92
+ /**
93
+ * Clearance (world px) a candidate centred path (the "thread"/mid-S or the
94
+ * C-wrap) must keep from a bound shape's interior before it counts as CROSSING
95
+ * it. This is the threshold that decides thread-vs-wrap-vs-A*: a larger value
96
+ * makes the router bail off the direct/centred path sooner (route stays further
97
+ * from shapes), a smaller value lets it skim closer to an edge before detouring.
98
+ * At 1 px it only rejects genuine interior crossings, allowing edge-grazing.
99
+ * Range: 1–8.
100
+ */
101
+ export declare const ELBOW_OBSTACLE_CLEARANCE = 1;
102
+ /**
103
+ * Parametric step used to sample each segment of a candidate path when testing
104
+ * whether it crosses a shape (`pathCrossesObstacle`). Smaller = finer (catches
105
+ * a narrow shape a coarse sampling would skip) at more cost; 0.1 samples 11
106
+ * points per segment, enough for typical shape sizes. Range: 0.02–0.2.
107
+ */
108
+ export declare const ELBOW_CROSS_SAMPLE_STEP = 0.1;
109
+ /**
110
+ * --- Curved (bezier) link geometry ---
111
+ *
112
+ * Shared by the renderer (draws cubic beziers), hit-testing and bounds
113
+ * (flatten the same curve) so the visible curve and the clickable curve
114
+ * agree. Lives in scene so lower layers own the geometry; renderer-core
115
+ * imports it.
116
+ *
117
+ * - `CURVE_CATMULL_TENSION` — divisor for the Catmull-Rom tangents in the
118
+ * spline→bezier conversion (waypointed curves). 6 is canonical uniform
119
+ * Catmull-Rom (control point = P + (Pnext − Pprev) / 6). Larger → tighter;
120
+ * smaller → looser. Range: 4–8.
121
+ * - `CURVE_END_TANGENT_RATIO` — for a no-waypoint span the cubic's control
122
+ * arms leave/enter the endpoints along their edge normals with length =
123
+ * this fraction of the endpoint distance, so the connector exits/enters
124
+ * perpendicular to the element edge (flowchart look). Larger → rounder /
125
+ * more pronounced. Range: 0.25–0.6.
126
+ * - `CURVE_END_TANGENT_MAX_PX` — caps that control-arm length (world px) so a
127
+ * long link doesn't over-bow. Range: 60–160.
128
+ * - `CURVE_FLATTEN_SEGMENTS` — samples per cubic when flattening the curve
129
+ * for hit-testing / bounds. Higher = closer to the drawn curve. Range:
130
+ * 8–24.
131
+ */
132
+ export declare const CURVE_CATMULL_TENSION = 6;
133
+ export declare const CURVE_END_TANGENT_RATIO = 0.8;
134
+ export declare const CURVE_END_TANGENT_MAX_PX = 240;
135
+ export declare const CURVE_FLATTEN_SEGMENTS = 16;
136
+ /**
137
+ * --- Roundness (Style.roundness) ---
138
+ *
139
+ * Adaptive radius: pick a fixed radius for shapes bigger than the cutoff,
140
+ * scale proportionally for smaller ones so the corner doesn't dominate.
141
+ * 32 px / 0.25 looks rounded without becoming a capsule across the
142
+ * realistic shape-size range.
143
+ */
144
+ /** Fixed pixel radius used by adaptive rounding for shapes ≥ cutoff. */
145
+ export declare const ADAPTIVE_CORNER_RADIUS = 32;
146
+ /**
147
+ * Proportional radius (0..1 of the smaller side) used by adaptive
148
+ * rounding for shapes below the cutoff, and the fall-through when
149
+ * `Roundness.value` is omitted but the type is `round`.
150
+ */
151
+ export declare const PROPORTIONAL_CORNER_RADIUS = 0.25;
152
+ /**
153
+ * --- Text bounds estimation ---
154
+ *
155
+ * The text bounder has no layout engine, so it approximates the box.
156
+ * Renderers compute the precise layout (via `measureText`) during
157
+ * draw / caret positioning; these factors only drive selection bbox
158
+ * and resize-handle placement, where a rough estimate is fine.
159
+ *
160
+ * - `TEXT_APPROX_CHAR_WIDTH_FACTOR` — average glyph advance as a
161
+ * fraction of font size (~0.6 for proportional Latin text).
162
+ * - `TEXT_LINE_HEIGHT_FACTOR` — line height as a multiple of font
163
+ * size. Must match the renderer's `DEFAULT_LINE_HEIGHT_FACTOR`.
164
+ */
165
+ export declare const TEXT_APPROX_CHAR_WIDTH_FACTOR = 0.6;
166
+ export declare const TEXT_LINE_HEIGHT_FACTOR = 1.2;
167
+ /**
168
+ * --- Frame header (label strip) geometry ---
169
+ *
170
+ * The frame's name is drawn in a strip ABOVE the frame body (local y in
171
+ * `[-FRAME_HEADER_HEIGHT, 0]`). Shared by the renderer (draws it), the
172
+ * editor (header double-click → rename hit zone + render overflow) and
173
+ * react-ui (positions the inline name editor) so all three agree.
174
+ *
175
+ * - `FRAME_HEADER_HEIGHT` — strip height (world px).
176
+ * - `FRAME_HEADER_PADDING_X` — horizontal text inset, each side (world px).
177
+ * - `FRAME_HEADER_FONT_SIZE` — label font size (world px).
178
+ *
179
+ * The strip width is dynamic: it hugs the label width but is capped at the
180
+ * frame's own width (a too-long name is ellipsised) — computed in the
181
+ * renderer, which can measure text.
182
+ */
183
+ export declare const FRAME_HEADER_HEIGHT = 24;
184
+ export declare const FRAME_HEADER_PADDING_X = 8;
185
+ export declare const FRAME_HEADER_FONT_SIZE = 12;
186
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;;;GAOG;AAEH,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,sBAAsB,KAAK,CAAC"}