@floegence/floe-webapp-core 0.82.0 → 0.83.1

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,35 @@
1
+ function c(t) {
2
+ return t.map((h, a) => `${a ? "L" : "M"}${h.x},${h.y}`).join(" ");
3
+ }
4
+ function o(t, h, a = 48) {
5
+ const i = Math.max(
6
+ 0.1,
7
+ Math.min(
8
+ 1,
9
+ (h.width - a * 2) / Math.max(1, t.width),
10
+ (h.height - a * 2) / Math.max(1, t.height)
11
+ )
12
+ );
13
+ return {
14
+ x: (h.width - t.width * i) / 2 - t.x * i,
15
+ y: (h.height - t.height * i) / 2 - t.y * i,
16
+ scale: i
17
+ };
18
+ }
19
+ function f(t, h) {
20
+ const a = /* @__PURE__ */ new Set([h]), i = /* @__PURE__ */ new Map();
21
+ for (const e of t.nodes)
22
+ e.parentId && i.set(e.parentId, [...i.get(e.parentId) ?? [], e.id]);
23
+ const n = (e) => {
24
+ for (const r of i.get(e) ?? [])
25
+ a.has(r) || (a.add(r), n(r));
26
+ };
27
+ return n(h), new Set(
28
+ t.edges.filter((e) => a.has(e.source) || a.has(e.target)).map((e) => e.id)
29
+ );
30
+ }
31
+ export {
32
+ o as fitGraphViewport,
33
+ f as graphRelatedEdges,
34
+ c as graphSectionPath
35
+ };
@@ -0,0 +1,5 @@
1
+ import type { ELK } from 'elkjs/lib/elk-api';
2
+ import type { GraphInput, GraphLayout, GraphLayoutOptions } from './types';
3
+ export declare function validateGraphInput(input: GraphInput): void;
4
+ /** Normalizes geometry around the supplied ELK engine; browser layout runs in its worker. */
5
+ export declare function computeGraphLayout(input: GraphInput, options: GraphLayoutOptions, engine: Pick<ELK, 'layout'>): Promise<GraphLayout>;
@@ -0,0 +1,121 @@
1
+ function k(n) {
2
+ const i = /* @__PURE__ */ new Set(), p = (e) => {
3
+ if (!e || i.has(e)) throw new Error(`Duplicate or empty graph ID: ${e}`);
4
+ i.add(e);
5
+ }, s = new Map(n.nodes.map((e) => [e.id, e]));
6
+ for (const e of n.nodes) {
7
+ if (p(e.id), ![e.width, e.height].every((r) => Number.isFinite(r) && r > 0))
8
+ throw new Error(`Invalid dimensions for graph node: ${e.id}`);
9
+ for (const r of e.ports ?? []) p(r.id);
10
+ const a = /* @__PURE__ */ new Set([e.id]);
11
+ let d = e.parentId;
12
+ for (; d !== void 0; ) {
13
+ const r = s.get(d);
14
+ if (!r || r.kind !== "group")
15
+ throw new Error(`Invalid parent for graph node: ${e.id}`);
16
+ if (a.has(d)) throw new Error(`Cyclic graph containment: ${e.id}`);
17
+ a.add(d), d = r.parentId;
18
+ }
19
+ }
20
+ for (const e of n.edges) {
21
+ p(e.id);
22
+ for (const [a, d] of [
23
+ [e.source, e.sourcePort],
24
+ [e.target, e.targetPort]
25
+ ]) {
26
+ const r = s.get(a);
27
+ if (!r || d !== void 0 && !r.ports?.some((c) => c.id === d))
28
+ throw new Error(`Invalid endpoint for graph edge: ${e.id}`);
29
+ }
30
+ }
31
+ }
32
+ async function N(n, i, p) {
33
+ k(n);
34
+ const s = i.groupPadding ?? { top: 64, right: 28, bottom: 28, left: 28 };
35
+ if (![...Object.values(s), i.spacing ?? 64].every((t) => Number.isFinite(t) && t >= 0))
36
+ throw new Error("Graph layout spacing must be finite and nonnegative");
37
+ const e = /* @__PURE__ */ new Map();
38
+ for (const t of n.nodes)
39
+ e.set(t.id, {
40
+ id: t.id,
41
+ width: t.width,
42
+ height: t.height,
43
+ ...t.kind === "group" ? { children: [] } : {},
44
+ ports: t.ports?.map((o) => ({
45
+ id: o.id,
46
+ width: 1,
47
+ height: 1,
48
+ layoutOptions: { "elk.port.side": o.side }
49
+ })),
50
+ layoutOptions: {
51
+ "elk.portConstraints": "FIXED_ORDER",
52
+ "elk.padding": `[top=${s.top},left=${s.left},bottom=${s.bottom},right=${s.right}]`
53
+ }
54
+ });
55
+ const a = [];
56
+ for (const t of n.nodes)
57
+ (t.parentId ? e.get(t.parentId).children : a).push(e.get(t.id));
58
+ const d = /* @__PURE__ */ new Set([
59
+ ...n.nodes.flatMap((t) => [t.id, ...(t.ports ?? []).map((o) => o.id)]),
60
+ ...n.edges.map((t) => t.id)
61
+ ]);
62
+ let r = "$graph";
63
+ for (; d.has(r); ) r += "$";
64
+ const c = await p.layout({
65
+ id: r,
66
+ children: a,
67
+ edges: n.edges.map((t) => ({
68
+ id: t.id,
69
+ sources: [t.sourcePort ?? t.source],
70
+ targets: [t.targetPort ?? t.target]
71
+ })),
72
+ layoutOptions: {
73
+ "elk.algorithm": "layered",
74
+ "elk.direction": i.direction ?? "RIGHT",
75
+ "elk.edgeRouting": "ORTHOGONAL",
76
+ "elk.hierarchyHandling": "INCLUDE_CHILDREN",
77
+ "elk.spacing.nodeNode": String(i.spacing ?? 64),
78
+ "elk.layered.spacing.nodeNodeBetweenLayers": String(i.spacing ?? 64),
79
+ "elk.layered.considerModelOrder.strategy": "NODES_AND_EDGES",
80
+ "elk.randomSeed": "1"
81
+ }
82
+ }), f = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), I = (t) => {
83
+ for (const o of t.edges ?? []) {
84
+ const h = o.container ?? t.id, l = f.get(h) ?? { x: 0, y: 0 };
85
+ x.set(
86
+ o.id,
87
+ (o.sections ?? []).map(
88
+ (g) => [g.startPoint, ...g.bendPoints ?? [], g.endPoint].map((w) => ({
89
+ x: w.x + l.x,
90
+ y: w.y + l.y
91
+ }))
92
+ )
93
+ );
94
+ }
95
+ for (const o of t.children ?? []) I(o);
96
+ }, E = (t, o, h) => {
97
+ const l = o + (t.x ?? 0), g = h + (t.y ?? 0);
98
+ f.set(t.id, { x: l, y: g, width: t.width ?? 0, height: t.height ?? 0 });
99
+ for (const w of t.children ?? []) E(w, l, g);
100
+ };
101
+ for (const t of c.children ?? []) E(t, 0, 0);
102
+ I(c);
103
+ const y = i.anchor && f.get(i.anchor.nodeId), u = y ? i.anchor.position.x - y.x : 0, m = y ? i.anchor.position.y - y.y : 0;
104
+ return {
105
+ nodes: n.nodes.map((t) => {
106
+ const o = f.get(t.id);
107
+ return { ...t, ...o, x: o.x + u, y: o.y + m };
108
+ }),
109
+ edges: n.edges.map((t) => ({
110
+ ...t,
111
+ sections: (x.get(t.id) ?? []).map(
112
+ (o) => o.map((h) => ({ x: h.x + u, y: h.y + m }))
113
+ )
114
+ })),
115
+ bounds: { x: u, y: m, width: c.width ?? 0, height: c.height ?? 0 }
116
+ };
117
+ }
118
+ export {
119
+ N as computeGraphLayout,
120
+ k as validateGraphInput
121
+ };
@@ -0,0 +1,72 @@
1
+ import type { InfiniteCanvasPoint } from '../ui/InfiniteCanvas';
2
+ export interface GraphPoint {
3
+ x: number;
4
+ y: number;
5
+ }
6
+ export interface GraphBounds extends GraphPoint {
7
+ width: number;
8
+ height: number;
9
+ }
10
+ export type GraphViewport = InfiniteCanvasPoint;
11
+ /** Port IDs, node IDs, and edge IDs share one namespace. */
12
+ export interface GraphPort {
13
+ id: string;
14
+ side: 'NORTH' | 'EAST' | 'SOUTH' | 'WEST';
15
+ }
16
+ export interface GraphNode {
17
+ id: string;
18
+ label: string;
19
+ kind?: 'node' | 'group';
20
+ parentId?: string;
21
+ width: number;
22
+ height: number;
23
+ ports?: readonly GraphPort[];
24
+ }
25
+ export interface GraphEdge {
26
+ id: string;
27
+ label: string;
28
+ source: string;
29
+ target: string;
30
+ sourcePort?: string;
31
+ targetPort?: string;
32
+ }
33
+ export interface GraphInput {
34
+ nodes: readonly GraphNode[];
35
+ edges: readonly GraphEdge[];
36
+ }
37
+ export interface GraphLayoutNode extends GraphNode, GraphBounds {
38
+ }
39
+ export interface GraphLayoutEdge extends GraphEdge {
40
+ /** Separate sections preserve branches and self loops without inventing joins. */
41
+ sections: readonly (readonly GraphPoint[])[];
42
+ }
43
+ export interface GraphLayout {
44
+ nodes: readonly GraphLayoutNode[];
45
+ edges: readonly GraphLayoutEdge[];
46
+ bounds: GraphBounds;
47
+ }
48
+ export interface GraphLayoutOptions {
49
+ direction?: 'RIGHT' | 'DOWN' | 'LEFT' | 'UP';
50
+ spacing?: number;
51
+ groupPadding?: {
52
+ top: number;
53
+ right: number;
54
+ bottom: number;
55
+ left: number;
56
+ };
57
+ /** Keep the object under inspection at its previous world position after layout. */
58
+ anchor?: {
59
+ nodeId: string;
60
+ position: GraphPoint;
61
+ };
62
+ }
63
+ export type GraphObjectRef = {
64
+ kind: 'node' | 'edge';
65
+ id: string;
66
+ };
67
+ /** Client coordinates go directly to SurfaceFloatingLayer; never rescale them. */
68
+ export interface GraphObjectEvent {
69
+ object: GraphObjectRef | null;
70
+ position: GraphPoint;
71
+ owner: Element;
72
+ }
@@ -14,7 +14,7 @@ export interface InfiniteCanvasContextMenuEvent {
14
14
  worldY: number;
15
15
  }
