@kolosal-ai/rivet 0.5.0 → 0.7.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/dist/anchor/index.d.ts +3 -3
- package/dist/anchor/index.js +1 -36
- package/dist/anchor/index.js.map +1 -1
- package/dist/chunk-B3YKB2QN.js +2 -0
- package/dist/chunk-B3YKB2QN.js.map +1 -0
- package/dist/chunk-CVSEMPYO.js +2 -0
- package/dist/chunk-CVSEMPYO.js.map +1 -0
- package/dist/chunk-NLE6BOPW.js +2 -0
- package/dist/chunk-NLE6BOPW.js.map +1 -0
- package/dist/chunk-RDSWIH3F.js +2 -0
- package/dist/chunk-RDSWIH3F.js.map +1 -0
- package/dist/chunk-X4VW3W4W.js +2 -0
- package/dist/chunk-X4VW3W4W.js.map +1 -0
- package/dist/chunk-XTQGGEUS.js +2 -0
- package/dist/chunk-XTQGGEUS.js.map +1 -0
- package/dist/index.d.ts +127 -40
- package/dist/index.js +1 -6176
- package/dist/index.js.map +1 -1
- package/dist/presence/index.d.ts +3 -3
- package/dist/presence/index.js +1 -37
- package/dist/presence/index.js.map +1 -1
- package/dist/{registry-ClCPnBac.d.ts → registry-Vc6r10MJ.d.ts} +24 -7
- package/dist/svg/index.d.ts +2 -2
- package/dist/svg/index.js +2 -128
- package/dist/svg/index.js.map +1 -1
- package/dist/swimlane/index.d.ts +4 -4
- package/dist/swimlane/index.js +1 -2
- package/dist/{types-D3aldmsO.d.ts → types-BPtqcAVr.d.ts} +52 -1
- package/dist/{use-node-lock-CbS59CRE.d.ts → use-node-lock-BFQQAr8M.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-6XRQSAQT.js +0 -148
- package/dist/chunk-6XRQSAQT.js.map +0 -1
- package/dist/chunk-QHMB4WYT.js +0 -148
- package/dist/chunk-QHMB4WYT.js.map +0 -1
- package/dist/chunk-TT2N7KWU.js +0 -676
- package/dist/chunk-TT2N7KWU.js.map +0 -1
- package/dist/chunk-VQYN27OK.js +0 -36
- package/dist/chunk-VQYN27OK.js.map +0 -1
package/dist/chunk-6XRQSAQT.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// ../utils/src/math.ts
|
|
2
|
-
function clamp(value, min, max) {
|
|
3
|
-
return Math.min(Math.max(value, min), max);
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
// src/swimlane/lanes.ts
|
|
7
|
-
var SWIMLANE_GROUP_HEADER_SIZE = 32;
|
|
8
|
-
var SWIMLANE_LABEL_WIDTH = 24;
|
|
9
|
-
var SWIMLANE_MIN_HEIGHT = 80;
|
|
10
|
-
function resolveMargin(margin) {
|
|
11
|
-
if (typeof margin === "number") {
|
|
12
|
-
return { top: margin, right: margin, bottom: margin, left: margin };
|
|
13
|
-
}
|
|
14
|
-
return {
|
|
15
|
-
top: margin?.top ?? 16,
|
|
16
|
-
right: margin?.right ?? 16,
|
|
17
|
-
bottom: margin?.bottom ?? 16,
|
|
18
|
-
left: margin?.left ?? 16
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
var ORIGIN = { x: 0, y: 0 };
|
|
22
|
-
function withAlpha(color, alpha) {
|
|
23
|
-
const hex = color.trim();
|
|
24
|
-
if (!hex.startsWith("#")) return color;
|
|
25
|
-
const h = hex.slice(1);
|
|
26
|
-
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
27
|
-
if (full.length !== 6) return color;
|
|
28
|
-
const n = Number.parseInt(full, 16);
|
|
29
|
-
if (Number.isNaN(n)) return color;
|
|
30
|
-
return `rgba(${n >> 16 & 255}, ${n >> 8 & 255}, ${n & 255}, ${alpha})`;
|
|
31
|
-
}
|
|
32
|
-
function resolveSwimlaneGroup(group, headerSize = SWIMLANE_GROUP_HEADER_SIZE) {
|
|
33
|
-
const pos = group.position ?? ORIGIN;
|
|
34
|
-
const lanes = [];
|
|
35
|
-
let offset = pos.y + headerSize;
|
|
36
|
-
for (const lane of group.lanes) {
|
|
37
|
-
lanes.push({
|
|
38
|
-
id: lane.id,
|
|
39
|
-
groupId: group.id,
|
|
40
|
-
label: lane.label,
|
|
41
|
-
// Only an explicit per-lane color tints a lane; the group's accent color
|
|
42
|
-
// is header-only, never a lane background wash.
|
|
43
|
-
color: lane.color,
|
|
44
|
-
top: offset,
|
|
45
|
-
height: lane.size,
|
|
46
|
-
x: pos.x,
|
|
47
|
-
width: group.length
|
|
48
|
-
});
|
|
49
|
-
offset += lane.size;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
id: group.id,
|
|
53
|
-
label: group.label,
|
|
54
|
-
color: group.color,
|
|
55
|
-
x: pos.x,
|
|
56
|
-
width: group.length,
|
|
57
|
-
top: pos.y,
|
|
58
|
-
headerSize,
|
|
59
|
-
bottom: offset,
|
|
60
|
-
lanes
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function resolveSwimlanes(groups, headerSize = SWIMLANE_GROUP_HEADER_SIZE) {
|
|
64
|
-
return groups.map((group) => resolveSwimlaneGroup(group, headerSize));
|
|
65
|
-
}
|
|
66
|
-
function flattenLanes(groups) {
|
|
67
|
-
return groups.flatMap((group) => group.lanes);
|
|
68
|
-
}
|
|
69
|
-
function findLaneByCoverage(lanes, top, height, preferredLaneId) {
|
|
70
|
-
if (lanes.length === 0) return null;
|
|
71
|
-
const bottom = top + height;
|
|
72
|
-
let best = null;
|
|
73
|
-
let bestOverlap = -1;
|
|
74
|
-
for (const lane of lanes) {
|
|
75
|
-
const overlap = Math.max(0, Math.min(bottom, lane.top + lane.height) - Math.max(top, lane.top));
|
|
76
|
-
if (overlap > bestOverlap || overlap === bestOverlap && lane.id === preferredLaneId) {
|
|
77
|
-
bestOverlap = overlap;
|
|
78
|
-
best = lane;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (bestOverlap > 0) return best;
|
|
82
|
-
const center = top + height / 2;
|
|
83
|
-
let nearest = null;
|
|
84
|
-
let bestDist = Number.POSITIVE_INFINITY;
|
|
85
|
-
for (const lane of lanes) {
|
|
86
|
-
const dist = Math.abs(lane.top + lane.height / 2 - center);
|
|
87
|
-
if (dist < bestDist) {
|
|
88
|
-
bestDist = dist;
|
|
89
|
-
nearest = lane;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return nearest;
|
|
93
|
-
}
|
|
94
|
-
function requiredLaneHeight(lane, nodes, marginBottom) {
|
|
95
|
-
let min = SWIMLANE_MIN_HEIGHT;
|
|
96
|
-
for (const node of nodes) {
|
|
97
|
-
if (node.laneId !== lane.id || !node.size) continue;
|
|
98
|
-
const need = node.position.y + node.size.height + marginBottom - lane.top;
|
|
99
|
-
if (need > min) min = need;
|
|
100
|
-
}
|
|
101
|
-
return min;
|
|
102
|
-
}
|
|
103
|
-
function swimlaneChromeAt(groups, viewport, point, headerHeight, labelWidth) {
|
|
104
|
-
const { x: vx, y: vy, zoom } = viewport;
|
|
105
|
-
const headerBottomByGroup = /* @__PURE__ */ new Map();
|
|
106
|
-
for (const group of groups) {
|
|
107
|
-
const screenTop = group.top * zoom + vy;
|
|
108
|
-
const screenBottom = group.bottom * zoom + vy;
|
|
109
|
-
const stickyY = Math.max(0, Math.min(screenTop, screenBottom - headerHeight));
|
|
110
|
-
headerBottomByGroup.set(group.id, stickyY + headerHeight);
|
|
111
|
-
if (screenBottom > 0 && point.y >= stickyY && point.y <= stickyY + headerHeight) {
|
|
112
|
-
return { type: "group", id: group.id };
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
for (const group of groups) {
|
|
116
|
-
const headerBottom = headerBottomByGroup.get(group.id) ?? 0;
|
|
117
|
-
for (const lane of group.lanes) {
|
|
118
|
-
const screenX = lane.x * zoom + vx;
|
|
119
|
-
const screenTop = lane.top * zoom + vy;
|
|
120
|
-
const screenBottom = screenTop + lane.height * zoom;
|
|
121
|
-
const screenEndX = screenX + lane.width * zoom;
|
|
122
|
-
const stickyX = Math.max(0, Math.min(screenX, screenEndX - labelWidth));
|
|
123
|
-
const top = Math.max(headerBottom, screenTop);
|
|
124
|
-
if (point.x >= stickyX && point.x <= stickyX + labelWidth && point.y >= top && point.y <= screenBottom) {
|
|
125
|
-
return { type: "lane", id: lane.id };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
function clampNodeToLanes(position, size, lanes, margin, labelWidth, preferredLaneId) {
|
|
132
|
-
const lane = findLaneByCoverage(lanes, position.y, size.height, preferredLaneId);
|
|
133
|
-
if (!lane) return { position, laneId: preferredLaneId ?? null };
|
|
134
|
-
const minY = lane.top + margin.top;
|
|
135
|
-
const maxY = Math.max(minY, lane.top + lane.height - size.height - margin.bottom);
|
|
136
|
-
const minX = lane.x + labelWidth + margin.left;
|
|
137
|
-
return {
|
|
138
|
-
position: {
|
|
139
|
-
x: Math.max(minX, position.x),
|
|
140
|
-
y: clamp(position.y, minY, maxY)
|
|
141
|
-
},
|
|
142
|
-
laneId: lane.id
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export { SWIMLANE_GROUP_HEADER_SIZE, SWIMLANE_LABEL_WIDTH, SWIMLANE_MIN_HEIGHT, clamp, clampNodeToLanes, findLaneByCoverage, flattenLanes, requiredLaneHeight, resolveMargin, resolveSwimlaneGroup, resolveSwimlanes, swimlaneChromeAt, withAlpha };
|
|
147
|
-
//# sourceMappingURL=chunk-6XRQSAQT.js.map
|
|
148
|
-
//# sourceMappingURL=chunk-6XRQSAQT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../utils/src/math.ts","../src/swimlane/lanes.ts"],"names":[],"mappings":"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","file":"chunk-6XRQSAQT.js","sourcesContent":["/** Clamp `value` into the inclusive range `[min, max]`. */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n\n/** Linear interpolation between `a` and `b` by `t` (unclamped). */\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t\n}\n\n/** Round `value` to `step`'s grid (used for snap-to-grid). */\nexport function snap(value: number, step: number): number {\n if (step <= 0) return value\n return Math.round(value / step) * step\n}\n","import { clamp } from \"@rivet/utils\"\nimport type { Rect, RivetNode, Size, SwimlaneGroup, SwimlaneMargin, Vec2, Viewport } from \"../types\"\n\n/** World height of a group's sticky header band. */\nexport const SWIMLANE_GROUP_HEADER_SIZE = 32\n/** Screen width of the sticky vertical lane-label strip, in px. */\nexport const SWIMLANE_LABEL_WIDTH = 24\n/** Floor for a lane's height when resized, in world units. */\nexport const SWIMLANE_MIN_HEIGHT = 80\n\n/** A margin with every side filled in. */\nexport type ResolvedMargin = { top: number; right: number; bottom: number; left: number }\n\n/** Normalize a `swimlaneMargin` prop (number shorthand or per-side) to all sides. */\nexport function resolveMargin(margin?: number | SwimlaneMargin): ResolvedMargin {\n if (typeof margin === \"number\") {\n return { top: margin, right: margin, bottom: margin, left: margin }\n }\n return {\n top: margin?.top ?? 16,\n right: margin?.right ?? 16,\n bottom: margin?.bottom ?? 16,\n left: margin?.left ?? 16,\n }\n}\n\n/** A lane resolved to world-space geometry (its body, below the group header). */\nexport type ResolvedLane = {\n id: string\n groupId: string\n label?: string\n color?: string\n /** World-space top edge of the lane body. */\n top: number\n height: number\n /** World x origin and nominal width (bands render effectively infinite). */\n x: number\n width: number\n}\n\n/** A group resolved to world-space geometry: its header band plus its lanes. */\nexport type ResolvedSwimlaneGroup = {\n id: string\n label?: string\n color?: string\n x: number\n width: number\n /** World top edge of the header band. */\n top: number\n /** Header band height in world units. */\n headerSize: number\n /** World bottom edge of the whole group (its last lane's bottom). */\n bottom: number\n lanes: ResolvedLane[]\n}\n\n/** A resolved lane plus how much of it is currently on screen. */\nexport type VisibleLane = ResolvedLane & {\n /** Area (world units²) of the lane's overlap with the viewport. */\n visibleArea: number\n}\n\n/**\n * A snapshot of what the pane is currently showing, produced by\n * `getViewportElements`. Read imperatively — it never subscribes to the\n * viewport, so calling it does not re-render.\n */\nexport type ViewportElements<TNodeData = unknown> = {\n /** The world-space rect currently visible in the pane. */\n rect: Rect\n /** Nodes whose world box intersects the viewport. */\n nodes: RivetNode<TNodeData>[]\n /** Visible lanes, largest on-screen area first. `lanes[0]` is the dominant lane. */\n lanes: VisibleLane[]\n}\n\nconst ORIGIN: Vec2 = { x: 0, y: 0 }\n\n/**\n * Return `color` as an `rgba()` with the given alpha. Hex (`#rgb`/`#rrggbb`) is\n * converted; any other form (already-rgba, named) is returned unchanged. Used so\n * a solid accent color renders as a subtle tint, never an opaque wash.\n */\nexport function withAlpha(color: string, alpha: number): string {\n const hex = color.trim()\n if (!hex.startsWith(\"#\")) return color\n const h = hex.slice(1)\n const full =\n h.length === 3\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h\n if (full.length !== 6) return color\n const n = Number.parseInt(full, 16)\n if (Number.isNaN(n)) return color\n return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`\n}\n\n/** Lay a group out: a header band at `position.y`, then its lanes stacked below. */\nexport function resolveSwimlaneGroup(\n group: SwimlaneGroup,\n headerSize = SWIMLANE_GROUP_HEADER_SIZE,\n): ResolvedSwimlaneGroup {\n const pos = group.position ?? ORIGIN\n const lanes: ResolvedLane[] = []\n let offset = pos.y + headerSize\n for (const lane of group.lanes) {\n lanes.push({\n id: lane.id,\n groupId: group.id,\n label: lane.label,\n // Only an explicit per-lane color tints a lane; the group's accent color\n // is header-only, never a lane background wash.\n color: lane.color,\n top: offset,\n height: lane.size,\n x: pos.x,\n width: group.length,\n })\n offset += lane.size\n }\n return {\n id: group.id,\n label: group.label,\n color: group.color,\n x: pos.x,\n width: group.length,\n top: pos.y,\n headerSize,\n bottom: offset,\n lanes,\n }\n}\n\n/** Resolve every group in a `swimlane` prop to world-space geometry. */\nexport function resolveSwimlanes(\n groups: SwimlaneGroup[],\n headerSize = SWIMLANE_GROUP_HEADER_SIZE,\n): ResolvedSwimlaneGroup[] {\n return groups.map((group) => resolveSwimlaneGroup(group, headerSize))\n}\n\n/** Flatten every group's lanes into a single list. */\nexport function flattenLanes(groups: ResolvedSwimlaneGroup[]): ResolvedLane[] {\n return groups.flatMap((group) => group.lanes)\n}\n\n/**\n * The lane a node box `[top, top+height]` belongs to: the one it overlaps most\n * (ties prefer `preferredLaneId`), or — with no overlap at all — the nearest by\n * center. Returns null only when there are no lanes.\n */\nexport function findLaneByCoverage(\n lanes: ResolvedLane[],\n top: number,\n height: number,\n preferredLaneId?: string,\n): ResolvedLane | null {\n if (lanes.length === 0) return null\n const bottom = top + height\n let best: ResolvedLane | null = null\n let bestOverlap = -1\n for (const lane of lanes) {\n const overlap = Math.max(0, Math.min(bottom, lane.top + lane.height) - Math.max(top, lane.top))\n if (overlap > bestOverlap || (overlap === bestOverlap && lane.id === preferredLaneId)) {\n bestOverlap = overlap\n best = lane\n }\n }\n if (bestOverlap > 0) return best\n\n // No vertical overlap with any lane — snap to the nearest by center.\n const center = top + height / 2\n let nearest: ResolvedLane | null = null\n let bestDist = Number.POSITIVE_INFINITY\n for (const lane of lanes) {\n const dist = Math.abs(lane.top + lane.height / 2 - center)\n if (dist < bestDist) {\n bestDist = dist\n nearest = lane\n }\n }\n return nearest\n}\n\n/**\n * The smallest height `lane` can take while still containing every node assigned\n * to it (by `laneId`), keeping `marginBottom` clearance below the lowest one.\n * Never returns less than {@link SWIMLANE_MIN_HEIGHT}. Backs both the resize\n * floor and auto-fit growth.\n */\nexport function requiredLaneHeight(\n lane: ResolvedLane,\n nodes: Iterable<RivetNode>,\n marginBottom: number,\n): number {\n let min = SWIMLANE_MIN_HEIGHT\n for (const node of nodes) {\n if (node.laneId !== lane.id || !node.size) continue\n const need = node.position.y + node.size.height + marginBottom - lane.top\n if (need > min) min = need\n }\n return min\n}\n\n/** A hit on sticky swimlane chrome (a group header or a lane label). */\nexport type SwimlaneChromeHit = { type: \"group\" | \"lane\"; id: string }\n\n/**\n * Which piece of sticky swimlane chrome sits under a pane-relative `point`, or\n * null. Mirrors the overlay's sticky math so a pane context-menu handler can\n * route right-clicks without the overlay needing to capture pointer events.\n */\nexport function swimlaneChromeAt(\n groups: ResolvedSwimlaneGroup[],\n viewport: Viewport,\n point: Vec2,\n headerHeight: number,\n labelWidth: number,\n): SwimlaneChromeHit | null {\n const { x: vx, y: vy, zoom } = viewport\n const headerBottomByGroup = new Map<string, number>()\n\n for (const group of groups) {\n const screenTop = group.top * zoom + vy\n const screenBottom = group.bottom * zoom + vy\n const stickyY = Math.max(0, Math.min(screenTop, screenBottom - headerHeight))\n headerBottomByGroup.set(group.id, stickyY + headerHeight)\n if (screenBottom > 0 && point.y >= stickyY && point.y <= stickyY + headerHeight) {\n return { type: \"group\", id: group.id }\n }\n }\n\n for (const group of groups) {\n const headerBottom = headerBottomByGroup.get(group.id) ?? 0\n for (const lane of group.lanes) {\n const screenX = lane.x * zoom + vx\n const screenTop = lane.top * zoom + vy\n const screenBottom = screenTop + lane.height * zoom\n const screenEndX = screenX + lane.width * zoom\n const stickyX = Math.max(0, Math.min(screenX, screenEndX - labelWidth))\n const top = Math.max(headerBottom, screenTop)\n if (\n point.x >= stickyX &&\n point.x <= stickyX + labelWidth &&\n point.y >= top &&\n point.y <= screenBottom\n ) {\n return { type: \"lane\", id: lane.id }\n }\n }\n }\n return null\n}\n\nexport type LaneClampResult = { position: Vec2; laneId: string | null }\n\n/**\n * Clamp a node so its whole body sits inside one lane, honoring `margin` (and\n * the left label strip). The target lane is chosen by {@link findLaneByCoverage}\n * — pass `preferredLaneId` to bias toward the node's current lane. Horizontally\n * only the left edge is bounded, since bands are effectively infinite.\n */\nexport function clampNodeToLanes(\n position: Vec2,\n size: Size,\n lanes: ResolvedLane[],\n margin: ResolvedMargin,\n labelWidth: number,\n preferredLaneId?: string,\n): LaneClampResult {\n const lane = findLaneByCoverage(lanes, position.y, size.height, preferredLaneId)\n if (!lane) return { position, laneId: preferredLaneId ?? null }\n\n const minY = lane.top + margin.top\n const maxY = Math.max(minY, lane.top + lane.height - size.height - margin.bottom)\n const minX = lane.x + labelWidth + margin.left\n return {\n position: {\n x: Math.max(minX, position.x),\n y: clamp(position.y, minY, maxY),\n },\n laneId: lane.id,\n }\n}\n"]}
|
package/dist/chunk-QHMB4WYT.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// src/anchor/registry.ts
|
|
2
|
-
var ANCHOR_SIDES = ["top", "right", "bottom", "left"];
|
|
3
|
-
function findSelectableAnchor(records, target) {
|
|
4
|
-
let found = null;
|
|
5
|
-
for (const record of records) {
|
|
6
|
-
if (!record.selectable || !record.element?.contains(target)) continue;
|
|
7
|
-
if (found?.element && record.element.contains(found.element)) continue;
|
|
8
|
-
found = record;
|
|
9
|
-
}
|
|
10
|
-
return found;
|
|
11
|
-
}
|
|
12
|
-
var DEFAULT_ANCHOR_OPTIONS = {
|
|
13
|
-
defaultSide: "top",
|
|
14
|
-
centerKeepoutPct: 14,
|
|
15
|
-
cornerMarginPct: 8,
|
|
16
|
-
dotSize: 7,
|
|
17
|
-
strayHandleSize: 8
|
|
18
|
-
};
|
|
19
|
-
var STRAY_PREFIX = "inode-";
|
|
20
|
-
var DOT_PREFIX = "inode-text-";
|
|
21
|
-
function isSide(value) {
|
|
22
|
-
return ANCHOR_SIDES.includes(value);
|
|
23
|
-
}
|
|
24
|
-
function anchorHandleId(anchorId, side) {
|
|
25
|
-
return `${STRAY_PREFIX}${anchorId}-${side}`;
|
|
26
|
-
}
|
|
27
|
-
function parseAnchorHandleId(handleId) {
|
|
28
|
-
if (!handleId?.startsWith(STRAY_PREFIX) || handleId.startsWith(DOT_PREFIX)) return null;
|
|
29
|
-
const lastDash = handleId.lastIndexOf("-");
|
|
30
|
-
const side = handleId.slice(lastDash + 1);
|
|
31
|
-
if (!isSide(side)) return null;
|
|
32
|
-
const anchorId = handleId.slice(STRAY_PREFIX.length, lastDash);
|
|
33
|
-
if (!anchorId) return null;
|
|
34
|
-
return { anchorId, side };
|
|
35
|
-
}
|
|
36
|
-
var AUTO_SIDE = "auto";
|
|
37
|
-
function anchorAutoHandleId(anchorId) {
|
|
38
|
-
return `${STRAY_PREFIX}${anchorId}-${AUTO_SIDE}`;
|
|
39
|
-
}
|
|
40
|
-
function parseAnchorAutoHandleId(handleId) {
|
|
41
|
-
if (!handleId?.startsWith(STRAY_PREFIX) || handleId.startsWith(DOT_PREFIX)) return null;
|
|
42
|
-
const lastDash = handleId.lastIndexOf("-");
|
|
43
|
-
if (handleId.slice(lastDash + 1) !== AUTO_SIDE) return null;
|
|
44
|
-
const anchorId = handleId.slice(STRAY_PREFIX.length, lastDash);
|
|
45
|
-
return anchorId || null;
|
|
46
|
-
}
|
|
47
|
-
function anchorDotHandleId(anchorId, side) {
|
|
48
|
-
return `${DOT_PREFIX}${anchorId}-${side}`;
|
|
49
|
-
}
|
|
50
|
-
function parseAnchorDotHandleId(handleId) {
|
|
51
|
-
if (!handleId?.startsWith(DOT_PREFIX)) return null;
|
|
52
|
-
const lastDash = handleId.lastIndexOf("-");
|
|
53
|
-
const side = handleId.slice(lastDash + 1);
|
|
54
|
-
if (!isSide(side)) return null;
|
|
55
|
-
const anchorId = handleId.slice(DOT_PREFIX.length, lastDash);
|
|
56
|
-
if (!anchorId) return null;
|
|
57
|
-
return { anchorId, side };
|
|
58
|
-
}
|
|
59
|
-
function normalizeAnchorConnection(connection) {
|
|
60
|
-
const source = parseAnchorDotHandleId(connection.sourceHandle);
|
|
61
|
-
const target = parseAnchorDotHandleId(connection.targetHandle);
|
|
62
|
-
if (!source && !target) return connection;
|
|
63
|
-
return {
|
|
64
|
-
...connection,
|
|
65
|
-
sourceHandle: source ? anchorHandleId(source.anchorId, source.side) : connection.sourceHandle,
|
|
66
|
-
targetHandle: target ? anchorHandleId(target.anchorId, target.side) : connection.targetHandle
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
function computeAnchorGeometry(root, first, last, box) {
|
|
70
|
-
const point = (x, y) => ({
|
|
71
|
-
xPct: (x - root.left) / root.width * 100,
|
|
72
|
-
yPct: (y - root.top) / root.height * 100
|
|
73
|
-
});
|
|
74
|
-
return {
|
|
75
|
-
firstLineYPct: (first.top + first.height / 2 - root.top) / root.height * 100,
|
|
76
|
-
lastLineYPct: (last.top + last.height / 2 - root.top) / root.height * 100,
|
|
77
|
-
centerXPct: (box.left + box.width / 2 - root.left) / root.width * 100,
|
|
78
|
-
dots: {
|
|
79
|
-
top: point(first.left + first.width / 2, first.top),
|
|
80
|
-
bottom: point(last.left + last.width / 2, last.top + last.height),
|
|
81
|
-
left: point(first.left, first.top + first.height / 2),
|
|
82
|
-
right: point(last.left + last.width, last.top + last.height / 2)
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function orderedSelectableAnchors(records) {
|
|
87
|
-
return records.filter((record) => record.selectable).map((record, index) => ({ record, index })).sort((a, b) => {
|
|
88
|
-
const left = a.record.geometry;
|
|
89
|
-
const right = b.record.geometry;
|
|
90
|
-
if (!left || !right) {
|
|
91
|
-
if (left) return -1;
|
|
92
|
-
if (right) return 1;
|
|
93
|
-
return a.index - b.index;
|
|
94
|
-
}
|
|
95
|
-
const rowDelta = left.firstLineYPct - right.firstLineYPct;
|
|
96
|
-
if (Math.abs(rowDelta) > SAME_ROW_TOLERANCE_PCT) return rowDelta;
|
|
97
|
-
return left.centerXPct - right.centerXPct;
|
|
98
|
-
}).map(({ record }) => record);
|
|
99
|
-
}
|
|
100
|
-
var SAME_ROW_TOLERANCE_PCT = 2;
|
|
101
|
-
function anchorRect(node, geometry) {
|
|
102
|
-
const { dots } = geometry;
|
|
103
|
-
const left = Math.min(dots.left.xPct, dots.right.xPct);
|
|
104
|
-
const right = Math.max(dots.left.xPct, dots.right.xPct);
|
|
105
|
-
const top = Math.min(dots.top.yPct, dots.bottom.yPct);
|
|
106
|
-
const bottom = Math.max(dots.top.yPct, dots.bottom.yPct);
|
|
107
|
-
return {
|
|
108
|
-
x: node.x + left / 100 * node.width,
|
|
109
|
-
y: node.y + top / 100 * node.height,
|
|
110
|
-
width: (right - left) / 100 * node.width,
|
|
111
|
-
height: (bottom - top) / 100 * node.height
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
var GEOMETRY_TOLERANCE_PCT = 0.1;
|
|
115
|
-
function pointsEqual(a, b) {
|
|
116
|
-
return Math.abs(a.xPct - b.xPct) < GEOMETRY_TOLERANCE_PCT && Math.abs(a.yPct - b.yPct) < GEOMETRY_TOLERANCE_PCT;
|
|
117
|
-
}
|
|
118
|
-
function anchorGeometryEqual(a, b) {
|
|
119
|
-
if (a === b) return true;
|
|
120
|
-
if (!a || !b) return false;
|
|
121
|
-
return Math.abs(a.firstLineYPct - b.firstLineYPct) < GEOMETRY_TOLERANCE_PCT && Math.abs(a.lastLineYPct - b.lastLineYPct) < GEOMETRY_TOLERANCE_PCT && Math.abs(a.centerXPct - b.centerXPct) < GEOMETRY_TOLERANCE_PCT && ANCHOR_SIDES.every((side) => pointsEqual(a.dots[side], b.dots[side]));
|
|
122
|
-
}
|
|
123
|
-
function placeAlongSide(rawPct, options) {
|
|
124
|
-
const clamped = Math.min(Math.max(rawPct, options.cornerMarginPct), 100 - options.cornerMarginPct);
|
|
125
|
-
if (Math.abs(clamped - 50) >= options.centerKeepoutPct) return clamped;
|
|
126
|
-
return clamped < 50 ? 50 - options.centerKeepoutPct : 50 + options.centerKeepoutPct;
|
|
127
|
-
}
|
|
128
|
-
function strayPoint(geometry, side, options) {
|
|
129
|
-
const along = strayPlacementPct(geometry, side, options);
|
|
130
|
-
switch (side) {
|
|
131
|
-
case "left":
|
|
132
|
-
return { xPct: 0, yPct: along };
|
|
133
|
-
case "right":
|
|
134
|
-
return { xPct: 100, yPct: along };
|
|
135
|
-
case "top":
|
|
136
|
-
return { xPct: along, yPct: 0 };
|
|
137
|
-
case "bottom":
|
|
138
|
-
return { xPct: along, yPct: 100 };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function strayPlacementPct(geometry, side, options) {
|
|
142
|
-
const raw = side === "left" ? geometry.firstLineYPct : side === "right" ? geometry.lastLineYPct : geometry.centerXPct;
|
|
143
|
-
return placeAlongSide(raw, options);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export { ANCHOR_SIDES, DEFAULT_ANCHOR_OPTIONS, anchorAutoHandleId, anchorDotHandleId, anchorGeometryEqual, anchorHandleId, anchorRect, computeAnchorGeometry, findSelectableAnchor, normalizeAnchorConnection, orderedSelectableAnchors, parseAnchorAutoHandleId, parseAnchorDotHandleId, parseAnchorHandleId, strayPlacementPct, strayPoint };
|
|
147
|
-
//# sourceMappingURL=chunk-QHMB4WYT.js.map
|
|
148
|
-
//# sourceMappingURL=chunk-QHMB4WYT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/anchor/registry.ts"],"names":[],"mappings":"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","file":"chunk-QHMB4WYT.js","sourcesContent":["/**\n * Anchor — a connectable point bound to a DOM element rendered inside a node.\n *\n * Rivet knows nothing about what the element means (an inline text span, an\n * icon, a table row — that's the consumer's business); it owns the geometry:\n * measuring the element's line boxes within the node, rendering grab dots and\n * stray perimeter handles from those measurements, and rewriting dot handle\n * ids to the persisted stray ids when a connection commits.\n *\n * Placement derives from the element's *first* and *last* client line rects —\n * never from CSS positioning inside the inline element, which Chrome anchors\n * to the first line box only — so a line-wrapped span places each dot where it\n * visually belongs. All values are percentages of the node box: client rects\n * scale together under the viewport transform, so the ratio cancels zoom.\n */\n\nimport type { Connection, HandlePosition, NodeId, Rect } from \"../types\"\n\n/** The four sides an anchor can expose handles on. */\nexport const ANCHOR_SIDES = [\"top\", \"right\", \"bottom\", \"left\"] as const\n\n/** A measured point within a node's box, as percentages (zoom-independent). */\nexport type AnchorPoint = { xPct: number; yPct: number }\n\n/** Where an anchor's element sits within its node box, from measured line rects. */\nexport type AnchorGeometry = {\n /** Vertical center of the element's first line box, as % of the node height. */\n firstLineYPct: number\n /** Vertical center of the element's last line box, as % of the node height. */\n lastLineYPct: number\n /** Horizontal center of the element's bounding box, as % of the node width. */\n centerXPct: number\n /**\n * Grab dot positions: left/top on the first line box, right/bottom on the\n * last, so a wrapped element gets each dot on the line it belongs to.\n */\n dots: Record<HandlePosition, AnchorPoint>\n}\n\n/**\n * Stray-handle chrome policy, chosen at registration:\n *\n * - `\"dynamic\"` — one perimeter handle per connected side, a single\n * default-side handle while unconnected. The inline-text default.\n * - `\"none\"` — no stray chrome at all; the anchor connects through its grab\n * dots only. For anchors whose element boundary *is* the visual connection\n * point (an SVG shape), where a floating perimeter handle reads as clutter.\n * Purely visual: edges bound to stray ids still resolve at the measured\n * placement — the renderer derives endpoints from geometry, not chrome.\n */\nexport type AnchorStrayPolicy = \"dynamic\" | \"none\"\n\n/**\n * A registered anchor. `element` is `null` while detached — the consumer\n * unmounted the display (or an editor replaced the DOM node) without\n * unregistering, so the last-known `geometry` is retained and edges stay put\n * (stale retention). Unregistering is the explicit removal signal.\n */\nexport type AnchorRecord = {\n nodeId: NodeId\n anchorId: string\n /** Accent color for the anchor's dots and stray handles. */\n color?: string\n /** Stray chrome policy. Absent = `\"dynamic\"`. */\n strays?: AnchorStrayPolicy\n /** Whether the anchor is a selection target at all — by press or key. Absent = `false`. */\n selectable?: boolean\n /**\n * Whatever the consumer attached at registration — a shape's label, the\n * bounds it was imported at, the layer it came from. Rivet stores and returns\n * it, and never reads it.\n */\n data?: unknown\n element: Element | null\n /** Null until the first successful measurement. */\n geometry: AnchorGeometry | null\n}\n\nexport type AnchorRegistrationOptions = {\n color?: string\n /** Stray-handle chrome policy for this anchor. Default `\"dynamic\"`. */\n strays?: AnchorStrayPolicy\n /**\n * Make the anchor a selection target: the element becomes pointer-interactive\n * so a click selects it, and it joins the node's keyboard stepping order\n * (Alt+Left/Right on the focused node).\n *\n * Default `false`: an anchor around inline text usually sits in editable\n * content, where swallowing a press would stop the caret from landing. Shapes\n * are the opposite case — the element *is* the thing you mean to click. One\n * flag for both routes, so an anchor is never selectable one way and not the\n * other.\n */\n selectable?: boolean\n /** Opaque consumer payload, returned on the record untouched. */\n data?: unknown\n}\n\n/** Identifies one anchor: which node holds it, and which anchor on that node. */\nexport type AnchorRef = {\n nodeId: NodeId\n anchorId: string\n}\n\n/**\n * The selectable anchor a press landed in, or `null` for a press anywhere else\n * on the node. Innermost wins: anchors can nest (a shape inside a group that is\n * itself an anchor), and the deepest one is the one under the pointer.\n */\nexport function findSelectableAnchor(\n records: readonly AnchorRecord[],\n target: Element,\n): AnchorRecord | null {\n let found: AnchorRecord | null = null\n for (const record of records) {\n if (!record.selectable || !record.element?.contains(target)) continue\n if (found?.element && record.element.contains(found.element)) continue\n found = record\n }\n return found\n}\n\n/**\n * Imperative registration handle for headless consumers (rich-text editors and\n * other renderers that own their DOM). Returned by `RivetInstance.registerAnchor`.\n */\nexport type AnchorRegistration = {\n /**\n * Re-attach the anchor after the renderer replaced its DOM node. Passing\n * `null` detaches instead: geometry is retained and edges stay put until the\n * element (or a replacement) is rebound or the anchor is unregistered.\n */\n rebind: (element: Element | null) => void\n /** Explicit removal — drops the anchor and the chrome rendered for it. */\n unregister: () => void\n}\n\n/** Placement policy for anchor chrome, set via the `anchorOptions` prop. */\nexport type AnchorOptions = {\n /** Side an unconnected anchor shows its single stray handle on. Default `\"top\"`. */\n defaultSide?: HandlePosition\n /**\n * Minimum distance (in % of the side) a stray handle keeps from the side's\n * center, where a node's own side-center handles usually sit. Default `14`.\n */\n centerKeepoutPct?: number\n /** Minimum distance (in % of the side) a stray handle keeps from the corners. Default `8`. */\n cornerMarginPct?: number\n /** Grab dot diameter in px. Default `7`. */\n dotSize?: number\n /** Stray handle diameter in px. Default `8`. */\n strayHandleSize?: number\n}\n\nexport type ResolvedAnchorOptions = Required<AnchorOptions>\n\nexport const DEFAULT_ANCHOR_OPTIONS: ResolvedAnchorOptions = {\n defaultSide: \"top\",\n centerKeepoutPct: 14,\n cornerMarginPct: 8,\n dotSize: 7,\n strayHandleSize: 8,\n}\n\n// ============================================================================\n// Handle-id grammar\n// ============================================================================\n//\n// The string forms are the persisted wire format for anchor connectivity:\n// `inode-<anchorId>-<side>` for the stray (perimeter) handles that edges\n// reference, `inode-text-<anchorId>-<side>` for the in-content grab dots that\n// only ever *start* a drag. The `inode-` prefix is the historical grammar from\n// the pre-primitive consumer implementation, kept verbatim so existing graphs\n// load without migration; the structured edge-end model replaces these strings\n// later without changing what's stored.\n\nconst STRAY_PREFIX = \"inode-\"\nconst DOT_PREFIX = \"inode-text-\"\n\nfunction isSide(value: string): value is HandlePosition {\n return (ANCHOR_SIDES as readonly string[]).includes(value)\n}\n\n/** Build the persisted handle id for an anchor's stray handle on `side`. */\nexport function anchorHandleId(anchorId: string, side: HandlePosition): string {\n return `${STRAY_PREFIX}${anchorId}-${side}`\n}\n\n/** Parse a stray anchor handle id back into `{ anchorId, side }`, or null. */\nexport function parseAnchorHandleId(\n handleId: string | null | undefined,\n): { anchorId: string; side: HandlePosition } | null {\n // A dot handle id shares the stray prefix; it is a local-only affordance,\n // never a persisted edge handle, so it must not be read as a stray here.\n if (!handleId?.startsWith(STRAY_PREFIX) || handleId.startsWith(DOT_PREFIX)) return null\n const lastDash = handleId.lastIndexOf(\"-\")\n const side = handleId.slice(lastDash + 1)\n if (!isSide(side)) return null\n const anchorId = handleId.slice(STRAY_PREFIX.length, lastDash)\n if (!anchorId) return null\n return { anchorId, side }\n}\n\n/**\n * The auto-side marker in the anchor id grammar. `inode-<anchorId>-auto` is an\n * anchor end with *no pinned side*: the renderer resolves the side per frame to\n * face the peer node (a floating end). Not a side, so it never collides with\n * the stray forms — but an `anchorId` should not itself end in `-auto` or a\n * `-<side>` suffix, which the grammar cannot distinguish.\n */\nconst AUTO_SIDE = \"auto\"\n\n/** Build the handle id for an anchor end with an auto (render-resolved) side. */\nexport function anchorAutoHandleId(anchorId: string): string {\n return `${STRAY_PREFIX}${anchorId}-${AUTO_SIDE}`\n}\n\n/** Parse an auto anchor handle id back into its `anchorId`, or null. */\nexport function parseAnchorAutoHandleId(handleId: string | null | undefined): string | null {\n if (!handleId?.startsWith(STRAY_PREFIX) || handleId.startsWith(DOT_PREFIX)) return null\n const lastDash = handleId.lastIndexOf(\"-\")\n if (handleId.slice(lastDash + 1) !== AUTO_SIDE) return null\n const anchorId = handleId.slice(STRAY_PREFIX.length, lastDash)\n return anchorId || null\n}\n\n/** Build the handle id for an anchor's in-content grab dot on `side`. */\nexport function anchorDotHandleId(anchorId: string, side: HandlePosition): string {\n return `${DOT_PREFIX}${anchorId}-${side}`\n}\n\n/** Parse a grab dot handle id back into `{ anchorId, side }`, or null. */\nexport function parseAnchorDotHandleId(\n handleId: string | null | undefined,\n): { anchorId: string; side: HandlePosition } | null {\n if (!handleId?.startsWith(DOT_PREFIX)) return null\n const lastDash = handleId.lastIndexOf(\"-\")\n const side = handleId.slice(lastDash + 1)\n if (!isSide(side)) return null\n const anchorId = handleId.slice(DOT_PREFIX.length, lastDash)\n if (!anchorId) return null\n return { anchorId, side }\n}\n\n/**\n * Rewrite grab dot ends to the persisted stray ids — same anchor, same side —\n * before a connection commits. Intent-preserving: the side comes from the dot\n * the user actually grabbed or dropped on. Runs before any consumer\n * `mapConnection`; connections not touching a dot pass through unchanged.\n */\nexport function normalizeAnchorConnection(connection: Connection): Connection {\n const source = parseAnchorDotHandleId(connection.sourceHandle)\n const target = parseAnchorDotHandleId(connection.targetHandle)\n if (!source && !target) return connection\n return {\n ...connection,\n sourceHandle: source ? anchorHandleId(source.anchorId, source.side) : connection.sourceHandle,\n targetHandle: target ? anchorHandleId(target.anchorId, target.side) : connection.targetHandle,\n }\n}\n\n// ============================================================================\n// Measurement\n// ============================================================================\n\n/** The rect fields measurement reads — satisfied by DOMRect. */\nexport type AnchorRectLike = {\n left: number\n top: number\n width: number\n height: number\n}\n\n/**\n * Derive an anchor's geometry from its node box (`root`), its first and last\n * client line rects, and its bounding box. Pure — callers supply the rects.\n */\nexport function computeAnchorGeometry(\n root: AnchorRectLike,\n first: AnchorRectLike,\n last: AnchorRectLike,\n box: AnchorRectLike,\n): AnchorGeometry {\n const point = (x: number, y: number): AnchorPoint => ({\n xPct: ((x - root.left) / root.width) * 100,\n yPct: ((y - root.top) / root.height) * 100,\n })\n return {\n firstLineYPct: ((first.top + first.height / 2 - root.top) / root.height) * 100,\n lastLineYPct: ((last.top + last.height / 2 - root.top) / root.height) * 100,\n centerXPct: ((box.left + box.width / 2 - root.left) / root.width) * 100,\n dots: {\n top: point(first.left + first.width / 2, first.top),\n bottom: point(last.left + last.width / 2, last.top + last.height),\n left: point(first.left, first.top + first.height / 2),\n right: point(last.left + last.width, last.top + last.height / 2),\n },\n }\n}\n\n/**\n * The box an anchor's element occupies inside `node`, in the same space that\n * rect is given in — how chrome that annotates a whole anchor (a peer's claim\n * on it, say) gets a rect out of measurements taken as percentages.\n *\n * Derived from the grab dots, which sit on the element's edges: left and top on\n * its first line box, right and bottom on its last. For anything that fits on\n * one line that *is* the element's box. A line-wrapped element instead reports\n * the span from where it starts to where it ends — the union of its line boxes\n * would be the node's whole text column, which describes the paragraph rather\n * than the anchor.\n */\n/**\n * A node's selectable anchors in **reading order** — first line box down the\n * node, then left to right within a line.\n *\n * The order keyboard stepping moves in, and it has to be reading order rather\n * than registration order: a consumer's JSX order is usually the same thing,\n * but an editor that re-registers anchors as its document changes has no reason\n * to keep them in sync, and stepping through a paragraph in the order its spans\n * happened to mount would be unusable.\n *\n * Unmeasured anchors sort last, keeping their relative order: they have no\n * position to compare, and putting them first would make the first press of a\n * key land somewhere arbitrary.\n */\nexport function orderedSelectableAnchors(\n records: readonly AnchorRecord[],\n): readonly AnchorRecord[] {\n return records\n .filter((record) => record.selectable)\n .map((record, index) => ({ record, index }))\n .sort((a, b) => {\n const left = a.record.geometry\n const right = b.record.geometry\n if (!left || !right) {\n if (left) return -1\n if (right) return 1\n return a.index - b.index\n }\n // A line's own height is the tolerance: two anchors on the same line\n // differ in `firstLineYPct` by measurement noise, not by a line.\n const rowDelta = left.firstLineYPct - right.firstLineYPct\n if (Math.abs(rowDelta) > SAME_ROW_TOLERANCE_PCT) return rowDelta\n return left.centerXPct - right.centerXPct\n })\n .map(({ record }) => record)\n}\n\n/** Vertical distance (% of node height) within which two anchors count as one row. */\nconst SAME_ROW_TOLERANCE_PCT = 2\n\nexport function anchorRect(node: Rect, geometry: AnchorGeometry): Rect {\n const { dots } = geometry\n // Sorted rather than assumed: a wrapped element can start further right than\n // it ends, which would otherwise come out as a negative width.\n const left = Math.min(dots.left.xPct, dots.right.xPct)\n const right = Math.max(dots.left.xPct, dots.right.xPct)\n const top = Math.min(dots.top.yPct, dots.bottom.yPct)\n const bottom = Math.max(dots.top.yPct, dots.bottom.yPct)\n return {\n x: node.x + (left / 100) * node.width,\n y: node.y + (top / 100) * node.height,\n width: ((right - left) / 100) * node.width,\n height: ((bottom - top) / 100) * node.height,\n }\n}\n\nconst GEOMETRY_TOLERANCE_PCT = 0.1\n\nfunction pointsEqual(a: AnchorPoint, b: AnchorPoint): boolean {\n return (\n Math.abs(a.xPct - b.xPct) < GEOMETRY_TOLERANCE_PCT &&\n Math.abs(a.yPct - b.yPct) < GEOMETRY_TOLERANCE_PCT\n )\n}\n\n/** Sub-tolerance equality, so measurement echoes don't churn subscribers. */\nexport function anchorGeometryEqual(a: AnchorGeometry | null, b: AnchorGeometry | null): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return (\n Math.abs(a.firstLineYPct - b.firstLineYPct) < GEOMETRY_TOLERANCE_PCT &&\n Math.abs(a.lastLineYPct - b.lastLineYPct) < GEOMETRY_TOLERANCE_PCT &&\n Math.abs(a.centerXPct - b.centerXPct) < GEOMETRY_TOLERANCE_PCT &&\n ANCHOR_SIDES.every((side) => pointsEqual(a.dots[side], b.dots[side]))\n )\n}\n\n// ============================================================================\n// Stray-handle placement along a node side\n// ============================================================================\n\n/**\n * Nudge a raw position (in % of the side) out of the side-center keep-out band\n * — where the node's own handles usually sit — and off the corners.\n */\nexport function placeAlongSide(rawPct: number, options: ResolvedAnchorOptions): number {\n const clamped = Math.min(Math.max(rawPct, options.cornerMarginPct), 100 - options.cornerMarginPct)\n if (Math.abs(clamped - 50) >= options.centerKeepoutPct) return clamped\n return clamped < 50 ? 50 - options.centerKeepoutPct : 50 + options.centerKeepoutPct\n}\n\n/**\n * Where an anchor's stray handle sits along `side`, as % of that side — the\n * measured row (left/right) or column (top/bottom) nearest the anchor, nudged\n * by {@link placeAlongSide}. Shared by the stray-handle chrome and by the edge\n * renderer when it places an auto anchor end on a render-resolved side.\n */\n/**\n * A stray handle's position on the node box, as percentages of it — the same\n * placement {@link strayPlacementPct} gives, resolved into both axes.\n *\n * Both axes, for every side, so one pair of numbers can put a stray anywhere on\n * the perimeter. That's what lets the chrome follow an end floating from one\n * side to another without re-rendering: the element's position is two style\n * writes rather than a different set of anchoring properties.\n */\nexport function strayPoint(\n geometry: AnchorGeometry,\n side: HandlePosition,\n options: ResolvedAnchorOptions,\n): AnchorPoint {\n const along = strayPlacementPct(geometry, side, options)\n switch (side) {\n case \"left\":\n return { xPct: 0, yPct: along }\n case \"right\":\n return { xPct: 100, yPct: along }\n case \"top\":\n return { xPct: along, yPct: 0 }\n case \"bottom\":\n return { xPct: along, yPct: 100 }\n }\n}\n\nexport function strayPlacementPct(\n geometry: AnchorGeometry,\n side: HandlePosition,\n options: ResolvedAnchorOptions,\n): number {\n const raw =\n side === \"left\"\n ? geometry.firstLineYPct\n : side === \"right\"\n ? geometry.lastLineYPct\n : geometry.centerXPct\n return placeAlongSide(raw, options)\n}\n"]}
|