vizcraft 0.3.0 → 1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # vizcraft
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`86655bf`](https://github.com/ChipiKaf/vizcraft/commit/86655bf59c9641af18416b79d0775212a9ecd15e) Thanks [@ChipiKaf](https://github.com/ChipiKaf)! - Added support for incremental scene updates, node resizing, JSON serialization/deserialization, viewport pan and zoom functionality, multi-line labels with text wrapping, declarative API options for nodes and edges, and custom dashed/dotted edge line styles.
8
+
9
+ ## 1.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - [`73ee1fb`](https://github.com/ChipiKaf/vizcraft/commit/73ee1fbd204bf1ba0447c764eba2c1b9d6981ee5) Thanks [@ChipiKaf](https://github.com/ChipiKaf)! - Added new shapes, connection ports, path based edges, Edge marker types, Multi-position edge labels, Containers and The overlay builder
14
+
3
15
  ## 0.3.0
4
16
 
5
17
  ### Minor Changes
package/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chipili Kafwilo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -92,6 +92,25 @@ b.view(width, height) // Set the coordinate space
92
92
  .edge(from, to); // Start defining an edge
93
93
  ```
94
94
 
95
+ ### Declarative Options Overloads
96
+
97
+ You can also configure nodes and edges in a single declarative call by passing an options object:
98
+
99
+ ```typescript
100
+ // Declarative — pass all options at once, returns VizBuilder
101
+ b.node('a', {
102
+ at: { x: 100, y: 100 },
103
+ rect: { w: 80, h: 40 },
104
+ fill: 'steelblue',
105
+ label: 'A',
106
+ })
107
+ .node('b', { circle: { r: 20 }, at: { x: 300, y: 100 }, label: 'B' })
108
+ .edge('a', 'b', { arrow: true, stroke: 'red', dash: 'dashed' })
109
+ .build();
110
+ ```
111
+
112
+ Both `NodeOptions` and `EdgeOptions` types are exported for full type-safety. See the [Essentials docs](https://vizcraft.dev/docs/essentials) for the complete options reference.
113
+
95
114
  ### Nodes
96
115
 
97
116
  Nodes are the primary entities in your graph. They can have shapes, labels, and styles.
@@ -101,15 +120,54 @@ b.node('n1')
101
120
  .at(x, y) // Absolute position
102
121
  // OR
103
122
  .cell(col, row) // Grid position
104
- .circle(radius) // Shape definition
123
+ .circle(radius) // Circle shape
124
+ .rect(w, h, [rx]) // Rectangle (optional corner radius)
125
+ .diamond(w, h) // Diamond shape
126
+ .cylinder(w, h, [arcHeight]) // Cylinder (database symbol)
127
+ .hexagon(r, [orientation]) // Hexagon ('pointy' or 'flat')
128
+ .ellipse(rx, ry) // Ellipse / oval
129
+ .arc(r, start, end, [closed]) // Arc / pie slice
130
+ .blockArrow(len, bodyW, headW, headLen, [dir]) // Block arrow
131
+ .callout(w, h, [opts]) // Speech bubble / callout
132
+ .cloud(w, h) // Cloud / thought bubble
133
+ .cross(size, [barWidth]) // Cross / plus sign
134
+ .cube(w, h, [depth]) // 3D isometric cube
135
+ .path(d, w, h) // Custom SVG path
136
+ .document(w, h, [wave]) // Document (wavy bottom)
137
+ .note(w, h, [foldSize]) // Note (folded corner)
138
+ .parallelogram(w, h, [skew]) // Parallelogram (I/O)
139
+ .star(points, outerR, [innerR]) // Star / badge
140
+ .trapezoid(topW, bottomW, h) // Trapezoid
141
+ .triangle(w, h, [direction]) // Triangle
105
142
  .label('Text', { dy: 5 }) // Label with offset
106
143
  .class('css-class') // Custom CSS class
107
144
  .data({ ... }) // Attach custom data
145
+ .port('out', { x: 50, y: 0 }) // Named connection port
146
+ .container(config?) // Mark as container / group node
147
+ .parent('containerId') // Make child of a container
148
+ ```
149
+
150
+ ### Container / Group Nodes
151
+
152
+ Group related nodes into visual containers (swimlanes, sub-processes, etc.).
153
+
154
+ ```typescript
155
+ b.node('lane')
156
+ .at(250, 170)
157
+ .rect(460, 300)
158
+ .label('Process Phase')
159
+ .container({ headerHeight: 36 });
160
+
161
+ b.node('step1').at(150, 220).rect(100, 50).parent('lane');
162
+ b.node('step2').at(350, 220).rect(100, 50).parent('lane');
108
163
  ```
109
164
 
165
+ Container children are nested inside the container `<g>` in the SVG and follow the container when moved at runtime.
166
+
110
167
  ### Edges
111
168
 
112
169
  Edges connect nodes and can be styled, directed, or animated.
170
+ All edges are rendered as `<path>` elements supporting three routing modes.
113
171
 
114
172
  ```typescript
115
173
  b.edge('n1', 'n2')
@@ -117,8 +175,73 @@ b.edge('n1', 'n2')
117
175
  .straight() // (Default) Straight line
118
176
  .label('Connection')
119
177
  .animate('flow'); // Add animation
178
+
179
+ // Curved edge
180
+ b.edge('a', 'b').curved().arrow();
181
+
182
+ // Orthogonal (right-angle) edge
183
+ b.edge('a', 'c').orthogonal().arrow();
184
+
185
+ // Waypoints — intermediate points the edge passes through
186
+ b.edge('x', 'y').curved().via(150, 50).via(200, 100).arrow();
187
+
188
+ // Per-edge styling (overrides CSS defaults)
189
+ b.edge('a', 'b').stroke('#ff0000', 3).fill('none').opacity(0.8);
190
+
191
+ // Dashed, dotted, and custom dash patterns
192
+ b.edge('a', 'b').dashed().stroke('#6c7086'); // dashed line
193
+ b.edge('a', 'b').dotted(); // dotted line
194
+ b.edge('a', 'b').dash('12, 3, 3, 3').stroke('#cba6f7'); // custom pattern
195
+
196
+ // Multi-position edge labels (start / mid / end)
197
+ b.edge('a', 'b')
198
+ .label('1', { position: 'start' })
199
+ .label('*', { position: 'end' })
200
+ .arrow();
201
+
202
+ // Edge markers / arrowhead types
203
+ b.edge('a', 'b').markerEnd('arrowOpen'); // Open arrow (inheritance)
204
+ b.edge('a', 'b').markerStart('diamond').markerEnd('arrow'); // UML composition
205
+ b.edge('a', 'b').markerStart('diamondOpen').markerEnd('arrow'); // UML aggregation
206
+ b.edge('a', 'b').arrow('both'); // Bidirectional arrows
207
+ b.edge('a', 'b').markerStart('circleOpen').markerEnd('arrow'); // Association
208
+ b.edge('a', 'b').markerEnd('bar'); // ER cardinality
209
+
210
+ // Connection ports — edges attach to specific points on nodes
211
+ b.node('srv')
212
+ .at(100, 100)
213
+ .rect(80, 60)
214
+ .port('out-1', { x: 40, y: -15 })
215
+ .port('out-2', { x: 40, y: 15 });
216
+ b.node('db').at(400, 100).cylinder(80, 60).port('in', { x: -40, y: 0 });
217
+ b.edge('srv', 'db').fromPort('out-1').toPort('in').arrow();
218
+
219
+ // Default ports (no .port() needed) — every shape has built-in ports
220
+ b.edge('a', 'b').fromPort('right').toPort('left').arrow();
120
221
  ```
121
222
 
223
+ | Method | Description |
224
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
225
+ | `.straight()` | Direct line (default). With waypoints → polyline. |
226
+ | `.curved()` | Smooth bezier curve. With waypoints → Catmull-Rom spline. |
227
+ | `.orthogonal()` | Right-angle elbows. |
228
+ | `.routing(mode)` | Set mode programmatically. |
229
+ | `.via(x, y)` | Add an intermediate waypoint (chainable). |
230
+ | `.label(text, opts?)` | Add a text label. Chain multiple calls for multi-position labels. `opts.position` can be `'start'`, `'mid'` (default), or `'end'`. |
231
+ | `.arrow([enabled])` | Shorthand for arrow markers. `true`/no-arg → markerEnd arrow. `'both'` → both ends. `'start'`/`'end'` → specific end. `false` → none. |
232
+ | `.markerEnd(type)` | Set marker type at the target end. See `EdgeMarkerType`. |
233
+ | `.markerStart(type)` | Set marker type at the source end. See `EdgeMarkerType`. |
234
+ | `.fromPort(portId)` | Connect from a specific named port on the source node. |
235
+ | `.toPort(portId)` | Connect to a specific named port on the target node. |
236
+ | `.stroke(color, width?)` | Set stroke color and optional width. |
237
+ | `.fill(color)` | Set fill color. |
238
+ | `.opacity(value)` | Set opacity (0–1). |
239
+ | `.dashed()` | Dashed stroke (`8, 4`). |
240
+ | `.dotted()` | Dotted stroke (`2, 4`). |
241
+ | `.dash(pattern)` | Custom SVG dasharray or preset (`'dashed'`, `'dotted'`, `'dash-dot'`, `'solid'`). |
242
+
243
+ **`EdgeMarkerType`** values: `'none'`, `'arrow'`, `'arrowOpen'`, `'diamond'`, `'diamondOpen'`, `'circle'`, `'circleOpen'`, `'square'`, `'bar'`, `'halfArrow'`.
244
+
122
245
  ### Animations
123
246
 
124
247
  See the full Animations guide [docs here](https://vizcraft-docs.vercel.app/docs/animations).
@@ -265,13 +388,19 @@ VizCraft generates standard SVG elements with predictable classes, making it eas
265
388
  fill: #ff6b6b;
266
389
  }
267
390
 
268
- /* Edge styling */
391
+ /* Edge styling (CSS defaults) */
269
392
  .viz-edge {
270
393
  stroke: #ccc;
271
394
  stroke-width: 2;
272
395
  }
273
396
  ```
274
397
 
398
+ Edges can also be styled **per-edge** via the builder (inline SVG attributes override CSS):
399
+
400
+ ```ts
401
+ b.edge('a', 'b').stroke('#e74c3c', 3).fill('none').opacity(0.8);
402
+ ```
403
+
275
404
  ## 🤝 Contributing
276
405
 
277
406
  Contributions are welcome! This is a monorepo managed with Turbo.
package/dist/builder.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import type { VizScene, VizNode, VizEdge, NodeLabel, EdgeLabel, AnimationConfig, OverlayId, OverlayParams, VizGridConfig } from './types';
1
+ import type { VizScene, VizNode, VizEdge, NodeLabel, EdgeLabel, AnimationConfig, OverlayId, OverlayParams, VizGridConfig, ContainerConfig, EdgeRouting, EdgeMarkerType, NodeOptions, EdgeOptions, PanZoomOptions, PanZoomController, VizSceneMutator } from './types';
2
2
  import { OverlayBuilder } from './overlayBuilder';
3
3
  import type { AnimationSpec } from './anim/spec';
4
4
  import { type AnimationBuilder, type AnimatableProps, type TweenOptions } from './anim/animationBuilder';
5
5
  import { type PlaybackController } from './anim/playback';
6
- interface VizBuilder {
6
+ export interface VizBuilder extends VizSceneMutator {
7
7
  view(w: number, h: number): VizBuilder;
8
8
  grid(cols: number, rows: number, padding?: {
9
9
  x: number;
@@ -18,8 +18,16 @@ interface VizBuilder {
18
18
  overlay(cb: (overlay: OverlayBuilder) => unknown): VizBuilder;
19
19
  overlay<K extends OverlayId>(id: K, params: OverlayParams<K>, key?: string): VizBuilder;
20
20
  overlay<T>(id: string, params: T, key?: string): VizBuilder;
21
+ /** Create a node and return the NodeBuilder for fluent chaining. */
21
22
  node(id: string): NodeBuilder;
23
+ /** Create a fully-configured node declaratively and return the parent VizBuilder. */
24
+ node(id: string, opts: NodeOptions): VizBuilder;
25
+ /** Create an edge and return the EdgeBuilder for fluent chaining. */
22
26
  edge(from: string, to: string, id?: string): EdgeBuilder;
27
+ /** Create a fully-configured edge declaratively and return the parent VizBuilder. */
28
+ edge(from: string, to: string, opts: EdgeOptions): VizBuilder;
29
+ /** Hydrates the builder from an existing VizScene. */
30
+ fromScene(scene: VizScene): VizBuilder;
23
31
  build(): VizScene;
24
32
  _getGridConfig(): VizGridConfig | null;
25
33
  _getViewBox(): {
@@ -27,11 +35,11 @@ interface VizBuilder {
27
35
  h: number;
28
36
  };
29
37
  svg(): string;
30
- mount(container: HTMLElement): void;
38
+ mount(container: HTMLElement): PanZoomController | undefined;
31
39
  mount(container: HTMLElement, opts: {
32
40
  autoplay?: boolean;
33
41
  css?: string | string[];
34
- }): void;
42
+ } & PanZoomOptions): PanZoomController | undefined;
35
43
  /**
36
44
  * Plays animation specs against a mounted container.
37
45
  *
@@ -45,6 +53,14 @@ interface VizBuilder {
45
53
  play(container: HTMLElement): PlaybackController | null;
46
54
  play(container: HTMLElement, spec: AnimationSpec): PlaybackController;
47
55
  play(container: HTMLElement, spec: AnimationSpec[]): PlaybackController;
56
+ /**
57
+ * Resizes a node at runtime, overriding its initial shape dimensions.
58
+ */
59
+ resizeNode(id: string, dims: {
60
+ w?: number;
61
+ h?: number;
62
+ r?: number;
63
+ }): VizBuilder;
48
64
  /**
49
65
  * Applies runtime-only patches (node.runtime / edge.runtime) to the mounted SVG.
50
66
  * This avoids full DOM reconciliation and is intended for animation frame updates.
@@ -57,6 +73,28 @@ interface NodeBuilder {
57
73
  circle(r: number): NodeBuilder;
58
74
  rect(w: number, h: number, rx?: number): NodeBuilder;
59
75
  diamond(w: number, h: number): NodeBuilder;
76
+ cylinder(w: number, h: number, arcHeight?: number): NodeBuilder;
77
+ hexagon(r: number, orientation?: 'pointy' | 'flat'): NodeBuilder;
78
+ ellipse(rx: number, ry: number): NodeBuilder;
79
+ arc(r: number, startAngle: number, endAngle: number, closed?: boolean): NodeBuilder;
80
+ blockArrow(length: number, bodyWidth: number, headWidth: number, headLength: number, direction?: 'right' | 'left' | 'up' | 'down'): NodeBuilder;
81
+ callout(w: number, h: number, opts?: {
82
+ rx?: number;
83
+ pointerSide?: 'bottom' | 'top' | 'left' | 'right';
84
+ pointerHeight?: number;
85
+ pointerWidth?: number;
86
+ pointerPosition?: number;
87
+ }): NodeBuilder;
88
+ cloud(w: number, h: number): NodeBuilder;
89
+ cross(size: number, barWidth?: number): NodeBuilder;
90
+ cube(w: number, h: number, depth?: number): NodeBuilder;
91
+ path(d: string, w: number, h: number): NodeBuilder;
92
+ document(w: number, h: number, waveHeight?: number): NodeBuilder;
93
+ note(w: number, h: number, foldSize?: number): NodeBuilder;
94
+ parallelogram(w: number, h: number, skew?: number): NodeBuilder;
95
+ star(points: number, outerR: number, innerR?: number): NodeBuilder;
96
+ trapezoid(topW: number, bottomW: number, h: number): NodeBuilder;
97
+ triangle(w: number, h: number, direction?: 'up' | 'down' | 'left' | 'right'): NodeBuilder;
60
98
  label(text: string, opts?: Partial<NodeLabel>): NodeBuilder;
61
99
  fill(color: string): NodeBuilder;
62
100
  stroke(color: string, width?: number): NodeBuilder;
@@ -68,9 +106,23 @@ interface NodeBuilder {
68
106
  animateTo(props: AnimatableProps, opts: TweenOptions): NodeBuilder;
69
107
  data(payload: unknown): NodeBuilder;
70
108
  onClick(handler: (id: string, node: VizNode) => void): NodeBuilder;
109
+ /**
110
+ * Define a named connection port on the node.
111
+ * @param id Unique port id (e.g. `'top'`, `'out-1'`)
112
+ * @param offset Position relative to the node center `{ x, y }`
113
+ * @param direction Optional outgoing tangent in degrees (0=right, 90=down, 180=left, 270=up)
114
+ */
115
+ port(id: string, offset: {
116
+ x: number;
117
+ y: number;
118
+ }, direction?: number): NodeBuilder;
119
+ container(config?: ContainerConfig): NodeBuilder;
120
+ parent(parentId: string): NodeBuilder;
71
121
  done(): VizBuilder;
72
122
  node(id: string): NodeBuilder;
123
+ node(id: string, opts: NodeOptions): VizBuilder;
73
124
  edge(from: string, to: string, id?: string): EdgeBuilder;
125
+ edge(from: string, to: string, opts: EdgeOptions): VizBuilder;
74
126
  overlay(cb: (overlay: OverlayBuilder) => unknown): VizBuilder;
75
127
  overlay<K extends OverlayId>(id: K, params: OverlayParams<K>, key?: string): VizBuilder;
76
128
  overlay<T>(id: string, params: T, key?: string): VizBuilder;
@@ -79,9 +131,41 @@ interface NodeBuilder {
79
131
  }
80
132
  interface EdgeBuilder {
81
133
  straight(): EdgeBuilder;
134
+ curved(): EdgeBuilder;
135
+ orthogonal(): EdgeBuilder;
136
+ routing(mode: EdgeRouting): EdgeBuilder;
137
+ via(x: number, y: number): EdgeBuilder;
82
138
  label(text: string, opts?: Partial<EdgeLabel>): EdgeBuilder;
83
- arrow(enabled?: boolean): EdgeBuilder;
139
+ /**
140
+ * Set arrow markers. Convenience method.
141
+ * - `arrow(true)` or `arrow()` sets markerEnd to 'arrow'
142
+ * - `arrow(false)` sets markerEnd to 'none'
143
+ * - `arrow('both')` sets both markerStart and markerEnd to 'arrow'
144
+ * - `arrow('start')` sets markerStart to 'arrow'
145
+ * - `arrow('end')` sets markerEnd to 'arrow'
146
+ */
147
+ arrow(enabled?: boolean | 'both' | 'start' | 'end'): EdgeBuilder;
148
+ /** Set the marker type at the end (target) of the edge. */
149
+ markerEnd(type: EdgeMarkerType): EdgeBuilder;
150
+ /** Set the marker type at the start (source) of the edge. */
151
+ markerStart(type: EdgeMarkerType): EdgeBuilder;
152
+ /** Connect the edge to a specific port on the source node. */
153
+ fromPort(portId: string): EdgeBuilder;
154
+ /** Connect the edge to a specific port on the target node. */
155
+ toPort(portId: string): EdgeBuilder;
84
156
  connect(anchor: 'center' | 'boundary'): EdgeBuilder;
157
+ /** Sets the fill color of the edge path. */
158
+ fill(color: string): EdgeBuilder;
159
+ /** Sets the stroke color and optional width of the edge path. */
160
+ stroke(color: string, width?: number): EdgeBuilder;
161
+ /** Sets the opacity of the edge. */
162
+ opacity(value: number): EdgeBuilder;
163
+ /** Apply a dashed stroke pattern (`8, 4`). */
164
+ dashed(): EdgeBuilder;
165
+ /** Apply a dotted stroke pattern (`2, 4`). */
166
+ dotted(): EdgeBuilder;
167
+ /** Apply a custom SVG `stroke-dasharray` value, or a preset name (`'dashed'`, `'dotted'`, `'dash-dot'`). */
168
+ dash(pattern: 'solid' | 'dashed' | 'dotted' | 'dash-dot' | string): EdgeBuilder;
85
169
  class(name: string): EdgeBuilder;
86
170
  hitArea(px: number): EdgeBuilder;
87
171
  animate(type: string, config?: AnimationConfig): EdgeBuilder;
@@ -92,7 +176,9 @@ interface EdgeBuilder {
92
176
  onClick(handler: (id: string, edge: VizEdge) => void): EdgeBuilder;
93
177
  done(): VizBuilder;
94
178
  node(id: string): NodeBuilder;
179
+ node(id: string, opts: NodeOptions): VizBuilder;
95
180
  edge(from: string, to: string, id?: string): EdgeBuilder;
181
+ edge(from: string, to: string, opts: EdgeOptions): VizBuilder;
96
182
  overlay(cb: (overlay: OverlayBuilder) => unknown): VizBuilder;
97
183
  overlay<K extends OverlayId>(id: K, params: OverlayParams<K>, key?: string): VizBuilder;
98
184
  overlay<T>(id: string, params: T, key?: string): VizBuilder;