@kolosal-ai/rivet 0.1.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.
@@ -0,0 +1,151 @@
1
+ import { H as HandlePosition, N as NodeId, C as Connection } from './types-B8AAJ60T.js';
2
+
3
+ /**
4
+ * Anchor — a connectable point bound to a DOM element rendered inside a node.
5
+ *
6
+ * Rivet knows nothing about what the element means (an inline text span, an
7
+ * icon, a table row — that's the consumer's business); it owns the geometry:
8
+ * measuring the element's line boxes within the node, rendering grab dots and
9
+ * stray perimeter handles from those measurements, and rewriting dot handle
10
+ * ids to the persisted stray ids when a connection commits.
11
+ *
12
+ * Placement derives from the element's *first* and *last* client line rects —
13
+ * never from CSS positioning inside the inline element, which Chrome anchors
14
+ * to the first line box only — so a line-wrapped span places each dot where it
15
+ * visually belongs. All values are percentages of the node box: client rects
16
+ * scale together under the viewport transform, so the ratio cancels zoom.
17
+ */
18
+
19
+ /** The four sides an anchor can expose handles on. */
20
+ declare const ANCHOR_SIDES: readonly ["top", "right", "bottom", "left"];
21
+ /** A measured point within a node's box, as percentages (zoom-independent). */
22
+ type AnchorPoint = {
23
+ xPct: number;
24
+ yPct: number;
25
+ };
26
+ /** Where an anchor's element sits within its node box, from measured line rects. */
27
+ type AnchorGeometry = {
28
+ /** Vertical center of the element's first line box, as % of the node height. */
29
+ firstLineYPct: number;
30
+ /** Vertical center of the element's last line box, as % of the node height. */
31
+ lastLineYPct: number;
32
+ /** Horizontal center of the element's bounding box, as % of the node width. */
33
+ centerXPct: number;
34
+ /**
35
+ * Grab dot positions: left/top on the first line box, right/bottom on the
36
+ * last, so a wrapped element gets each dot on the line it belongs to.
37
+ */
38
+ dots: Record<HandlePosition, AnchorPoint>;
39
+ };
40
+ /**
41
+ * Stray-handle chrome policy, chosen at registration:
42
+ *
43
+ * - `"dynamic"` — one perimeter handle per connected side, a single
44
+ * default-side handle while unconnected. The inline-text default.
45
+ * - `"none"` — no stray chrome at all; the anchor connects through its grab
46
+ * dots only. For anchors whose element boundary *is* the visual connection
47
+ * point (an SVG shape), where a floating perimeter handle reads as clutter.
48
+ * Purely visual: edges bound to stray ids still resolve at the measured
49
+ * placement — the renderer derives endpoints from geometry, not chrome.
50
+ */
51
+ type AnchorStrayPolicy = "dynamic" | "none";
52
+ /**
53
+ * A registered anchor. `element` is `null` while detached — the consumer
54
+ * unmounted the display (or an editor replaced the DOM node) without
55
+ * unregistering, so the last-known `geometry` is retained and edges stay put
56
+ * (stale retention). Unregistering is the explicit removal signal.
57
+ */
58
+ type AnchorRecord = {
59
+ nodeId: NodeId;
60
+ anchorId: string;
61
+ /** Accent color for the anchor's dots and stray handles. */
62
+ color?: string;
63
+ /** Stray chrome policy. Absent = `"dynamic"`. */
64
+ strays?: AnchorStrayPolicy;
65
+ element: Element | null;
66
+ /** Null until the first successful measurement. */
67
+ geometry: AnchorGeometry | null;
68
+ };
69
+ type AnchorRegistrationOptions = {
70
+ color?: string;
71
+ /** Stray-handle chrome policy for this anchor. Default `"dynamic"`. */
72
+ strays?: AnchorStrayPolicy;
73
+ };
74
+ /**
75
+ * Imperative registration handle for headless consumers (rich-text editors and
76
+ * other renderers that own their DOM). Returned by `RivetInstance.registerAnchor`.
77
+ */
78
+ type AnchorRegistration = {
79
+ /**
80
+ * Re-attach the anchor after the renderer replaced its DOM node. Passing
81
+ * `null` detaches instead: geometry is retained and edges stay put until the
82
+ * element (or a replacement) is rebound or the anchor is unregistered.
83
+ */
84
+ rebind: (element: Element | null) => void;
85
+ /** Explicit removal — drops the anchor and the chrome rendered for it. */
86
+ unregister: () => void;
87
+ };
88
+ /** Placement policy for anchor chrome, set via the `anchorOptions` prop. */
89
+ type AnchorOptions = {
90
+ /** Side an unconnected anchor shows its single stray handle on. Default `"top"`. */
91
+ defaultSide?: HandlePosition;
92
+ /**
93
+ * Minimum distance (in % of the side) a stray handle keeps from the side's
94
+ * center, where a node's own side-center handles usually sit. Default `14`.
95
+ */
96
+ centerKeepoutPct?: number;
97
+ /** Minimum distance (in % of the side) a stray handle keeps from the corners. Default `8`. */
98
+ cornerMarginPct?: number;
99
+ /** Grab dot diameter in px. Default `7`. */
100
+ dotSize?: number;
101
+ /** Stray handle diameter in px. Default `8`. */
102
+ strayHandleSize?: number;
103
+ };
104
+ type ResolvedAnchorOptions = Required<AnchorOptions>;
105
+ declare const DEFAULT_ANCHOR_OPTIONS: ResolvedAnchorOptions;
106
+ /** Build the persisted handle id for an anchor's stray handle on `side`. */
107
+ declare function anchorHandleId(anchorId: string, side: HandlePosition): string;
108
+ /** Parse a stray anchor handle id back into `{ anchorId, side }`, or null. */
109
+ declare function parseAnchorHandleId(handleId: string | null | undefined): {
110
+ anchorId: string;
111
+ side: HandlePosition;
112
+ } | null;
113
+ /** Build the handle id for an anchor end with an auto (render-resolved) side. */
114
+ declare function anchorAutoHandleId(anchorId: string): string;
115
+ /** Parse an auto anchor handle id back into its `anchorId`, or null. */
116
+ declare function parseAnchorAutoHandleId(handleId: string | null | undefined): string | null;
117
+ /** Build the handle id for an anchor's in-content grab dot on `side`. */
118
+ declare function anchorDotHandleId(anchorId: string, side: HandlePosition): string;
119
+ /** Parse a grab dot handle id back into `{ anchorId, side }`, or null. */
120
+ declare function parseAnchorDotHandleId(handleId: string | null | undefined): {
121
+ anchorId: string;
122
+ side: HandlePosition;
123
+ } | null;
124
+ /**
125
+ * Rewrite grab dot ends to the persisted stray ids — same anchor, same side —
126
+ * before a connection commits. Intent-preserving: the side comes from the dot
127
+ * the user actually grabbed or dropped on. Runs before any consumer
128
+ * `mapConnection`; connections not touching a dot pass through unchanged.
129
+ */
130
+ declare function normalizeAnchorConnection(connection: Connection): Connection;
131
+ /** The rect fields measurement reads — satisfied by DOMRect. */
132
+ type AnchorRectLike = {
133
+ left: number;
134
+ top: number;
135
+ width: number;
136
+ height: number;
137
+ };
138
+ /**
139
+ * Derive an anchor's geometry from its node box (`root`), its first and last
140
+ * client line rects, and its bounding box. Pure — callers supply the rects.
141
+ */
142
+ declare function computeAnchorGeometry(root: AnchorRectLike, first: AnchorRectLike, last: AnchorRectLike, box: AnchorRectLike): AnchorGeometry;
143
+ /**
144
+ * Where an anchor's stray handle sits along `side`, as % of that side — the
145
+ * measured row (left/right) or column (top/bottom) nearest the anchor, nudged
146
+ * by {@link placeAlongSide}. Shared by the stray-handle chrome and by the edge
147
+ * renderer when it places an auto anchor end on a render-resolved side.
148
+ */
149
+ declare function strayPlacementPct(geometry: AnchorGeometry, side: HandlePosition, options: ResolvedAnchorOptions): number;
150
+
151
+ export { ANCHOR_SIDES as A, DEFAULT_ANCHOR_OPTIONS as D, type ResolvedAnchorOptions as R, type AnchorGeometry as a, type AnchorOptions as b, type AnchorPoint as c, type AnchorRecord as d, type AnchorRegistration as e, type AnchorRegistrationOptions as f, type AnchorStrayPolicy as g, anchorAutoHandleId as h, anchorDotHandleId as i, anchorHandleId as j, computeAnchorGeometry as k, parseAnchorDotHandleId as l, parseAnchorHandleId as m, normalizeAnchorConnection as n, parseAnchorAutoHandleId as p, strayPlacementPct as s };
@@ -0,0 +1,127 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { g as AnchorStrayPolicy } from '../registry-Dkk4ZKt-.js';
4
+ import '../types-B8AAJ60T.js';
5
+
6
+ /**
7
+ * The extract seam behind `<SvgArtwork>` — which elements inside a parsed
8
+ * artwork become connectable anchors. The default is attribute-driven and
9
+ * convention-free: markup opts elements in with `data-anchor`. Consumers whose
10
+ * artwork follows other conventions (Figma layer names serialized as `id`s, a
11
+ * tool-specific class scheme) swap the extractor via the `extract` prop —
12
+ * granularity lives here, never in core heuristics.
13
+ */
14
+ /** An element inside the artwork to expose as a connectable anchor. */
15
+ type SvgAnchorTarget = {
16
+ /**
17
+ * Identifies the anchor within its node. Persisted edge handle ids derive
18
+ * from it, so it must be stable across parses of the same markup.
19
+ */
20
+ anchorId: string;
21
+ /** The live element inside the mounted artwork the anchor binds to. */
22
+ element: Element;
23
+ /** Accent color for the anchor's grab dots. */
24
+ color?: string;
25
+ };
26
+ /**
27
+ * The extract seam's contract: the mounted, sanitized `<svg>` root in, the
28
+ * anchor targets out. Runs after `parse`, re-runs when the markup changes.
29
+ */
30
+ type SvgAnchorExtractor = (root: SVGSVGElement) => SvgAnchorTarget[];
31
+ /**
32
+ * The default extractor: elements carrying a `data-anchor` attribute become
33
+ * anchors, in document order. The attribute's value is the anchor id; an empty
34
+ * value falls back to the element's `id`. Elements with neither are skipped,
35
+ * as is anything inside `<defs>` (not rendered, so there is nothing to measure)
36
+ * and any id already taken (first occurrence wins). Bare `id`s alone do not
37
+ * opt in — ids are routinely gradient/clip plumbing, not connection points.
38
+ */
39
+ declare const extractAnchors: SvgAnchorExtractor;
40
+
41
+ /**
42
+ * The default SVG parser behind `<SvgArtwork>` — markup string in, safe live
43
+ * root out, `null` when the input can't be rendered.
44
+ *
45
+ * The policy is strict-and-lossy: anything with scripting or
46
+ * external-reference potential is dropped, never allowlisted-by-default.
47
+ * Fidelity for exotic markup (filters, embedded rasters, cross-document
48
+ * `<use>`) is regained by swapping the parser via the `parse` prop — the trust
49
+ * decision is explicit and lives in the app, not in flags on this default.
50
+ */
51
+ /**
52
+ * The parse seam's contract: markup in, a safe, detached `<svg>` root out.
53
+ * Returning `null` refuses to render. Consumers swap in their own (DOMPurify,
54
+ * or a pass-through for self-authored markup) via `<SvgArtwork parse>`.
55
+ */
56
+ type SvgParser = (markup: string) => SVGSVGElement | null;
57
+ /**
58
+ * Parse and sanitize SVG markup. Rejects (`null`) inputs that aren't a
59
+ * well-formed SVG document; strips `<script>` and `<foreignObject>` subtrees,
60
+ * every `on*` event attribute, and any `href`/`xlink:href` that isn't a
61
+ * same-document fragment (`#…`) — which covers `javascript:` and external
62
+ * URLs alike.
63
+ */
64
+ declare const parseSvg: SvgParser;
65
+ /** An SVG document's natural size, from its attributes or `viewBox`. */
66
+ type SvgIntrinsicSize = {
67
+ width: number;
68
+ height: number;
69
+ };
70
+ /**
71
+ * Read an SVG root's intrinsic size — the `width`/`height` attributes when
72
+ * they carry positive lengths, the `viewBox` extent otherwise, `null` when
73
+ * neither does. Consumers use it for spawn sizing (how large a pasted artwork
74
+ * should drop in); `<SvgArtwork>` uses it to synthesize a missing `viewBox` so
75
+ * the artwork scales with the node box.
76
+ */
77
+ declare function readIntrinsicSize(svg: SVGSVGElement): SvgIntrinsicSize | null;
78
+
79
+ type SvgArtworkProps = {
80
+ /** Raw SVG markup — parsed and sanitized through `parse` before mounting. */
81
+ markup: string;
82
+ /**
83
+ * The parse seam: markup in, safe live root out, `null` to refuse. Defaults
84
+ * to the built-in strict-and-lossy parser; swap in DOMPurify (or a
85
+ * pass-through for markup you author yourself) to trade strictness for
86
+ * fidelity — the trust decision is yours and explicit.
87
+ */
88
+ parse?: SvgParser;
89
+ /**
90
+ * The extract seam: which elements inside the artwork become connectable
91
+ * anchors. Defaults to the attribute-driven `extractAnchors` (`data-anchor`
92
+ * opts in). Only consulted when the artwork renders inside a rivet node;
93
+ * return `[]` to render display-only.
94
+ */
95
+ extract?: SvgAnchorExtractor;
96
+ /**
97
+ * Stray-handle chrome policy for the extracted anchors. Defaults to
98
+ * `"none"` — a shape edge-connects at its boundary through its grab dots,
99
+ * and a floating perimeter handle beside artwork reads as clutter. Pass
100
+ * `"dynamic"` for the inline-text-style perimeter chrome.
101
+ */
102
+ anchorStrays?: AnchorStrayPolicy;
103
+ className?: string;
104
+ style?: CSSProperties;
105
+ };
106
+ /**
107
+ * Renders SVG markup inside a node, filling the node box. The markup is parsed
108
+ * through the `parse` seam, then the root is normalized to scale with the box:
109
+ * `width`/`height` become `100%`, a missing `viewBox` is synthesized from the
110
+ * intrinsic size, and a missing `preserveAspectRatio` defaults to
111
+ * `"xMidYMid meet"` (a value the document already carries is respected).
112
+ *
113
+ * Inside a rivet node, elements the `extract` seam selects are registered as
114
+ * anchors on that node — measured, given grab-dot chrome, connectable like any
115
+ * `<Anchor>`. Extraction re-runs when the markup changes, diffing by anchor
116
+ * id: surviving ids are rebound (geometry retained, edges stay put), ids gone
117
+ * from the new markup are unregistered — the shape was edited away for good.
118
+ * Unmounting only *detaches* (stale retention), matching `<Anchor>`. Outside a
119
+ * node — or outside `<Rivet>` entirely — the component is display-only.
120
+ *
121
+ * The wrapper ignores pointer events so a press falls through to the node
122
+ * wrapper — the whole graphic stays draggable. Nothing renders when `parse`
123
+ * refuses the markup.
124
+ */
125
+ declare function SvgArtwork({ markup, parse, extract, anchorStrays, className, style, }: SvgArtworkProps): react.JSX.Element;
126
+
127
+ export { type SvgAnchorExtractor, type SvgAnchorTarget, SvgArtwork, type SvgArtworkProps, type SvgIntrinsicSize, type SvgParser, extractAnchors, parseSvg, readIntrinsicSize };
@@ -0,0 +1,126 @@
1
+ import { RivetContext, RivetNodeContext } from '../chunk-VQYN27OK.js';
2
+ import { useRef, useContext, useLayoutEffect } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/svg/extract.ts
6
+ var extractAnchors = (root) => {
7
+ const targets = [];
8
+ const taken = /* @__PURE__ */ new Set();
9
+ for (const element of root.querySelectorAll("[data-anchor]")) {
10
+ if (element.closest("defs")) continue;
11
+ const anchorId = element.getAttribute("data-anchor")?.trim() || element.id;
12
+ if (!anchorId || taken.has(anchorId)) continue;
13
+ taken.add(anchorId);
14
+ targets.push({ anchorId, element });
15
+ }
16
+ return targets;
17
+ };
18
+
19
+ // src/svg/parse.ts
20
+ var SVG_NS = "http://www.w3.org/2000/svg";
21
+ var DENIED_ELEMENTS = "script, foreignObject";
22
+ var parseSvg = (markup) => {
23
+ if (typeof DOMParser === "undefined") return null;
24
+ const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
25
+ if (doc.querySelector("parsererror")) return null;
26
+ const root = doc.documentElement;
27
+ if (root.namespaceURI !== SVG_NS || root.localName !== "svg") return null;
28
+ const svg = root;
29
+ for (const el of svg.querySelectorAll(DENIED_ELEMENTS)) el.remove();
30
+ const strip = (el) => {
31
+ for (const attr of Array.from(el.attributes)) {
32
+ const name = attr.name.toLowerCase();
33
+ const isRef = name === "href" || name === "xlink:href";
34
+ const denied = name.startsWith("on") || isRef && !attr.value.trim().startsWith("#");
35
+ if (denied) el.removeAttribute(attr.name);
36
+ }
37
+ };
38
+ strip(svg);
39
+ for (const el of svg.querySelectorAll("*")) strip(el);
40
+ return svg;
41
+ };
42
+ function readIntrinsicSize(svg) {
43
+ const box = viewBoxNumbers(svg.getAttribute("viewBox"));
44
+ const width = parseLength(svg.getAttribute("width")) ?? box?.[2] ?? null;
45
+ const height = parseLength(svg.getAttribute("height")) ?? box?.[3] ?? null;
46
+ if (!width || !height || width <= 0 || height <= 0) return null;
47
+ return { width, height };
48
+ }
49
+ function viewBoxNumbers(viewBox) {
50
+ if (!viewBox) return null;
51
+ const parts = viewBox.split(/[\s,]+/).map(Number);
52
+ if (parts.length !== 4 || !parts.every(Number.isFinite)) return null;
53
+ return parts;
54
+ }
55
+ function parseLength(value) {
56
+ if (!value || value.includes("%")) return null;
57
+ const n = Number.parseFloat(value);
58
+ return Number.isFinite(n) ? n : null;
59
+ }
60
+ function SvgArtwork({
61
+ markup,
62
+ parse,
63
+ extract,
64
+ anchorStrays,
65
+ className,
66
+ style
67
+ }) {
68
+ const ref = useRef(null);
69
+ const rivet = useContext(RivetContext);
70
+ const node = useContext(RivetNodeContext);
71
+ const store = rivet?.store ?? null;
72
+ const nodeId = node?.nodeId;
73
+ const wired = useRef(/* @__PURE__ */ new Set());
74
+ useLayoutEffect(() => {
75
+ const container = ref.current;
76
+ if (!container) return;
77
+ const svg = (parse ?? parseSvg)(markup);
78
+ if (svg) {
79
+ fitToBox(svg);
80
+ container.replaceChildren(svg);
81
+ } else {
82
+ container.replaceChildren();
83
+ }
84
+ if (!store || nodeId === void 0) return;
85
+ const targets = svg ? (extract ?? extractAnchors)(svg) : [];
86
+ const next = /* @__PURE__ */ new Set();
87
+ for (const target of targets) {
88
+ next.add(target.anchorId);
89
+ store.registerAnchor(nodeId, target.anchorId, target.element, {
90
+ color: target.color,
91
+ strays: anchorStrays ?? "none"
92
+ });
93
+ }
94
+ for (const id of wired.current) {
95
+ if (!next.has(id)) store.unregisterAnchor(nodeId, id);
96
+ }
97
+ wired.current = next;
98
+ return () => {
99
+ for (const id of wired.current) store.detachAnchor(nodeId, id);
100
+ };
101
+ }, [markup, parse, extract, anchorStrays, store, nodeId]);
102
+ return /* @__PURE__ */ jsx(
103
+ "div",
104
+ {
105
+ ref,
106
+ "data-rivet-svg-artwork": "",
107
+ className,
108
+ style: { width: "100%", height: "100%", pointerEvents: "none", ...style }
109
+ }
110
+ );
111
+ }
112
+ function fitToBox(svg) {
113
+ if (!svg.getAttribute("viewBox")) {
114
+ const size = readIntrinsicSize(svg);
115
+ if (size) svg.setAttribute("viewBox", `0 0 ${size.width} ${size.height}`);
116
+ }
117
+ svg.setAttribute("width", "100%");
118
+ svg.setAttribute("height", "100%");
119
+ if (!svg.getAttribute("preserveAspectRatio"))
120
+ svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
121
+ svg.style.display = "block";
122
+ }
123
+
124
+ export { SvgArtwork, extractAnchors, parseSvg, readIntrinsicSize };
125
+ //# sourceMappingURL=index.js.map
126
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/svg/extract.ts","../../src/svg/parse.ts","../../src/svg/svg-artwork.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The extract seam behind `<SvgArtwork>` — which elements inside a parsed\n * artwork become connectable anchors. The default is attribute-driven and\n * convention-free: markup opts elements in with `data-anchor`. Consumers whose\n * artwork follows other conventions (Figma layer names serialized as `id`s, a\n * tool-specific class scheme) swap the extractor via the `extract` prop —\n * granularity lives here, never in core heuristics.\n */\n\n/** An element inside the artwork to expose as a connectable anchor. */\nexport type SvgAnchorTarget = {\n /**\n * Identifies the anchor within its node. Persisted edge handle ids derive\n * from it, so it must be stable across parses of the same markup.\n */\n anchorId: string\n /** The live element inside the mounted artwork the anchor binds to. */\n element: Element\n /** Accent color for the anchor's grab dots. */\n color?: string\n}\n\n/**\n * The extract seam's contract: the mounted, sanitized `<svg>` root in, the\n * anchor targets out. Runs after `parse`, re-runs when the markup changes.\n */\nexport type SvgAnchorExtractor = (root: SVGSVGElement) => SvgAnchorTarget[]\n\n/**\n * The default extractor: elements carrying a `data-anchor` attribute become\n * anchors, in document order. The attribute's value is the anchor id; an empty\n * value falls back to the element's `id`. Elements with neither are skipped,\n * as is anything inside `<defs>` (not rendered, so there is nothing to measure)\n * and any id already taken (first occurrence wins). Bare `id`s alone do not\n * opt in — ids are routinely gradient/clip plumbing, not connection points.\n */\nexport const extractAnchors: SvgAnchorExtractor = (root) => {\n const targets: SvgAnchorTarget[] = []\n const taken = new Set<string>()\n for (const element of root.querySelectorAll(\"[data-anchor]\")) {\n if (element.closest(\"defs\")) continue\n const anchorId = element.getAttribute(\"data-anchor\")?.trim() || element.id\n if (!anchorId || taken.has(anchorId)) continue\n taken.add(anchorId)\n targets.push({ anchorId, element })\n }\n return targets\n}\n","/**\n * The default SVG parser behind `<SvgArtwork>` — markup string in, safe live\n * root out, `null` when the input can't be rendered.\n *\n * The policy is strict-and-lossy: anything with scripting or\n * external-reference potential is dropped, never allowlisted-by-default.\n * Fidelity for exotic markup (filters, embedded rasters, cross-document\n * `<use>`) is regained by swapping the parser via the `parse` prop — the trust\n * decision is explicit and lives in the app, not in flags on this default.\n */\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\"\n\n/**\n * The parse seam's contract: markup in, a safe, detached `<svg>` root out.\n * Returning `null` refuses to render. Consumers swap in their own (DOMPurify,\n * or a pass-through for self-authored markup) via `<SvgArtwork parse>`.\n */\nexport type SvgParser = (markup: string) => SVGSVGElement | null\n\n/** Elements with scripting or arbitrary-content potential, dropped entirely. */\nconst DENIED_ELEMENTS = \"script, foreignObject\"\n\n/**\n * Parse and sanitize SVG markup. Rejects (`null`) inputs that aren't a\n * well-formed SVG document; strips `<script>` and `<foreignObject>` subtrees,\n * every `on*` event attribute, and any `href`/`xlink:href` that isn't a\n * same-document fragment (`#…`) — which covers `javascript:` and external\n * URLs alike.\n */\nexport const parseSvg: SvgParser = (markup) => {\n if (typeof DOMParser === \"undefined\") return null\n const doc = new DOMParser().parseFromString(markup, \"image/svg+xml\")\n if (doc.querySelector(\"parsererror\")) return null\n const root = doc.documentElement\n if (root.namespaceURI !== SVG_NS || root.localName !== \"svg\") return null\n const svg = root as unknown as SVGSVGElement\n\n for (const el of svg.querySelectorAll(DENIED_ELEMENTS)) el.remove()\n\n const strip = (el: Element) => {\n for (const attr of Array.from(el.attributes)) {\n const name = attr.name.toLowerCase()\n const isRef = name === \"href\" || name === \"xlink:href\"\n const denied = name.startsWith(\"on\") || (isRef && !attr.value.trim().startsWith(\"#\"))\n if (denied) el.removeAttribute(attr.name)\n }\n }\n strip(svg)\n for (const el of svg.querySelectorAll(\"*\")) strip(el)\n\n return svg\n}\n\n/** An SVG document's natural size, from its attributes or `viewBox`. */\nexport type SvgIntrinsicSize = { width: number; height: number }\n\n/**\n * Read an SVG root's intrinsic size — the `width`/`height` attributes when\n * they carry positive lengths, the `viewBox` extent otherwise, `null` when\n * neither does. Consumers use it for spawn sizing (how large a pasted artwork\n * should drop in); `<SvgArtwork>` uses it to synthesize a missing `viewBox` so\n * the artwork scales with the node box.\n */\nexport function readIntrinsicSize(svg: SVGSVGElement): SvgIntrinsicSize | null {\n const box = viewBoxNumbers(svg.getAttribute(\"viewBox\"))\n const width = parseLength(svg.getAttribute(\"width\")) ?? box?.[2] ?? null\n const height = parseLength(svg.getAttribute(\"height\")) ?? box?.[3] ?? null\n if (!width || !height || width <= 0 || height <= 0) return null\n return { width, height }\n}\n\n/** Parse a `viewBox` to its four numbers, or `null` if it isn't well-formed. */\nfunction viewBoxNumbers(viewBox: string | null): [number, number, number, number] | null {\n if (!viewBox) return null\n const parts = viewBox.split(/[\\s,]+/).map(Number)\n if (parts.length !== 4 || !parts.every(Number.isFinite)) return null\n return parts as [number, number, number, number]\n}\n\n/** Parse a length attribute (`\"240\"`, `\"240px\"`) to a number, or `null`. Relative lengths (`\"100%\"`) aren't sizes. */\nfunction parseLength(value: string | null): number | null {\n if (!value || value.includes(\"%\")) return null\n const n = Number.parseFloat(value)\n return Number.isFinite(n) ? n : null\n}\n","import { type CSSProperties, useContext, useLayoutEffect, useRef } from \"react\"\nimport type { AnchorStrayPolicy } from \"../anchor/registry\"\nimport { RivetContext, RivetNodeContext } from \"../context\"\nimport { extractAnchors, type SvgAnchorExtractor } from \"./extract\"\nimport { parseSvg, readIntrinsicSize, type SvgParser } from \"./parse\"\n\nexport type SvgArtworkProps = {\n /** Raw SVG markup — parsed and sanitized through `parse` before mounting. */\n markup: string\n /**\n * The parse seam: markup in, safe live root out, `null` to refuse. Defaults\n * to the built-in strict-and-lossy parser; swap in DOMPurify (or a\n * pass-through for markup you author yourself) to trade strictness for\n * fidelity — the trust decision is yours and explicit.\n */\n parse?: SvgParser\n /**\n * The extract seam: which elements inside the artwork become connectable\n * anchors. Defaults to the attribute-driven `extractAnchors` (`data-anchor`\n * opts in). Only consulted when the artwork renders inside a rivet node;\n * return `[]` to render display-only.\n */\n extract?: SvgAnchorExtractor\n /**\n * Stray-handle chrome policy for the extracted anchors. Defaults to\n * `\"none\"` — a shape edge-connects at its boundary through its grab dots,\n * and a floating perimeter handle beside artwork reads as clutter. Pass\n * `\"dynamic\"` for the inline-text-style perimeter chrome.\n */\n anchorStrays?: AnchorStrayPolicy\n className?: string\n style?: CSSProperties\n}\n\n/**\n * Renders SVG markup inside a node, filling the node box. The markup is parsed\n * through the `parse` seam, then the root is normalized to scale with the box:\n * `width`/`height` become `100%`, a missing `viewBox` is synthesized from the\n * intrinsic size, and a missing `preserveAspectRatio` defaults to\n * `\"xMidYMid meet\"` (a value the document already carries is respected).\n *\n * Inside a rivet node, elements the `extract` seam selects are registered as\n * anchors on that node — measured, given grab-dot chrome, connectable like any\n * `<Anchor>`. Extraction re-runs when the markup changes, diffing by anchor\n * id: surviving ids are rebound (geometry retained, edges stay put), ids gone\n * from the new markup are unregistered — the shape was edited away for good.\n * Unmounting only *detaches* (stale retention), matching `<Anchor>`. Outside a\n * node — or outside `<Rivet>` entirely — the component is display-only.\n *\n * The wrapper ignores pointer events so a press falls through to the node\n * wrapper — the whole graphic stays draggable. Nothing renders when `parse`\n * refuses the markup.\n */\nexport function SvgArtwork({\n markup,\n parse,\n extract,\n anchorStrays,\n className,\n style,\n}: SvgArtworkProps) {\n const ref = useRef<HTMLDivElement>(null)\n // Read, not required: outside <Rivet> or outside a node the artwork still\n // renders, it just exposes no anchors.\n const rivet = useContext(RivetContext)\n const node = useContext(RivetNodeContext)\n const store = rivet?.store ?? null\n const nodeId = node?.nodeId\n /** Anchor ids currently registered from this artwork. */\n const wired = useRef(new Set<string>())\n\n useLayoutEffect(() => {\n const container = ref.current\n if (!container) return\n const svg = (parse ?? parseSvg)(markup)\n if (svg) {\n fitToBox(svg)\n container.replaceChildren(svg)\n } else {\n container.replaceChildren()\n }\n\n if (!store || nodeId === undefined) return\n // Refused markup extracts nothing, so its anchors unregister below — the\n // same fate as ids missing from a successful re-parse.\n const targets = svg ? (extract ?? extractAnchors)(svg) : []\n const next = new Set<string>()\n for (const target of targets) {\n next.add(target.anchorId)\n store.registerAnchor(nodeId, target.anchorId, target.element, {\n color: target.color,\n strays: anchorStrays ?? \"none\",\n })\n }\n for (const id of wired.current) {\n if (!next.has(id)) store.unregisterAnchor(nodeId, id)\n }\n wired.current = next\n return () => {\n for (const id of wired.current) store.detachAnchor(nodeId, id)\n }\n }, [markup, parse, extract, anchorStrays, store, nodeId])\n\n return (\n <div\n ref={ref}\n data-rivet-svg-artwork=\"\"\n className={className}\n style={{ width: \"100%\", height: \"100%\", pointerEvents: \"none\", ...style }}\n />\n )\n}\n\n/** Make the root scale with its container instead of its authored size. */\nfunction fitToBox(svg: SVGSVGElement): void {\n if (!svg.getAttribute(\"viewBox\")) {\n const size = readIntrinsicSize(svg)\n if (size) svg.setAttribute(\"viewBox\", `0 0 ${size.width} ${size.height}`)\n }\n svg.setAttribute(\"width\", \"100%\")\n svg.setAttribute(\"height\", \"100%\")\n if (!svg.getAttribute(\"preserveAspectRatio\"))\n svg.setAttribute(\"preserveAspectRatio\", \"xMidYMid meet\")\n svg.style.display = \"block\"\n}\n"]}
@@ -0,0 +1,96 @@
1
+ import { R as Rect, c as RivetNode, V as Vec2, S as Size, k as SwimlaneMargin, j as SwimlaneGroup } from '../types-B8AAJ60T.js';
2
+ export { L as LaneChange, M as Swimlane, l as SwimlaneHeaderProps, m as SwimlaneLabelProps, n as SwimlaneSizeChange } from '../types-B8AAJ60T.js';
3
+ import 'react';
4
+
5
+ /** World height of a group's sticky header band. */
6
+ declare const SWIMLANE_GROUP_HEADER_SIZE = 32;
7
+ /** Screen width of the sticky vertical lane-label strip, in px. */
8
+ declare const SWIMLANE_LABEL_WIDTH = 24;
9
+ /** Floor for a lane's height when resized, in world units. */
10
+ declare const SWIMLANE_MIN_HEIGHT = 80;
11
+ /** A margin with every side filled in. */
12
+ type ResolvedMargin = {
13
+ top: number;
14
+ right: number;
15
+ bottom: number;
16
+ left: number;
17
+ };
18
+ /** Normalize a `swimlaneMargin` prop (number shorthand or per-side) to all sides. */
19
+ declare function resolveMargin(margin?: number | SwimlaneMargin): ResolvedMargin;
20
+ /** A lane resolved to world-space geometry (its body, below the group header). */
21
+ type ResolvedLane = {
22
+ id: string;
23
+ groupId: string;
24
+ label?: string;
25
+ color?: string;
26
+ /** World-space top edge of the lane body. */
27
+ top: number;
28
+ height: number;
29
+ /** World x origin and nominal width (bands render effectively infinite). */
30
+ x: number;
31
+ width: number;
32
+ };
33
+ /** A group resolved to world-space geometry: its header band plus its lanes. */
34
+ type ResolvedSwimlaneGroup = {
35
+ id: string;
36
+ label?: string;
37
+ color?: string;
38
+ x: number;
39
+ width: number;
40
+ /** World top edge of the header band. */
41
+ top: number;
42
+ /** Header band height in world units. */
43
+ headerSize: number;
44
+ /** World bottom edge of the whole group (its last lane's bottom). */
45
+ bottom: number;
46
+ lanes: ResolvedLane[];
47
+ };
48
+ /** A resolved lane plus how much of it is currently on screen. */
49
+ type VisibleLane = ResolvedLane & {
50
+ /** Area (world units²) of the lane's overlap with the viewport. */
51
+ visibleArea: number;
52
+ };
53
+ /**
54
+ * A snapshot of what the pane is currently showing, produced by
55
+ * `getViewportElements`. Read imperatively — it never subscribes to the
56
+ * viewport, so calling it does not re-render.
57
+ */
58
+ type ViewportElements<TNodeData = unknown> = {
59
+ /** The world-space rect currently visible in the pane. */
60
+ rect: Rect;
61
+ /** Nodes whose world box intersects the viewport. */
62
+ nodes: RivetNode<TNodeData>[];
63
+ /** Visible lanes, largest on-screen area first. `lanes[0]` is the dominant lane. */
64
+ lanes: VisibleLane[];
65
+ };
66
+ /**
67
+ * Return `color` as an `rgba()` with the given alpha. Hex (`#rgb`/`#rrggbb`) is
68
+ * converted; any other form (already-rgba, named) is returned unchanged. Used so
69
+ * a solid accent color renders as a subtle tint, never an opaque wash.
70
+ */
71
+ declare function withAlpha(color: string, alpha: number): string;
72
+ /** Lay a group out: a header band at `position.y`, then its lanes stacked below. */
73
+ declare function resolveSwimlaneGroup(group: SwimlaneGroup, headerSize?: number): ResolvedSwimlaneGroup;
74
+ /** Resolve every group in a `swimlane` prop to world-space geometry. */
75
+ declare function resolveSwimlanes(groups: SwimlaneGroup[], headerSize?: number): ResolvedSwimlaneGroup[];
76
+ /** Flatten every group's lanes into a single list. */
77
+ declare function flattenLanes(groups: ResolvedSwimlaneGroup[]): ResolvedLane[];
78
+ /**
79
+ * The lane a node box `[top, top+height]` belongs to: the one it overlaps most
80
+ * (ties prefer `preferredLaneId`), or — with no overlap at all — the nearest by
81
+ * center. Returns null only when there are no lanes.
82
+ */
83
+ declare function findLaneByCoverage(lanes: ResolvedLane[], top: number, height: number, preferredLaneId?: string): ResolvedLane | null;
84
+ type LaneClampResult = {
85
+ position: Vec2;
86
+ laneId: string | null;
87
+ };
88
+ /**
89
+ * Clamp a node so its whole body sits inside one lane, honoring `margin` (and
90
+ * the left label strip). The target lane is chosen by {@link findLaneByCoverage}
91
+ * — pass `preferredLaneId` to bias toward the node's current lane. Horizontally
92
+ * only the left edge is bounded, since bands are effectively infinite.
93
+ */
94
+ declare function clampNodeToLanes(position: Vec2, size: Size, lanes: ResolvedLane[], margin: ResolvedMargin, labelWidth: number, preferredLaneId?: string): LaneClampResult;
95
+
96
+ export { type LaneClampResult, type ResolvedLane, type ResolvedMargin, type ResolvedSwimlaneGroup, SWIMLANE_GROUP_HEADER_SIZE, SWIMLANE_LABEL_WIDTH, SWIMLANE_MIN_HEIGHT, SwimlaneGroup, SwimlaneMargin, type ViewportElements, type VisibleLane, clampNodeToLanes, findLaneByCoverage, flattenLanes, resolveMargin, resolveSwimlaneGroup, resolveSwimlanes, withAlpha };
@@ -0,0 +1,3 @@
1
+ export { SWIMLANE_GROUP_HEADER_SIZE, SWIMLANE_LABEL_WIDTH, SWIMLANE_MIN_HEIGHT, clampNodeToLanes, findLaneByCoverage, flattenLanes, resolveMargin, resolveSwimlaneGroup, resolveSwimlanes, withAlpha } from '../chunk-6XRQSAQT.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}