@cnvx/nodal 0.0.4 → 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.
package/README.md CHANGED
@@ -7,6 +7,7 @@ A Svelte 5 library for creating interactive node diagrams with customizable conn
7
7
  - Static rendering by default
8
8
  - SVG Rendering
9
9
  - Fully typed
10
+ - Lightweight and performant
10
11
 
11
12
  ## Quick Start
12
13
 
@@ -220,3 +221,8 @@ const nodes: DiagramNodeType[] = [
220
221
  ## License
221
222
 
222
223
  MIT License - see LICENSE file for details.
224
+
225
+
226
+ # TODOs:
227
+ - Refactor away from Diagram.svelte:247 into a more efficient rendering system
228
+ - Add a testing suite that uses the browser DOM and takes screenshots to verify correct rendering against expected outputs
@@ -1,25 +1,21 @@
1
1
  <script lang="ts" module>
2
- const browser = !!globalThis?.window
3
- const dev = globalThis.process?.env?.NODE_ENV && !globalThis.process.env.NODE_ENV.toLowerCase().startsWith('prod');
4
-
5
2
  import { onMount, setContext, type Snippet } from "svelte";
6
3
  import { SvelteMap } from "svelte/reactivity";
7
4
  import {
8
- debugSide,
9
5
  eq,
10
6
  getBezierPath,
11
7
  getSmoothStepPath,
12
8
  normaliseAngle,
13
9
  Side,
14
10
  sideForAngle,
15
- unitVectorFromAngle,
16
11
  vector2,
17
12
  type Vector2,
18
13
  Anchor,
14
+ browser,
15
+ dev,
19
16
  } from "./diagram-lib.js";
20
- import { draw } from "svelte/transition";
21
17
 
22
- export interface DiagramNode {
18
+ export interface DiagramNodeDef {
23
19
  id: string;
24
20
  x: number;
25
21
  y: number;
@@ -44,7 +40,7 @@
44
40
 
45
41
  export type DiagramEdgeParams = {
46
42
  // target: string;
47
- snippet?: Snippet<[edge: DiagramEdge, path: string, extra: any]>;
43
+ snippet?: Snippet<[edge: DiagramEdgeDef, path: string, extra: any]>;
48
44
  snippetExtraArg?: any;
49
45
 
50
46
  sourceAnchor?: Vector2;
@@ -55,14 +51,14 @@
55
51
  zIndex?: number;
56
52
  } & PathGenParams;
57
53
 
58
- export type DiagramEdge = {
54
+ export type DiagramEdgeDef = {
59
55
  source: string;
60
56
  target: string;
61
57
  } & DiagramEdgeParams;
62
58
 
63
59
  export type DiagramProps = {
64
- nodes: SvelteMap<string, DiagramNode>;
65
- edges: SvelteMap<string, DiagramEdge>;
60
+ nodes: SvelteMap<string, DiagramNodeDef>;
61
+ edges: SvelteMap<string, DiagramEdgeDef>;
66
62
  children: Snippet;
67
63
  };
68
64
 
@@ -70,7 +66,7 @@
70
66
  // export const getNodeOrigin = (node: DiagramNode) => node.origin ?? vector2(0.5, 0.5);
71
67
  // export const getNodeOrigin = (node: DiagramNode) => vector2(0.0, 0.0);
72
68
 
73
- const getNodeSize = (node: DiagramNode) => ({
69
+ const getNodeSize = (node: DiagramNodeDef) => ({
74
70
  x: node.width ?? 0,
75
71
  y: node.height ?? 0,
76
72
  });
@@ -83,7 +79,7 @@
83
79
  y1: number,
84
80
  x2: number,
85
81
  y2: number,
86
- edge: DiagramEdge,
82
+ edge: DiagramEdgeDef,
87
83
  ): string {
88
84
  const {
89
85
  sourceAnchor: source = Anchor.CENTER_CENTER,
@@ -154,7 +150,7 @@
154
150
  }
155
151
 
156
152
  function calculateDimensions(_nodes: typeof nodes) {
157
- console.time("dim");
153
+ // console.time("dim");
158
154
  let newMin = vector2(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
159
155
  let newMax = vector2(Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
160
156
 
@@ -177,7 +173,7 @@
177
173
  );
178
174
  }
179
175
 
180
- console.timeEnd("dim");
176
+ // console.timeEnd("dim");
181
177
  return { min: newMin, max: newMax };
182
178
  }
183
179
 
@@ -195,11 +191,12 @@
195
191
  setContext("nodeMap", () => nodes);
196
192
  setContext("edgeMap", () => edges);
197
193
  setContext("dimensions", () => dimensions);
194
+ setContext("prerendering", false);
198
195
 
199
196
  let width = $derived(Math.max(dimensions.max.x - dimensions.min.x, 1));
200
197
  let height = $derived(Math.max(dimensions.max.y - dimensions.min.y, 1));
201
198
 
202
- function generateEdgePath(edge: DiagramEdge) {
199
+ function generateEdgePath(edge: DiagramEdgeDef) {
203
200
  const sourceNode = nodes.get(edge.source)!;
204
201
  const targetNode = nodes.get(edge.target)!;
205
202
 
@@ -221,7 +218,7 @@
221
218
  );
222
219
  }
223
220
 
224
- function getNodeAnchor(node: DiagramNode, anchor: Vector2) {
221
+ function getNodeAnchor(node: DiagramNodeDef, anchor: Vector2) {
225
222
  const size = getNodeSize(node);
226
223
 
227
224
  // if (!browser && !eq(anchor, Anchor.CENTER_CENTER) && eq(size, vector2(0, 0))) {
@@ -236,15 +233,16 @@
236
233
  return { left, top };
237
234
  }
238
235
 
236
+ // TODO: desperate need for refactoring
239
237
  let edgesByZIndexPlane = $derived(
240
- edges.values().reduce((acc, edge) => {
238
+ Array.from(edges.values()).reduce((acc, edge) => {
241
239
  const zIndex = edge.zIndex ?? 0;
242
240
  if (!acc.has(zIndex)) {
243
241
  acc.set(zIndex, []);
244
242
  }
245
243
  acc.get(zIndex)!.push(edge);
246
244
  return acc;
247
- }, new Map<number, DiagramEdge[]>()),
245
+ }, new Map<number, DiagramEdgeDef[]>()),
248
246
  );
249
247
 
250
248
  // let depthMap = new SvelteMap<number, [DiagramEdge[], DiagramNode[]]>();
@@ -262,7 +260,7 @@
262
260
  });
263
261
  </script>
264
262
 
265
- {#snippet defaultEdge(edge: DiagramEdge, edgePath: string)}
263
+ {#snippet defaultEdge(edge: DiagramEdgeDef, edgePath: string)}
266
264
  <path
267
265
  d={edgePath}
268
266
  fill="none"
@@ -1,7 +1,7 @@
1
1
  import { type Snippet } from "svelte";
2
2
  import { SvelteMap } from "svelte/reactivity";
3
3
  import { type Vector2 } from "./diagram-lib.js";
4
- export interface DiagramNode {
4
+ export interface DiagramNodeDef {
5
5
  id: string;
6
6
  x: number;
7
7
  y: number;
@@ -19,7 +19,7 @@ type PathGenParams = {
19
19
  center?: Vector2;
20
20
  };
21
21
  export type DiagramEdgeParams = {
22
- snippet?: Snippet<[edge: DiagramEdge, path: string, extra: any]>;
22
+ snippet?: Snippet<[edge: DiagramEdgeDef, path: string, extra: any]>;
23
23
  snippetExtraArg?: any;
24
24
  sourceAnchor?: Vector2;
25
25
  targetAnchor?: Vector2;
@@ -27,17 +27,17 @@ export type DiagramEdgeParams = {
27
27
  style?: string;
28
28
  zIndex?: number;
29
29
  } & PathGenParams;
30
- export type DiagramEdge = {
30
+ export type DiagramEdgeDef = {
31
31
  source: string;
32
32
  target: string;
33
33
  } & DiagramEdgeParams;
34
34
  export type DiagramProps = {
35
- nodes: SvelteMap<string, DiagramNode>;
36
- edges: SvelteMap<string, DiagramEdge>;
35
+ nodes: SvelteMap<string, DiagramNodeDef>;
36
+ edges: SvelteMap<string, DiagramEdgeDef>;
37
37
  children: Snippet;
38
38
  };
39
39
  declare const Diagram: import("svelte").Component<DiagramProps, {
40
- generateCurvePath: (x1: number, y1: number, x2: number, y2: number, edge: DiagramEdge) => string;
40
+ generateCurvePath: (x1: number, y1: number, x2: number, y2: number, edge: DiagramEdgeDef) => string;
41
41
  }, "">;
42
42
  type Diagram = ReturnType<typeof Diagram>;
43
43
  export default Diagram;
@@ -1,42 +1,105 @@
1
1
  <script lang="ts">
2
- import { SvelteMap } from 'svelte/reactivity';
3
- import Diagram, { type DiagramNode, type DiagramEdge } from './Diagram.svelte';
4
- import { createRawSnippet, flushSync, onMount, setContext, type Snippet } from 'svelte';
5
- import type { HTMLAttributes } from 'svelte/elements';
6
- import PrerenderDiagram from './PrerenderDiagram.svelte';
2
+ import { SvelteMap } from "svelte/reactivity";
3
+ import Diagram, {
4
+ type DiagramNodeDef,
5
+ type DiagramEdgeDef,
6
+ } from "./Diagram.svelte";
7
+ import {
8
+ createRawSnippet,
9
+ flushSync,
10
+ onMount,
11
+ setContext,
12
+ type Snippet,
13
+ } from "svelte";
14
+ import type { HTMLAttributes } from "svelte/elements";
15
+ import PrerenderDiagram from "./PrerenderDiagram.svelte";
16
+ import { browser } from "./diagram-lib.js";
7
17
 
8
- let {
9
- children,
10
- ...rest
11
- }: {
12
- children: Snippet;
13
- } & HTMLAttributes<HTMLDivElement> = $props();
18
+ let {
19
+ children,
20
+ eagerLoad = false,
21
+ rootMargin = "100px", // start a bit before it enters the viewport
22
+ ...rest
23
+ }: {
24
+ children: Snippet;
25
+ eagerLoad: boolean;
26
+ rootMargin?: string;
27
+ } & HTMLAttributes<HTMLDivElement> = $props();
14
28
 
15
- const nodes = new SvelteMap<string, DiagramNode>();
29
+ const nodes = new SvelteMap<string, DiagramNodeDef>();
30
+ const layers = new SvelteMap<number, Record<string, DiagramNodeDef>>();
31
+ setContext("layerNodeMap", () => layers);
32
+ const edges = new SvelteMap<string, DiagramEdgeDef>();
16
33
 
17
- const edges = new SvelteMap<string, DiagramEdge>();
34
+ let containerEl: HTMLDivElement | undefined;
18
35
 
19
- // let initialDiagramContainer: HTMLElement;
36
+ // If we're SSR (not browser), or eagerLoad is false, render immediately.
37
+ // Otherwise wait until after load + idle + intersection.
38
+ let shouldRender = $derived(!eagerLoad);
20
39
 
21
- // onMount(() => {
22
- // initialDiagramContainer.remove();
23
- // });
40
+ const initialTime = performance.now();
24
41
 
25
- const initialTime = performance.now();
42
+ onMount(() => {
43
+ if (!eagerLoad || !containerEl) return;
44
+
45
+ let io: IntersectionObserver | null = null;
46
+
47
+ const idle = (fn: () => void) => {
48
+ const ric = (window as any).requestIdleCallback as
49
+ | ((cb: () => void) => number)
50
+ | undefined;
51
+ if (ric) ric(fn);
52
+ else setTimeout(fn, 0);
53
+ };
54
+
55
+ const startObserving = () => {
56
+ // In case the element is already visible at this moment
57
+ io = new IntersectionObserver(
58
+ (entries) => {
59
+ if (entries.some((e) => e.isIntersecting)) {
60
+ shouldRender = true;
61
+ io?.disconnect();
62
+ io = null;
63
+ }
64
+ },
65
+ { root: null, rootMargin, threshold: 0 },
66
+ );
67
+ io.observe(containerEl!);
68
+ };
69
+
70
+ if (document.readyState === "complete") {
71
+ idle(startObserving);
72
+ } else {
73
+ // Wait until the whole document (including images) has loaded
74
+ window.addEventListener("load", () => idle(startObserving), {
75
+ once: true,
76
+ });
77
+ }
78
+
79
+ return () => {
80
+ io?.disconnect();
81
+ io = null;
82
+ };
83
+ });
26
84
  </script>
27
85
 
28
- <!-- first time to register all the nodes and edges -->
29
- <PrerenderDiagram {nodes} {edges} {children} />
30
-
31
- <!-- second time to actually render it after we calculate all the relative positions and total widths and heights -->
32
- <!-- this is necessary because we need to calculate the relative positions of the nodes and edges -->
33
- <!-- TODO: Investigate how to do this properly with hydrate and mount and render https://svelte.dev/docs/svelte/imperative-component-api#hydrate -->
34
- <div {...rest}>
35
- <Diagram {nodes} {edges}>
36
- {@render children()}
37
- </Diagram>
38
- </div>
39
- <!--
86
+ {#if shouldRender}
87
+ <!-- first pass: register all the nodes and edges -->
88
+ <PrerenderDiagram {nodes} {edges} {children} />
89
+
90
+ <!-- second pass: render with computed positions -->
91
+ <div {...rest}>
92
+ <Diagram {nodes} {edges}>
93
+ {@render children()}
94
+ </Diagram>
95
+ </div>
96
+ {:else}
97
+ <!-- Lightweight placeholder / container used for intersection observation. -->
98
+ <div bind:this={containerEl} {...rest}></div>
99
+ {/if}
100
+
101
+ <!--
40
102
  <svelte:boundary>
41
103
  {@const _ = console.log('Finished rendering diagram in', performance.now() - initialTime, 'ms')}
42
- </svelte:boundary> -->
104
+ </svelte:boundary>
105
+ -->
@@ -1,7 +1,9 @@
1
- import { type Snippet } from 'svelte';
2
- import type { HTMLAttributes } from 'svelte/elements';
1
+ import { type Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
3
  type $$ComponentProps = {
4
4
  children: Snippet;
5
+ eagerLoad: boolean;
6
+ rootMargin?: string;
5
7
  } & HTMLAttributes<HTMLDivElement>;
6
8
  declare const DiagramController: import("svelte").Component<$$ComponentProps, {}, "">;
7
9
  type DiagramController = ReturnType<typeof DiagramController>;
@@ -1,47 +1,35 @@
1
1
  <script lang="ts">
2
- import {
3
- getContext,
4
- onMount,
5
- tick,
6
- type ComponentProps,
7
- type Snippet,
8
- } from "svelte";
2
+ import { getContext, onMount, tick, type Snippet } from "svelte";
9
3
  import type {
10
- DiagramNode,
4
+ DiagramNodeDef,
11
5
  DiagramEdgeParams,
12
- DiagramEdge,
6
+ DiagramEdgeDef,
13
7
  } from "./Diagram.svelte";
14
8
  import type { SvelteMap } from "svelte/reactivity";
15
9
  import type { HTMLAttributes } from "svelte/elements";
16
- import { vector2, type Vector2 } from "./diagram-lib";
10
+ import { vector2, type Vector2 } from "./diagram-lib.js";
17
11
 
18
- // type IndividualConnectActionParam = string | Omit<DiagramEdge, 'source'>;
19
12
  type IndividualConnectActionParam =
20
13
  | string
21
- | (DiagramEdgeParams & { target: string });
22
- type IndividualConnectSourceActionParam =
23
- | string
14
+ | (DiagramEdgeParams & { target: string })
24
15
  | (DiagramEdgeParams & { source: string });
16
+
25
17
  type DiagramNodeConnectParam =
26
18
  | IndividualConnectActionParam
27
19
  | IndividualConnectActionParam[];
28
- type DiagramNodeConnectSourceParam =
29
- | IndividualConnectSourceActionParam
30
- | IndividualConnectSourceActionParam[];
31
20
 
32
21
  export type DiagramNodeProps = {
33
22
  children?: Snippet;
34
23
  connect?: DiagramNodeConnectParam;
35
- connectSource?: DiagramNodeConnectSourceParam;
36
24
  autosize?: boolean;
37
25
  origin?: Vector2;
38
- } & Omit<DiagramNode, "snippet"> &
26
+ } & Omit<DiagramNodeDef, "snippet"> &
39
27
  HTMLAttributes<HTMLDivElement>;
40
28
 
41
29
  let {
42
30
  children,
43
31
  connect,
44
- connectSource: connectFrom,
32
+ // connectSource: connectFrom,
45
33
  id,
46
34
  x,
47
35
  y,
@@ -56,11 +44,12 @@
56
44
  }: DiagramNodeProps = $props();
57
45
 
58
46
  const nodeMap = (
59
- getContext("nodeMap") as () => SvelteMap<string, DiagramNode>
47
+ getContext("nodeMap") as () => SvelteMap<string, DiagramNodeDef>
60
48
  )();
61
49
  const edgeMap = (
62
- getContext("edgeMap") as () => SvelteMap<string, DiagramEdge>
50
+ getContext("edgeMap") as () => SvelteMap<string, DiagramEdgeDef>
63
51
  )();
52
+
64
53
  let dimensions = (
65
54
  getContext("dimensions") as () =>
66
55
  | { min: Vector2; max: Vector2 }
@@ -76,8 +65,7 @@
76
65
  y: y - (origin?.y ?? 0.5) * (height ?? 0),
77
66
  });
78
67
 
79
- // const nodeDef: DiagramNode = $derived({ id, x, y, width, height, clientOnly, snippet: children });
80
- const nodeDef: DiagramNode = $derived({
68
+ const nodeDef: DiagramNodeDef = $derived({
81
69
  id,
82
70
  x: absolutePosition.x,
83
71
  y: absolutePosition.y,
@@ -86,17 +74,8 @@
86
74
  clientOnly: clientOnly || autosize,
87
75
  snippet: children,
88
76
  });
89
- const prerender = $state.snapshot(getContext("prerendering"));
90
-
91
- // console.log('set', nodeDef.id, nodeDef);
92
-
93
- // const source = elementNodeMap.get(element);
94
- // if (!source) {
95
- // return console.warn(`Could not find source element for`, element);
96
- // }
97
77
 
98
78
  let mounted = $state(false);
99
-
100
79
  const previousEdgeIds = new Set();
101
80
 
102
81
  // TODO: this should be done only if clientWidth is needed
@@ -108,63 +87,59 @@
108
87
  // nodeDef.y -= (origin?.y ?? 0) * $state.snapshot(nodeDef.height ?? 0);
109
88
  // }
110
89
 
111
- if (autosize) {
112
- onMount(() => {
90
+ onMount(async () => {
91
+ if (autosize) {
113
92
  width = clientWidth;
114
93
  height = clientHeight;
115
- });
116
- }
94
+ }
117
95
 
118
- onMount(async () => {
119
96
  if (nodeDef.clientOnly) {
120
97
  await tick();
121
98
  mounted = true;
122
99
  }
123
100
  });
124
101
 
125
- function updateEdgeFrom(
126
- param: IndividualConnectSourceActionParam,
127
- index: number = 0,
128
- ) {
129
- const getEdgeId = (source: string, i: number) =>
130
- `${source}:${nodeDef.id}:(${i})`;
131
- // const edgeId = getEdgeId();
132
- if (typeof param == "string") {
133
- const source = param;
134
-
135
- const edgeId = getEdgeId(source, index);
136
- previousEdgeIds.add(edgeId);
137
-
138
- edgeMap.set(edgeId, { target: nodeDef.id, source });
139
- } else {
140
- const edgeId = getEdgeId(param.source, index);
141
- previousEdgeIds.add(edgeId);
142
-
143
- (param as DiagramEdge).target = nodeDef.id;
144
- edgeMap.set(edgeId, param as DiagramEdge);
145
- }
146
- }
147
-
148
102
  function updateEdge(
149
103
  param: IndividualConnectActionParam,
150
104
  index: number = 0,
151
105
  ) {
152
- const getEdgeId = (target: string, i: number) =>
153
- `${nodeDef.id}:${target}:(${i})`;
154
- // const edgeId = getEdgeId();
106
+ const selfId = nodeDef.id;
107
+ const getEdgeId = ({
108
+ source,
109
+ target,
110
+ index,
111
+ }: {
112
+ source: string;
113
+ target: string;
114
+ index: number;
115
+ }) => `${source}:${target}:(${index})`;
116
+
155
117
  if (typeof param == "string") {
156
118
  const target = param;
157
119
 
158
- const edgeId = getEdgeId(target, index);
120
+ const edgeId = getEdgeId({ source: selfId, target, index });
159
121
  previousEdgeIds.add(edgeId);
160
122
 
161
123
  edgeMap.set(edgeId, { source: nodeDef.id, target });
162
- } else {
163
- const edgeId = getEdgeId(param.target, index);
124
+ } else if ("target" in param) {
125
+ // const edgeId = getEdgeId(param.target, index);
126
+ const edgeId = getEdgeId({
127
+ source: selfId,
128
+ target: param.target,
129
+ index,
130
+ });
164
131
  previousEdgeIds.add(edgeId);
165
-
166
- (param as DiagramEdge).source = nodeDef.id;
167
- edgeMap.set(edgeId, param as DiagramEdge);
132
+ (param as DiagramEdgeDef).source = nodeDef.id;
133
+ edgeMap.set(edgeId, param as DiagramEdgeDef);
134
+ } else if ("source" in param) {
135
+ const edgeId = getEdgeId({
136
+ source: param.source,
137
+ target: selfId,
138
+ index,
139
+ });
140
+ previousEdgeIds.add(edgeId);
141
+ (param as DiagramEdgeDef).target = selfId;
142
+ edgeMap.set(edgeId, param as DiagramEdgeDef);
168
143
  }
169
144
  }
170
145
 
@@ -176,28 +151,14 @@
176
151
  }
177
152
  }
178
153
 
179
- if (connectFrom) {
180
- if (!Array.isArray(connectFrom)) {
181
- updateEdgeFrom(connectFrom);
182
- } else {
183
- connectFrom.forEach(updateEdgeFrom);
184
- }
185
- }
186
-
187
154
  let left = $derived(nodeDef.x - (dimensions?.min.x ?? 0));
188
155
  let top = $derived(nodeDef.y - (dimensions?.min.y ?? 0));
189
156
 
157
+ nodeMap.set(nodeDef.id, nodeDef);
190
158
  $effect(() => {
191
159
  nodeMap.set(nodeDef.id, nodeDef);
192
160
  });
193
161
 
194
- // nodeMap.set(nodeDef.id, nodeDef);
195
- $effect(() => {
196
- // nodeDef.x -= (origin?.x ?? 0.5) * (nodeDef?.width ?? clientWidth ?? 0);
197
- // nodeDef.y -= (origin?.y ?? 0.5) * (nodeDef?.height ?? clientHeight ?? 0);
198
- nodeMap.set(nodeDef.id, nodeDef);
199
- });
200
-
201
162
  // $inspect(
202
163
  // 'mounted render diagramNode',
203
164
  // nodeDef.id,
@@ -226,6 +187,5 @@ top:${top}px;left:${left}px;${nodeDef.clientOnly && !mounted ? "opacity:0" : ""}
226
187
  bind:clientHeight
227
188
  ></div>
228
189
  {/if}
229
- <!-- <div class="absolute top-0 left-0 w-full h-full bg-red-500"> {nodeDef.clientOnly} {mounted}</div> -->
230
190
  {@render children?.()}
231
191
  </div>
@@ -1,22 +1,19 @@
1
1
  import { type Snippet } from "svelte";
2
- import type { DiagramNode, DiagramEdgeParams } from "./Diagram.svelte";
2
+ import type { DiagramNodeDef, DiagramEdgeParams } from "./Diagram.svelte";
3
3
  import type { HTMLAttributes } from "svelte/elements";
4
- import { type Vector2 } from "./diagram-lib";
4
+ import { type Vector2 } from "./diagram-lib.js";
5
5
  type IndividualConnectActionParam = string | (DiagramEdgeParams & {
6
6
  target: string;
7
- });
8
- type IndividualConnectSourceActionParam = string | (DiagramEdgeParams & {
7
+ }) | (DiagramEdgeParams & {
9
8
  source: string;
10
9
  });
11
10
  type DiagramNodeConnectParam = IndividualConnectActionParam | IndividualConnectActionParam[];
12
- type DiagramNodeConnectSourceParam = IndividualConnectSourceActionParam | IndividualConnectSourceActionParam[];
13
11
  export type DiagramNodeProps = {
14
12
  children?: Snippet;
15
13
  connect?: DiagramNodeConnectParam;
16
- connectSource?: DiagramNodeConnectSourceParam;
17
14
  autosize?: boolean;
18
15
  origin?: Vector2;
19
- } & Omit<DiagramNode, "snippet"> & HTMLAttributes<HTMLDivElement>;
20
- declare const DiagramNode: any;
16
+ } & Omit<DiagramNodeDef, "snippet"> & HTMLAttributes<HTMLDivElement>;
17
+ declare const DiagramNode: import("svelte").Component<DiagramNodeProps, {}, "">;
21
18
  type DiagramNode = ReturnType<typeof DiagramNode>;
22
19
  export default DiagramNode;
@@ -1,18 +1,23 @@
1
1
  <script lang="ts">
2
- import { setContext } from 'svelte';
3
- import type { DiagramProps } from './Diagram.svelte';
4
- import { vector2 } from './diagram-lib';
2
+ import { setContext } from "svelte";
3
+ import type { DiagramProps } from "./Diagram.svelte";
4
+ import { vector2 } from "./diagram-lib.js";
5
5
 
6
- let { nodes, edges, children }: DiagramProps = $props();
6
+ let { nodes, edges, children }: DiagramProps = $props();
7
7
 
8
- setContext('nodeMap', () => nodes);
9
- setContext('edgeMap', () => edges);
10
- setContext('dimensions', () => ({ min: vector2(0, 0), max: vector2(0, 0) }));
11
- setContext('prerendering', true);
8
+ setContext("nodeMap", () => nodes);
9
+ setContext("edgeMap", () => edges);
10
+ setContext("dimensions", () => ({
11
+ min: vector2(0, 0),
12
+ max: vector2(0, 0),
13
+ }));
14
+ setContext("prerendering", true);
12
15
  </script>
13
16
 
14
17
  <template>
15
- {@render children()}
18
+ {@render children()}
16
19
  </template>
17
20
 
18
- <svelte:boundary>{@const _ = setContext('prerendering', false)}</svelte:boundary>
21
+ <svelte:boundary>
22
+ {@const _ = setContext("prerendering", false)}
23
+ </svelte:boundary>
@@ -1,4 +1,4 @@
1
- import type { DiagramProps } from './Diagram.svelte';
1
+ import type { DiagramProps } from "./Diagram.svelte";
2
2
  declare const PrerenderDiagram: import("svelte").Component<DiagramProps, {}, "">;
3
3
  type PrerenderDiagram = ReturnType<typeof PrerenderDiagram>;
4
4
  export default PrerenderDiagram;
@@ -1,3 +1,5 @@
1
+ export declare const browser = true;
2
+ export declare const dev: any;
1
3
  export interface Vector2 {
2
4
  x: number;
3
5
  y: number;
@@ -1,3 +1,6 @@
1
+ export const browser = !!globalThis?.window;
2
+ export const dev = globalThis?.process?.env?.NODE_ENV &&
3
+ !globalThis?.process.env.NODE_ENV.toLowerCase().startsWith("prod");
1
4
  export const vector2 = (x, y) => ({ x, y });
2
5
  export const eq = (a, b) => a.x === b.x && a.y === b.y;
3
6
  export const Anchor = {
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { default as DiagramController } from './DiagramController.svelte';
3
3
  export { default as DiagramNode } from './DiagramNode.svelte';
4
4
  export { default as PrerenderDiagram } from './PrerenderDiagram.svelte';
5
5
  export * from './diagram-lib.js';
6
- export type { DiagramNode as DiagramNodeType, DiagramEdge, DiagramEdgeParams, DiagramProps } from './Diagram.svelte';
6
+ export type { DiagramEdgeDef as DiagramEdge, DiagramEdgeParams, DiagramProps } from './Diagram.svelte';
7
7
  export type { DiagramNodeProps } from './DiagramNode.svelte';
package/package.json CHANGED
@@ -1,83 +1,83 @@
1
1
  {
2
- "name": "@cnvx/nodal",
3
- "private": false,
4
- "license": "MIT",
5
- "version": "0.0.4",
6
- "scripts": {
7
- "dev": "vite dev",
8
- "build": "vite build && npm run prepack",
9
- "preview": "vite preview",
10
- "prepare": "svelte-kit sync || echo ''",
11
- "prepack": "svelte-kit sync && svelte-package && publint",
12
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
13
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
14
- "format": "prettier --write .",
15
- "lint": "prettier --check . && eslint .",
16
- "test:unit": "vitest",
17
- "test": "npm run test:unit -- --run"
18
- },
19
- "files": [
20
- "dist",
21
- "!dist/**/*.test.*",
22
- "!dist/**/*.spec.*"
23
- ],
24
- "sideEffects": [
25
- "**/*.css"
26
- ],
27
- "svelte": "./dist/index.js",
28
- "types": "./dist/index.d.ts",
29
- "type": "module",
30
- "exports": {
31
- ".": {
32
- "types": "./dist/index.d.ts",
33
- "svelte": "./dist/index.js"
34
- }
35
- },
36
- "peerDependencies": {
37
- "svelte": "^5.0.0"
38
- },
39
- "devDependencies": {
40
- "@eslint/compat": "^1.2.5",
41
- "@eslint/js": "^9.18.0",
42
- "@sveltejs/adapter-auto": "^6.0.0",
43
- "@sveltejs/kit": "^2.22.0",
44
- "@sveltejs/package": "^2.0.0",
45
- "@sveltejs/vite-plugin-svelte": "^6.0.0",
46
- "@tailwindcss/vite": "^4.1.11",
47
- "@vitest/browser": "^3.2.3",
48
- "eslint": "^9.18.0",
49
- "eslint-config-prettier": "^10.0.1",
50
- "eslint-plugin-svelte": "^3.0.0",
51
- "globals": "^16.0.0",
52
- "playwright": "^1.53.0",
53
- "prettier": "^3.4.2",
54
- "prettier-plugin-svelte": "^3.3.3",
55
- "publint": "^0.3.2",
56
- "svelte": "^5.0.0",
57
- "svelte-check": "^4.0.0",
58
- "typescript": "^5.0.0",
59
- "typescript-eslint": "^8.20.0",
60
- "vite": "^7.0.4",
61
- "vitest": "^3.2.3",
62
- "vitest-browser-svelte": "^0.1.0"
63
- },
64
- "keywords": [
65
- "svelte",
66
- "diagram",
67
- "nodes",
68
- "graph",
69
- "visualization",
70
- "flow-chart",
71
- "connections",
72
- "bezier",
73
- "smoothstep"
74
- ],
75
- "repository": {
76
- "type": "git",
77
- "url": "git+https://github.com/Convex-Works/nodal.git"
78
- },
79
- "dependencies": {
80
- "@sveltejs/adapter-static": "^3.0.8",
81
- "mdsvex": "^0.12.6"
82
- }
2
+ "name": "@cnvx/nodal",
3
+ "private": false,
4
+ "license": "MIT",
5
+ "version": "0.1.0",
6
+ "scripts": {
7
+ "dev": "vite dev",
8
+ "dev-lib": "svelte-package -w",
9
+ "build": "vite build && npm run prepack",
10
+ "preview": "vite preview",
11
+ "prepare": "svelte-kit sync || echo ''",
12
+ "prepack": "svelte-kit sync && svelte-package && publint",
13
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
14
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
15
+ "test:capture": "bun run test/capture.ts",
16
+ "format": "prettier --write .",
17
+ "lint": "prettier --check . && eslint ."
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "!dist/**/*.test.*",
22
+ "!dist/**/*.spec.*"
23
+ ],
24
+ "sideEffects": [
25
+ "**/*.css"
26
+ ],
27
+ "svelte": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "type": "module",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "svelte": "./dist/index.js"
34
+ }
35
+ },
36
+ "peerDependencies": {
37
+ "svelte": "^5.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@eslint/compat": "^1.2.5",
41
+ "@eslint/js": "^9.18.0",
42
+ "@sveltejs/adapter-auto": "^6.0.0",
43
+ "@sveltejs/adapter-static": "^3.0.8",
44
+ "@sveltejs/kit": "^2.22.0",
45
+ "@sveltejs/package": "^2.0.0",
46
+ "@sveltejs/vite-plugin-svelte": "^6.0.0",
47
+ "@tailwindcss/vite": "^4.1.11",
48
+ "@types/bun": "^1.2.19",
49
+ "@types/pixelmatch": "^5.2.6",
50
+ "@types/pngjs": "^6.0.5",
51
+ "eslint": "^9.18.0",
52
+ "eslint-config-prettier": "^10.0.1",
53
+ "eslint-plugin-svelte": "^3.0.0",
54
+ "globals": "^16.0.0",
55
+ "mdsvex": "^0.12.6",
56
+ "pixelmatch": "^7.1.0",
57
+ "playwright": "^1.53.0",
58
+ "prettier": "^3.4.2",
59
+ "prettier-plugin-svelte": "^3.3.3",
60
+ "publint": "^0.3.2",
61
+ "svelte": "^5.0.0",
62
+ "svelte-check": "^4.0.0",
63
+ "typescript": "^5.0.0",
64
+ "typescript-eslint": "^8.20.0",
65
+ "vite": "^7.0.4"
66
+ },
67
+ "keywords": [
68
+ "svelte",
69
+ "diagram",
70
+ "nodes",
71
+ "graph",
72
+ "visualization",
73
+ "flow-chart",
74
+ "connections",
75
+ "bezier",
76
+ "smoothstep"
77
+ ],
78
+ "repository": {
79
+ "type": "git",
80
+ "url": "git+https://github.com/Convex-Works/nodal.git"
81
+ },
82
+ "dependencies": {}
83
83
  }