16
16
  export interface InfiniteCanvasProps {
17
- children: JSX.Element;
17
+ children: JSX.Element | ((viewport: Accessor<InfiniteCanvasPoint>) => JSX.Element);
18
18
  overlay?: (viewport: Accessor<InfiniteCanvasPoint>) => JSX.Element;
19
19
  viewport: InfiniteCanvasPoint;
20
20
  onViewportChange?: (viewport: InfiniteCanvasPoint) => void;
@@ -1,38 +1,41 @@
1
- import { spread as B, mergeProps as j, insert as T, effect as q, className as G, setStyleProperty as J, template as K, use as Q } from "solid-js/web";
2
- import { createSignal as b, untrack as x, createEffect as A, onCleanup as D } from "solid-js";
3
- import { cn as I } from "../../utils/cn.js";
4
- import { startHotInteraction as y } from "../../utils/hotInteraction.js";
5
- import { resolveSurfaceInteractionTargetRole as ee, resolveSurfaceWheelRouting as te, DEFAULT_CANVAS_WHEEL_INTERACTIVE_SELECTOR as ne } from "./localInteractionSurface.js";
6
- import { startPointerSession as re } from "./pointerSession.js";
7
- import { clientToCanvasLocal as R, localToCanvasWorld as oe, createViewportFromZoomAnchor as ae } from "./canvasGeometry.js";
8
- import { SURFACE_PORTAL_LAYER_ATTR as ie } from "./dialogSurfaceScope.js";
9
- var ce = /* @__PURE__ */ K('<div><div style="transform-origin:0 0">');
10
- const le = 1, se = 0.45, ue = 2.2, de = 14e-4, fe = '[data-floe-canvas-pan-surface="true"]', Y = 3;
11
- function me(t, s, c) {
12
- return Math.max(s, Math.min(c, t));
1
+ import { spread as j, mergeProps as q, insert as T, effect as G, className as J, setStyleProperty as K, template as Q, use as ee } from "solid-js/web";
2
+ import { createSignal as b, untrack as x, createMemo as te, createEffect as A, onCleanup as D } from "solid-js";
3
+ import { cn as y } from "../../utils/cn.js";
4
+ import { startHotInteraction as I } from "../../utils/hotInteraction.js";
5
+ import { resolveSurfaceInteractionTargetRole as ne, resolveSurfaceWheelRouting as re, DEFAULT_CANVAS_WHEEL_INTERACTIVE_SELECTOR as oe } from "./localInteractionSurface.js";
6
+ import { startPointerSession as ae } from "./pointerSession.js";
7
+ import { clientToCanvasLocal as R, localToCanvasWorld as ie, createViewportFromZoomAnchor as ce } from "./canvasGeometry.js";
8
+ import { SURFACE_PORTAL_LAYER_ATTR as le } from "./dialogSurfaceScope.js";
9
+ var se = /* @__PURE__ */ Q('<div><div style="transform-origin:0 0">');
10
+ const ue = 1, de = 0.45, fe = 2.2, me = 14e-4, we = '[data-floe-canvas-pan-surface="true"]', Y = 3;
11
+ function Se(t, s, i) {
12
+ return Math.max(s, Math.min(i, t));
13
13
  }
14
14
  function k(t) {
15
15
  return {
16
16
  x: Number.isFinite(t.x) ? t.x : 0,
17
17
  y: Number.isFinite(t.y) ? t.y : 0,
18
- scale: Number.isFinite(t.scale) && t.scale > 0 ? t.scale : le
18
+ scale: Number.isFinite(t.scale) && t.scale > 0 ? t.scale : ue
19
19
  };
20
20
  }
21
- function we(t, s) {
21
+ function ve(t, s) {
22
22
  return t.deltaMode === 1 ? t.deltaY * 16 : t.deltaMode === 2 ? t.deltaY * (s?.clientHeight ?? window.innerHeight) : t.deltaY;
23
23
  }
24
- function Le(t) {
24
+ function Te(t) {
25
25
  let s = !1;
26
- const [c, S] = b(x(() => k(t.viewport))), [w, v] = b(null);
26
+ const [i, S] = b(x(() => k(t.viewport))), [w, v] = b(null), M = te(() => {
27
+ const e = t.children;
28
+ return typeof e == "function" ? e(i) : e;
29
+ });
27
30
  let l, d, f, m = !1, u;
28
- const M = () => t.interactiveSelector ?? '[data-floe-canvas-interactive="true"]', X = () => t.panSurfaceSelector ?? fe, F = () => t.wheelInteractiveSelector ?? ne, V = () => t.minScale ?? se, N = () => t.maxScale ?? ue, H = () => t.wheelZoomSpeed ?? de, U = () => {
31
+ const X = () => t.interactiveSelector ?? '[data-floe-canvas-interactive="true"]', F = () => t.panSurfaceSelector ?? we, V = () => t.wheelInteractiveSelector ?? oe, N = () => t.minScale ?? de, H = () => t.maxScale ?? fe, U = () => t.wheelZoomSpeed ?? me, W = () => {
29
32
  const e = w();
30
33
  return e ? e.startedFromPanSurface ? e.moved : !0 : !1;
31
34
  }, p = () => {
32
35
  f !== void 0 && (window.clearTimeout(f), f = void 0);
33
36
  }, g = () => {
34
37
  m = !1, u !== void 0 && (window.clearTimeout(u), u = void 0);
35
- }, W = () => {
38
+ }, Z = () => {
36
39
  if (typeof window > "u") {
37
40
  m = !1;
38
41
  return;
@@ -42,7 +45,7 @@ function Le(t) {
42
45
  }, 0);
43
46
  }, E = (e) => {
44
47
  x(() => t.onViewportChange?.(e));
45
- }, Z = (e) => {
48
+ }, O = (e) => {
46
49
  if (typeof window > "u") {
47
50
  E(e);
48
51
  return;
@@ -50,16 +53,16 @@ function Le(t) {
50
53
  p(), f = window.setTimeout(() => {
51
54
  f = void 0, E(e);
52
55
  }, 90);
53
- }, C = (e) => (t.resolveTargetRole ?? ee)({
56
+ }, C = (e) => (t.resolveTargetRole ?? ne)({
54
57
  target: e,
55
- interactiveSelector: M(),
56
- panSurfaceSelector: X()
57
- }), O = () => {
58
+ interactiveSelector: X(),
59
+ panSurfaceSelector: F()
60
+ }), $ = () => {
58
61
  const e = w();
59
62
  if (!e) return;
60
63
  e.stopInteraction?.();
61
- const n = c();
62
- v(null), d = void 0, e.startedFromPanSurface && e.moved && (m = !0, W()), E(n);
64
+ const n = i();
65
+ v(null), d = void 0, e.startedFromPanSurface && e.moved && (m = !0, Z()), E(n);
63
66
  };
64
67
  A(() => {
65
68
  w() || S(k(t.viewport));
@@ -74,13 +77,13 @@ function Le(t) {
74
77
  s = !1;
75
78
  };
76
79
  document.addEventListener("keydown", a), document.addEventListener("keyup", r), window.addEventListener("blur", r);
77
- const i = (o) => {
80
+ const c = (o) => {
78
81
  (o.button === 1 || o.button === 0 && s) && h(o);
79
82
  };
80
- e.addEventListener("pointerdown", i, !0), e.addEventListener("click", n, !0), e.addEventListener("wheel", P, {
83
+ e.addEventListener("pointerdown", c, !0), e.addEventListener("click", n, !0), e.addEventListener("wheel", P, {
81
84
  passive: !1
82
85
  }), D(() => {
83
- document.removeEventListener("keydown", a), document.removeEventListener("keyup", r), window.removeEventListener("blur", r), e.removeEventListener("pointerdown", i, !0), e.removeEventListener("click", n, !0), e.removeEventListener("wheel", P);
86
+ document.removeEventListener("keydown", a), document.removeEventListener("keyup", r), window.removeEventListener("blur", r), e.removeEventListener("pointerdown", c, !0), e.removeEventListener("click", n, !0), e.removeEventListener("wheel", P);
84
87
  });
85
88
  }), D(() => {
86
89
  p(), d?.stop({
@@ -102,23 +105,23 @@ function Le(t) {
102
105
  pointerId: e.pointerId,
103
106
  startClientX: e.clientX,
104
107
  startClientY: e.clientY,
105
- startViewport: c(),
108
+ startViewport: i(),
106
109
  moved: !1,
107
110
  startedFromPanSurface: r,
108
- stopInteraction: r ? void 0 : y({
111
+ stopInteraction: r ? void 0 : I({
109
112
  kind: "drag",
110
113
  cursor: "grabbing"
111
114
  })
112
- }), d = re({
115
+ }), d = ae({
113
116
  pointerEvent: e,
114
117
  captureEl: l ?? null,
115
118
  capturePointer: !r,
116
- onMove: $,
119
+ onMove: z,
117
120
  onEnd: () => {
118
- O();
121
+ $();
119
122
  }
120
123
  }));
121
- }, $ = (e) => {
124
+ }, z = (e) => {
122
125
  const n = w();
123
126
  if (!n || n.pointerId !== e.pointerId) return;
124
127
  const a = e.clientX - n.startClientX, r = e.clientY - n.startClientY;
@@ -132,31 +135,31 @@ function Le(t) {
132
135
  n.moved || v({
133
136
  ...n,
134
137
  moved: !0,
135
- stopInteraction: n.stopInteraction ?? y({
138
+ stopInteraction: n.stopInteraction ?? I({
136
139
  kind: "drag",
137
140
  cursor: "grabbing"
138
141
  })
139
142
  }), S(o);
140
143
  }, P = (e) => {
141
144
  const n = l?.getBoundingClientRect();
142
- if (!n || (t.resolveWheelRouting ?? te)({
145
+ if (!n || (t.resolveWheelRouting ?? re)({
143
146
  target: e.target,
144
147
  disablePanZoom: !!t.disablePanZoom,
145
- wheelInteractiveSelector: F()
148
+ wheelInteractiveSelector: V()
146
149
  }).kind !== "canvas_zoom") return;
147
150
  e.preventDefault(), t.onViewportInteractionStart?.("wheel");
148
- const r = c(), i = R(n, {
151
+ const r = i(), c = R(n, {
149
152
  clientX: e.clientX,
150
153
  clientY: e.clientY
151
- }), o = we(e, l), L = me(r.scale * Math.exp(-o * H()), V(), N());
154
+ }), o = ve(e, l), L = Se(r.scale * Math.exp(-o * U()), N(), H());
152
155
  if (Math.abs(L - r.scale) < 1e-4) return;
153
- const _ = ae({
156
+ const _ = ce({
154
157
  viewport: r,
155
- localPoint: i,
158
+ localPoint: c,
156
159
  nextScale: L
157
160
  });
158
- S(_), Z(_);
159
- }, z = (e) => {
161
+ S(_), O(_);
162
+ }, B = (e) => {
160
163
  if (C(e.target) !== "canvas") return;
161
164
  const n = l?.getBoundingClientRect();
162
165
  if (!n) return;
@@ -164,33 +167,33 @@ function Le(t) {
164
167
  const a = R(n, {
165
168
  clientX: e.clientX,
166
169
  clientY: e.clientY
167
- }), r = c(), i = oe(r, a);
170
+ }), r = i(), c = ie(r, a);
168
171
  t.onCanvasContextMenu?.({
169
172
  clientX: e.clientX,
170
173
  clientY: e.clientY,
171
174
  localX: a.localX,
172
175
  localY: a.localY,
173
- worldX: i.worldX,
174
- worldY: i.worldY
176
+ worldX: c.worldX,
177
+ worldY: c.worldY
175
178
  });
176
179
  };
177
180
  return (() => {
178
- var e = ce(), n = e.firstChild, a = l;
179
- return typeof a == "function" ? Q(a, e) : l = e, B(e, j({
181
+ var e = se(), n = e.firstChild, a = l;
182
+ return typeof a == "function" ? ee(a, e) : l = e, j(e, q({
180
183
  get class() {
181
- return I("floe-infinite-canvas", U() && "is-panning", t.disablePanZoom && "is-locked", t.class);
184
+ return y("floe-infinite-canvas", W() && "is-panning", t.disablePanZoom && "is-locked", t.class);
182
185
  }
183
186
  }, {
184
- [ie]: "true"
187
+ [le]: "true"
185
188
  }, {
186
189
  onPointerDown: h,
187
- onContextMenu: z,
190
+ onContextMenu: B,
188
191
  get "aria-label"() {
189
192
  return t.ariaLabel ?? "Infinite canvas";
190
193
  }
191
- }), !1, !0), T(n, () => t.children), T(e, () => t.overlay?.(c), null), q((r) => {
192
- var i = I("floe-infinite-canvas__viewport", t.contentClass), o = `translate(${c().x}px, ${c().y}px) scale(${c().scale})`;
193
- return i !== r.e && G(n, r.e = i), o !== r.t && J(n, "transform", r.t = o), r;
194
+ }), !1, !0), T(n, M), T(e, () => t.overlay?.(i), null), G((r) => {
195
+ var c = y("floe-infinite-canvas__viewport", t.contentClass), o = `translate(${i().x}px, ${i().y}px) scale(${i().scale})`;
196
+ return c !== r.e && J(n, r.e = c), o !== r.t && K(n, "transform", r.t = o), r;
194
197
  }, {
195
198
  e: void 0,
196
199
  t: void 0
@@ -198,5 +201,5 @@ function Le(t) {
198
201
  })();
199
202
  }
200
203
  export {
201
- Le as InfiniteCanvas
204
+ Te as InfiniteCanvas
202
205
  };
package/dist/graph.css ADDED
@@ -0,0 +1,93 @@
1
+ /* Domain colors and card content belong to the consumer. These tokens are theme-neutral. */
2
+ .floe-graph {
3
+ position: relative;
4
+ isolation: isolate;
5
+ width: 100%;
6
+ height: 100%;
7
+ min-height: 0;
8
+ overflow: hidden;
9
+ background-color: var(--graph-background, var(--background));
10
+ background-image: radial-gradient(
11
+ circle,
12
+ var(--graph-dot, color-mix(in srgb, var(--foreground) 12%, transparent)) 0.65px,
13
+ transparent 0.8px
14
+ );
15
+ background-size: 22px 22px;
16
+ color: var(--foreground);
17
+ }
18
+ .floe-graph .floe-infinite-canvas {
19
+ width: 100%;
20
+ height: 100%;
21
+ }
22
+ .floe-graph__node,
23
+ .floe-graph__group {
24
+ position: absolute;
25
+ box-sizing: border-box;
26
+ user-select: none;
27
+ }
28
+ .floe-graph__node {
29
+ z-index: 2;
30
+ }
31
+ .floe-graph__group {
32
+ z-index: 0;
33
+ }
34
+ .floe-graph__node > *,
35
+ .floe-graph__group > * {
36
+ box-sizing: border-box;
37
+ }
38
+ .floe-graph__edges {
39
+ position: absolute;
40
+ inset: 0;
41
+ overflow: visible;
42
+ z-index: 1;
43
+ pointer-events: none;
44
+ }
45
+ .floe-graph__edge {
46
+ fill: none;
47
+ stroke: var(--graph-edge, var(--muted-foreground));
48
+ stroke-width: 1.3px;
49
+ opacity: 0.32;
50
+ vector-effect: non-scaling-stroke;
51
+ stroke-linejoin: round;
52
+ }
53
+ .floe-graph__edge-target {
54
+ fill: none;
55
+ stroke: transparent;
56
+ stroke-width: 14px;
57
+ vector-effect: non-scaling-stroke;
58
+ pointer-events: stroke;
59
+ cursor: pointer;
60
+ }
61
+ .floe-graph__edges [data-emphasized='true'] .floe-graph__edge,
62
+ .floe-graph__edges [data-selected='true'] .floe-graph__edge {
63
+ stroke: var(--graph-edge-active, var(--foreground));
64
+ stroke-width: 2px;
65
+ opacity: 0.95;
66
+ }
67
+ .floe-graph__edge-target:focus-visible {
68
+ outline: none;
69
+ stroke: var(--graph-edge-active, var(--foreground));
70
+ stroke-width: 3px;
71
+ }
72
+ .floe-graph__node:focus-visible,
73
+ .floe-graph__group:focus-visible {
74
+ outline: 2px solid var(--ring);
75
+ outline-offset: 4px;
76
+ border-radius: 8px;
77
+ }
78
+ @media (forced-colors: active) {
79
+ .floe-graph {
80
+ background: Canvas;
81
+ }
82
+ .floe-graph__edge {
83
+ stroke: CanvasText;
84
+ opacity: 1;
85
+ }
86
+ .floe-graph__edges [data-emphasized='true'] .floe-graph__edge {
87
+ stroke: Highlight;
88
+ }
89
+ .floe-graph__node:focus-visible,
90
+ .floe-graph__group:focus-visible {
91
+ outline-color: Highlight;
92
+ }
93
+ }
@@ -0,0 +1,4 @@
1
+ export * from './components/graph/types';
2
+ export * from './components/graph/geometry';
3
+ export * from './components/graph/engine';
4
+ export * from './components/graph/GraphCanvas';
package/dist/graph.js ADDED
@@ -0,0 +1,10 @@
1
+ import { fitGraphViewport as e, graphRelatedEdges as t, graphSectionPath as o } from "./components/graph/geometry.js";
2
+ import { createGraphLayoutEngine as h } from "./components/graph/engine.js";
3
+ import { GraphCanvas as g } from "./components/graph/GraphCanvas.js";
4
+ export {
5
+ g as GraphCanvas,
6
+ h as createGraphLayoutEngine,
7
+ e as fitGraphViewport,
8
+ t as graphRelatedEdges,
9
+ o as graphSectionPath
10
+ };