@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.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/a11y.d.ts +23 -0
- package/dist/a11y.d.ts.map +1 -0
- package/dist/a11y.js +37 -0
- package/dist/a11y.js.map +1 -0
- package/dist/anchors.d.ts +120 -0
- package/dist/anchors.d.ts.map +1 -0
- package/dist/anchors.js +322 -0
- package/dist/anchors.js.map +1 -0
- package/dist/annotation-geometry.d.ts +11 -0
- package/dist/annotation-geometry.d.ts.map +1 -0
- package/dist/annotation-geometry.js +19 -0
- package/dist/annotation-geometry.js.map +1 -0
- package/dist/annotation.d.ts +32 -0
- package/dist/annotation.d.ts.map +1 -0
- package/dist/annotation.js +2 -0
- package/dist/annotation.js.map +1 -0
- package/dist/constants.d.ts +186 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +186 -0
- package/dist/constants.js.map +1 -0
- package/dist/container.d.ts +112 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +227 -0
- package/dist/container.js.map +1 -0
- package/dist/diff.d.ts +22 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +18 -0
- package/dist/diff.js.map +1 -0
- package/dist/edge-curve.d.ts +37 -0
- package/dist/edge-curve.d.ts.map +1 -0
- package/dist/edge-curve.js +72 -0
- package/dist/edge-curve.js.map +1 -0
- package/dist/edge-geometry.d.ts +96 -0
- package/dist/edge-geometry.d.ts.map +1 -0
- package/dist/edge-geometry.js +467 -0
- package/dist/edge-geometry.js.map +1 -0
- package/dist/edge.d.ts +206 -0
- package/dist/edge.d.ts.map +1 -0
- package/dist/edge.js +2 -0
- package/dist/edge.js.map +1 -0
- package/dist/elbow-link.d.ts +23 -0
- package/dist/elbow-link.d.ts.map +1 -0
- package/dist/elbow-link.js +419 -0
- package/dist/elbow-link.js.map +1 -0
- package/dist/elbow-router.d.ts +20 -0
- package/dist/elbow-router.d.ts.map +1 -0
- package/dist/elbow-router.js +218 -0
- package/dist/elbow-router.js.map +1 -0
- package/dist/file.d.ts +41 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/file.js +13 -0
- package/dist/file.js.map +1 -0
- package/dist/heading.d.ts +42 -0
- package/dist/heading.d.ts.map +1 -0
- package/dist/heading.js +77 -0
- package/dist/heading.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/layer.d.ts +18 -0
- package/dist/layer.d.ts.map +1 -0
- package/dist/layer.js +2 -0
- package/dist/layer.js.map +1 -0
- package/dist/layout-registry.d.ts +35 -0
- package/dist/layout-registry.d.ts.map +1 -0
- package/dist/layout-registry.js +13 -0
- package/dist/layout-registry.js.map +1 -0
- package/dist/layout.d.ts +128 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +453 -0
- package/dist/layout.js.map +1 -0
- package/dist/operations.d.ts +32 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +105 -0
- package/dist/operations.js.map +1 -0
- package/dist/outline.d.ts +36 -0
- package/dist/outline.d.ts.map +1 -0
- package/dist/outline.js +129 -0
- package/dist/outline.js.map +1 -0
- package/dist/patch.d.ts +57 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +47 -0
- package/dist/patch.js.map +1 -0
- package/dist/queries.d.ts +115 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +403 -0
- package/dist/queries.js.map +1 -0
- package/dist/render-bounds.d.ts +36 -0
- package/dist/render-bounds.d.ts.map +1 -0
- package/dist/render-bounds.js +36 -0
- package/dist/render-bounds.js.map +1 -0
- package/dist/scene.d.ts +80 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +129 -0
- package/dist/scene.js.map +1 -0
- package/dist/shape.d.ts +306 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +192 -0
- package/dist/shape.js.map +1 -0
- package/dist/snap.d.ts +88 -0
- package/dist/snap.d.ts.map +1 -0
- package/dist/snap.js +128 -0
- package/dist/snap.js.map +1 -0
- package/dist/spatial.d.ts +38 -0
- package/dist/spatial.d.ts.map +1 -0
- package/dist/spatial.js +95 -0
- package/dist/spatial.js.map +1 -0
- package/dist/style.d.ts +87 -0
- package/dist/style.d.ts.map +1 -0
- package/dist/style.js +43 -0
- package/dist/style.js.map +1 -0
- package/dist/text-measure.d.ts +29 -0
- package/dist/text-measure.d.ts.map +1 -0
- package/dist/text-measure.js +8 -0
- package/dist/text-measure.js.map +1 -0
- package/dist/three-way-merge.d.ts +59 -0
- package/dist/three-way-merge.d.ts.map +1 -0
- package/dist/three-way-merge.js +108 -0
- package/dist/three-way-merge.js.map +1 -0
- package/dist/viewport.d.ts +78 -0
- package/dist/viewport.d.ts.map +1 -0
- package/dist/viewport.js +62 -0
- package/dist/viewport.js.map +1 -0
- package/package.json +54 -0
package/dist/anchors.js
ADDED
|
@@ -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 @@
|
|
|
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"}
|