@elicdavis/node-flow 0.1.15 → 0.1.17

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.
Files changed (43) hide show
  1. package/.mocharc.json +5 -0
  2. package/dist/index.d.mts +139 -4
  3. package/dist/index.d.ts +139 -4
  4. package/dist/index.js +3754 -3352
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +3741 -3352
  7. package/dist/index.mjs.map +1 -1
  8. package/index.html +0 -30
  9. package/package.json +4 -2
  10. package/src/graph.ts +234 -41
  11. package/src/index.ts +82 -40
  12. package/src/input.ts +22 -14
  13. package/src/markdown/entry.ts +26 -3
  14. package/src/markdown/lexicalParser.ts +22 -32
  15. package/src/markdown/syntaxParser.ts +93 -68
  16. package/src/minimap.ts +178 -0
  17. package/src/node.ts +114 -0
  18. package/src/nodes/factory.ts +9 -5
  19. package/src/nodes/publisher.ts +39 -4
  20. package/src/nodes/subsystem.ts +218 -8
  21. package/src/notes/subsystem.ts +3 -0
  22. package/src/organize.ts +276 -181
  23. package/src/port.ts +143 -7
  24. package/src/render_scheduler.ts +49 -0
  25. package/src/styles/text.ts +8 -0
  26. package/src/theme.ts +8 -0
  27. package/src/types/text.ts +8 -4
  28. package/src/utils/string.ts +7 -1
  29. package/src/widgets/color.ts +5 -6
  30. package/src/widgets/factory.ts +1 -1
  31. package/src/widgets/image.ts +2 -0
  32. package/src/widgets/number.ts +2 -2
  33. package/src/widgets/slider.ts +2 -2
  34. package/src/widgets/string.ts +2 -2
  35. package/src/widgets/toggle.ts +2 -2
  36. package/src/widgets/widget.ts +22 -0
  37. package/test/markdown.test.ts +165 -0
  38. package/test/minimap.test.ts +103 -0
  39. package/test/node_menu_filter.test.ts +119 -0
  40. package/test/organize.test.ts +156 -0
  41. package/test/port.test.ts +125 -0
  42. package/test/render_scheduler.test.ts +119 -0
  43. package/tsconfig.json +6 -2
package/.mocharc.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "require": "ts-node/register",
3
+ "spec": "test/**/*.test.ts",
4
+ "extension": ["ts"]
5
+ }
package/dist/index.d.mts CHANGED
@@ -72,6 +72,7 @@ interface ConnectionRendererParams {
72
72
  outPort: Port | null;
73
73
  }
74
74
  type ConnectionRenderer = (params: ConnectionRendererParams) => void;
75
+ declare function DefaultConnectionRenderer(connectionSize: number, connectionColor: string | undefined, mouseOverSize: number, mouseOverColor: string | undefined): ConnectionRenderer;
75
76
  declare class Connection {
76
77
  #private;
77
78
  constructor(inNode: FlowNode | null, inNodePortIndex: number, outNode: FlowNode | null, outNodePortIndex: number, renderer: ConnectionRenderer);
@@ -170,14 +171,45 @@ interface PortConfig {
170
171
  type?: string;
171
172
  description?: string;
172
173
  array?: boolean;
174
+ /**
175
+ * Lets this port connect to a port of any type, instead of only to one
176
+ * declaring the same `type` string. For a port whose type is decided by
177
+ * whatever gets wired into it, where `type` is a placeholder shown to
178
+ * the user rather than a promise about the values passing through.
179
+ */
180
+ anyType?: boolean;
181
+ /**
182
+ * Every type this port takes, when it takes more than one. For a port
183
+ * that works on a single value or an array of them, where `type` is
184
+ * only the one it happens to be showing.
185
+ *
186
+ * An empty or absent list means the port takes exactly its `type`,
187
+ * unless `anyType` says otherwise.
188
+ */
189
+ acceptedTypes?: Array<string>;
173
190
  emptyStyle?: PortStyle;
174
191
  filledStyle?: PortStyle;
175
192
  onConnectionAdded?: ConnectionChangeCallback;
176
193
  onConnectionRemoved?: ConnectionChangeCallback;
177
194
  }
195
+ /**
196
+ * Whether two ports may be connected, going by their types alone. Each port
197
+ * offers the set of types it takes - one entry for an ordinary port, several
198
+ * for a port that takes more than one, and none at all for a port that takes
199
+ * anything. They connect when those sets overlap.
200
+ */
201
+ declare function portsCompatible(a: Port | undefined | null, b: Port | undefined | null): boolean;
202
+ declare function typeSetsCompatible(a: Array<string>, b: Array<string>): boolean;
203
+ declare function portConfigTypes(config: PortConfig): Array<string>;
178
204
  declare class Port {
179
205
  #private;
180
206
  constructor(node: FlowNode, portType: PortType, config?: PortConfig);
207
+ setDataType(type: string): void;
208
+ setAnyType(anyType: boolean): void;
209
+ /**
210
+ * Scope accepted types to singular type
211
+ */
212
+ settleOn(type: string): void;
181
213
  addConnection(connection: Connection): void;
182
214
  replaceConnection(connection: Connection, index: number): void;
183
215
  addConnectionAddedListener(callback: ConnectionChangeCallback): void;
@@ -185,6 +217,12 @@ declare class Port {
185
217
  addConnectionRemovedListener(callback: ConnectionChangeCallback): void;
186
218
  clearConnection(connection: Connection): void;
187
219
  getDataType(): string;
220
+ acceptsAnyType(): boolean;
221
+ /**
222
+ * Every type this port takes. Empty means it takes anything.
223
+ */
224
+ acceptedTypes(): Array<string>;
225
+ setAcceptedTypes(types: Array<string>): void;
188
226
  getPortType(): PortType;
189
227
  getDisplayName(): string;
190
228
  filledStyleColor(): string;
@@ -313,8 +351,14 @@ declare class FlowNode {
313
351
  addInput(config: PortConfig): Port;
314
352
  addOutput(config: PortConfig): Port;
315
353
  addWidget(widget: Widget): void;
354
+ insertWidget(index: number, widget: Widget): void;
316
355
  removeWidget(widget: Widget): void;
356
+ removeWidgetAt(index: number): Widget | undefined;
357
+ moveWidget(from: number, to: number): void;
358
+ clearWidgets(): void;
317
359
  getWidget(index: number): Widget;
360
+ widgets(): ReadonlyArray<Widget>;
361
+ indexOfWidget(widget: Widget): number;
318
362
  widgetCount(): number;
319
363
  translate(delta: Vector2): void;
320
364
  inBounds(ctx: CanvasRenderingContext2D, camera: Camera, position: Vector2): NodeIntersection;
@@ -331,11 +375,36 @@ declare class FlowNode {
331
375
  outputs(): number;
332
376
  addMessage(message: NodeMessageConfig): void;
333
377
  clearMessages(): void;
378
+ hasBeenLaidOut(): boolean;
334
379
  render(ctx: CanvasRenderingContext2D, camera: Camera, state: NodeState, mousePosition: Vector2 | undefined, postProcess: PassSubsystem): void;
380
+ layoutPortsWithoutDrawing(bounds: Box, zoom: number): boolean;
335
381
  }
336
382
 
337
383
  type NodeAddedCallback = (node: FlowNode) => void;
338
384
  type NodeRemovedCallback = (node: FlowNode) => void;
385
+ /**
386
+ * A connection between two nodes of a copied set, by their position in that
387
+ * set rather than by node, so it can be rebuilt against the copies.
388
+ */
389
+ interface InternalConnection {
390
+ fromNode: number;
391
+ fromPort: number;
392
+ toNode: number;
393
+ toPort: number;
394
+ }
395
+ /**
396
+ * A connection that was dragged out and dropped on empty canvas. Describes
397
+ * what a node would have to offer to finish it, and joins the two up once
398
+ * one has been made.
399
+ */
400
+ interface DanglingConnection {
401
+ /** The side the new node has to offer. */
402
+ needs: PortType;
403
+ /** The types that side has to accept. Empty takes anything. */
404
+ types: Array<string>;
405
+ /** Wires the new node to the port the drag started from. */
406
+ connectTo: (node: FlowNode) => void;
407
+ }
339
408
  interface ConnectionRendererConfiguration {
340
409
  size?: number;
341
410
  color?: string;
@@ -358,6 +427,8 @@ declare class NodeSubsystem {
358
427
  clickStart(mousePosition: Vector2, camera: Camera, ctrlKey: boolean): boolean;
359
428
  mouseDragEvent(delta: Vector2, scale: number): boolean;
360
429
  clearNodeInputConnection(node: FlowNode, index: number): void;
430
+ connectionsWithin(nodes: Array<FlowNode>): Array<InternalConnection>;
431
+ setConnectionReleasedHandler(handler: (dangling: DanglingConnection) => void): void;
361
432
  clickEnd(): void;
362
433
  connectNodes(nodeOut: FlowNode, outPort: number, nodeIn: FlowNode, inPort: number): Connection | undefined;
363
434
  getNodes(): Array<FlowNode>;
@@ -380,9 +451,26 @@ declare class NodeSubsystem {
380
451
  removeNode(node: FlowNode): void;
381
452
  organize(ctx: CanvasRenderingContext2D): void;
382
453
  openContextMenu(ctx: CanvasRenderingContext2D, position: Vector2): ContextMenuConfig | null;
454
+ /** Drops the selection entirely. */
455
+ unselectAllNodes(): void;
456
+ /**
457
+ * Removes every selected node, and the connections that touched them.
458
+ */
459
+ removeSelectedNodes(): Array<FlowNode>;
383
460
  render(ctx: CanvasRenderingContext2D, camera: Camera, mousePosition: Vector2 | undefined): RenderResults | undefined;
384
461
  }
385
462
 
463
+ /**
464
+ * Narrows a "new node" menu to the nodes that could actually be wired to a
465
+ * particular port.
466
+ */
467
+ interface NodeMenuFilter {
468
+ /** Which side the new node has to offer. */
469
+ needs: PortType;
470
+ /** Types that side has to accept. An empty list takes anything. */
471
+ types: Array<string>;
472
+ onCreated?: (node: FlowNode) => void;
473
+ }
386
474
  interface PublisherNodes {
387
475
  [name: string]: FlowNodeConfig;
388
476
  }
@@ -398,7 +486,7 @@ declare class Publisher {
398
486
  nodes(): Map<string, FlowNodeConfig>;
399
487
  register(nodeType: string, config: FlowNodeConfig): void;
400
488
  unregister(nodeType: string): boolean;
401
- contextMenu(graph: NodeSubsystem, position: Vector2): ContextMenuConfig;
489
+ contextMenu(graph: NodeSubsystem, position: Vector2, filter?: NodeMenuFilter): ContextMenuConfig;
402
490
  create(nodeType: string): FlowNode;
403
491
  }
404
492
 
@@ -416,8 +504,8 @@ declare class NodeFactory {
416
504
  addOnNodeCreatedListener(callback: NodeCreatedCallback): void;
417
505
  addPublisher(identifier: string, publisher: Publisher): void;
418
506
  create(publisher: string, nodeType: string): FlowNode;
419
- newNodeSubmenus(graph: NodeSubsystem, position: Vector2): Array<ContextMenuConfig>;
420
- openMenu(graph: NodeSubsystem, position: Vector2): ContextMenuConfig;
507
+ newNodeSubmenus(graph: NodeSubsystem, position: Vector2, filter?: NodeMenuFilter): Array<ContextMenuConfig>;
508
+ openMenu(graph: NodeSubsystem, position: Vector2, filter?: NodeMenuFilter): ContextMenuConfig;
421
509
  }
422
510
 
423
511
  type NoteContentChangeCallback = (node: FlowNote, newContents: string) => void;
@@ -476,7 +564,33 @@ interface NoteSubsystemConfig {
476
564
  notes?: Array<FlowNoteConfig>;
477
565
  }
478
566
 
567
+ interface MinimapConfig {
568
+ enabled?: boolean;
569
+ width?: number;
570
+ height?: number;
571
+ margin?: number;
572
+ /** Overrides Theme.Minimap.BackgroundColor for this minimap alone. */
573
+ backgroundColor?: string;
574
+ /** Overrides Theme.Minimap.BorderColor for this minimap alone. */
575
+ borderColor?: string;
576
+ /** Overrides Theme.Minimap.NodeColor for this minimap alone. */
577
+ nodeColor?: string;
578
+ /** Overrides Theme.Minimap.ViewportColor for this minimap alone. */
579
+ viewportColor?: string;
580
+ }
581
+ declare class Minimap {
582
+ #private;
583
+ constructor(config?: MinimapConfig);
584
+ enabled(): boolean;
585
+ setEnabled(enabled: boolean): void;
586
+ contains(position: Vector2): boolean;
587
+ graphPositionOf(position: Vector2, out: Vector2): void;
588
+ render(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, camera: Camera, nodes: Array<FlowNode>): void;
589
+ }
590
+
479
591
  type GraphRenderer = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, position: Vector2, scale: number) => void;
592
+ type CopyCallback = (nodes: Array<FlowNode>, connections: Array<InternalConnection>) => void;
593
+ type PasteCallback = (position: Vector2) => void;
480
594
  interface FlowNodeGraphConfiguration {
481
595
  backgroundRenderer?: GraphRenderer;
482
596
  backgroundColor?: string;
@@ -484,6 +598,7 @@ interface FlowNodeGraphConfiguration {
484
598
  contextMenu?: ContextMenuConfig;
485
599
  nodes?: NodeFactoryConfig;
486
600
  board?: NoteSubsystemConfig;
601
+ minimap?: MinimapConfig;
487
602
  }
488
603
  declare class GraphView {
489
604
  #private;
@@ -498,11 +613,15 @@ declare class GraphView {
498
613
  declare class NodeFlowGraph {
499
614
  #private;
500
615
  constructor(canvas: HTMLCanvasElement, config?: FlowNodeGraphConfiguration);
616
+ addCopyListener(callback: CopyCallback): void;
617
+ addPasteListener(callback: PasteCallback): void;
501
618
  addNoteAddedListener(callback: NoteAddedCallback): void;
502
619
  addNoteRemovedListener(callback: NoteRemovedCallback): void;
503
620
  addNoteDragStartListener(callback: NoteDragStartCallback): void;
504
621
  addNoteDragStopListener(callback: NoteDragStopCallback): void;
505
622
  zoom(amount: number, anchor?: Vector2): void;
623
+ /** The minimap, so it can be turned on and sized by the host. */
624
+ minimap(): Minimap;
506
625
  currentView(): GraphView;
507
626
  organize(): void;
508
627
  addPublisher(identifier: string, publisher: Publisher): void;
@@ -512,6 +631,8 @@ declare class NodeFlowGraph {
512
631
  * state used by Ctrl+click and box-select styling via FlowNode.selected()).
513
632
  */
514
633
  getSelectedNodes(): Array<FlowNode>;
634
+ unselectAllNodes(): void;
635
+ removeSelectedNodes(): Array<FlowNode>;
515
636
  /**
516
637
  * Returns a copy of the current camera orientation (screen-space pan + zoom).
517
638
  */
@@ -541,6 +662,9 @@ declare class NodeFlowGraph {
541
662
  addOnNodeCreatedListener(callback: NodeCreatedCallback): void;
542
663
  addOnNodeAddedListener(callback: NodeAddedCallback): void;
543
664
  addOnNodeRemovedListener(callback: NodeRemovedCallback): void;
665
+ requestRender(): void;
666
+ renderContinuously(): () => void;
667
+ dispose(): void;
544
668
  }
545
669
 
546
670
  declare const Theme: {
@@ -604,6 +728,14 @@ declare const Theme: {
604
728
  FontColor: string;
605
729
  DisabledFontColor: string;
606
730
  };
731
+ Minimap: {
732
+ BackgroundColor: string;
733
+ BorderColor: string;
734
+ BorderRadius: number;
735
+ Padding: number;
736
+ NodeColor: string;
737
+ ViewportColor: string;
738
+ };
607
739
  Note: {
608
740
  FontColor: string;
609
741
  FontSize: number;
@@ -628,6 +760,9 @@ declare const Theme: {
628
760
  };
629
761
  };
630
762
 
763
+ declare function requestRender(): void;
764
+ declare function renderContinuously(): () => void;
765
+
631
766
  interface TextBoxStyleConfig {
632
767
  box?: BoxStyleConfig;
633
768
  text?: TextStyleConfig;
@@ -847,4 +982,4 @@ declare class WidgetFactory {
847
982
  }
848
983
  declare const GlobalWidgetFactory: WidgetFactory;
849
984
 
850
- export { ButtonWidget, type CameraOrientation, ColorWidget, type ContextMenuConfig, type ContextMenuItemConfig, ContextMenuItemState, FlowNode, type FlowNodeConfig, type FlowNodeGraphConfiguration, type FlowNodeStyle, type FlowNodeTitleConfig, FlowNote, type FlowNoteConfig, GlobalWidgetFactory, type GraphRenderer, ImageWidget, MessageType, NodeFlowGraph, type NodeIntersection, type NodeMessageConfig, NodeState, type NoteAddedCallback, type NoteContentChangeCallback, type NoteDragStartCallback, type NoteDragStopCallback, type NoteRemovedCallback, type NoteSubsystemConfig, type NoteWidthChangeCallback, NumberWidget, Publisher, type PublisherConfig, SliderWidget, StringWidget, TextWidget, Theme, ToggleWidget, type WidgetConfig };
985
+ export { type Box, ButtonWidget, type ButtonWidgetConfig, type CameraOrientation, ColorWidget, type ColorWidgetConfig, Connection, type ConnectionRenderer, type ConnectionRendererParams, type ContextMenuConfig, type ContextMenuItemConfig, ContextMenuItemState, type CopyCallback, type DanglingConnection, DefaultConnectionRenderer, FlowNode, type FlowNodeConfig, type FlowNodeGraphConfiguration, type FlowNodeStyle, type FlowNodeTitleConfig, FlowNote, type FlowNoteConfig, GlobalWidgetFactory, type GraphRenderer, ImageWidget, type ImageWidgetConfig, type InternalConnection, MessageType, Minimap, type MinimapConfig, type NodeCreatedCallback, NodeFactory, type NodeFactoryConfig, NodeFlowGraph, type NodeIntersection, type NodeMenuFilter, type NodeMessageConfig, NodeState, type NoteAddedCallback, type NoteContentChangeCallback, type NoteDragStartCallback, type NoteDragStopCallback, type NoteRemovedCallback, type NoteSubsystemConfig, type NoteWidthChangeCallback, NumberWidget, type NumberWidgetConfig, type PasteCallback, Port, type PortConfig, type PortStyle, PortType, Publisher, type PublisherConfig, SliderWidget, type SliderWidgetConfig, StringWidget, type StringWidgetConfig, TextWidget, type TextWidgetConfig, Theme, type ToggleStyleConfig, ToggleWidget, type ToggleWidgetConfig, type Vector2, type Widget, type WidgetBuilder, type WidgetConfig, WidgetFactory, portConfigTypes, portsCompatible, renderContinuously, requestRender, typeSetsCompatible };
package/dist/index.d.ts CHANGED
@@ -72,6 +72,7 @@ interface ConnectionRendererParams {
72
72
  outPort: Port | null;
73
73
  }
74
74
  type ConnectionRenderer = (params: ConnectionRendererParams) => void;
75
+ declare function DefaultConnectionRenderer(connectionSize: number, connectionColor: string | undefined, mouseOverSize: number, mouseOverColor: string | undefined): ConnectionRenderer;
75
76
  declare class Connection {
76
77
  #private;
77
78
  constructor(inNode: FlowNode | null, inNodePortIndex: number, outNode: FlowNode | null, outNodePortIndex: number, renderer: ConnectionRenderer);
@@ -170,14 +171,45 @@ interface PortConfig {
170
171
  type?: string;
171
172
  description?: string;
172
173
  array?: boolean;
174
+ /**
175
+ * Lets this port connect to a port of any type, instead of only to one
176
+ * declaring the same `type` string. For a port whose type is decided by
177
+ * whatever gets wired into it, where `type` is a placeholder shown to
178
+ * the user rather than a promise about the values passing through.
179
+ */
180
+ anyType?: boolean;
181
+ /**
182
+ * Every type this port takes, when it takes more than one. For a port
183
+ * that works on a single value or an array of them, where `type` is
184
+ * only the one it happens to be showing.
185
+ *
186
+ * An empty or absent list means the port takes exactly its `type`,
187
+ * unless `anyType` says otherwise.
188
+ */
189
+ acceptedTypes?: Array<string>;
173
190
  emptyStyle?: PortStyle;
174
191
  filledStyle?: PortStyle;
175
192
  onConnectionAdded?: ConnectionChangeCallback;
176
193
  onConnectionRemoved?: ConnectionChangeCallback;
177
194
  }
195
+ /**
196
+ * Whether two ports may be connected, going by their types alone. Each port
197
+ * offers the set of types it takes - one entry for an ordinary port, several
198
+ * for a port that takes more than one, and none at all for a port that takes
199
+ * anything. They connect when those sets overlap.
200
+ */
201
+ declare function portsCompatible(a: Port | undefined | null, b: Port | undefined | null): boolean;
202
+ declare function typeSetsCompatible(a: Array<string>, b: Array<string>): boolean;
203
+ declare function portConfigTypes(config: PortConfig): Array<string>;
178
204
  declare class Port {
179
205
  #private;
180
206
  constructor(node: FlowNode, portType: PortType, config?: PortConfig);
207
+ setDataType(type: string): void;
208
+ setAnyType(anyType: boolean): void;
209
+ /**
210
+ * Scope accepted types to singular type
211
+ */
212
+ settleOn(type: string): void;
181
213
  addConnection(connection: Connection): void;
182
214
  replaceConnection(connection: Connection, index: number): void;
183
215
  addConnectionAddedListener(callback: ConnectionChangeCallback): void;
@@ -185,6 +217,12 @@ declare class Port {
185
217
  addConnectionRemovedListener(callback: ConnectionChangeCallback): void;
186
218
  clearConnection(connection: Connection): void;
187
219
  getDataType(): string;
220
+ acceptsAnyType(): boolean;
221
+ /**
222
+ * Every type this port takes. Empty means it takes anything.
223
+ */
224
+ acceptedTypes(): Array<string>;
225
+ setAcceptedTypes(types: Array<string>): void;
188
226
  getPortType(): PortType;
189
227
  getDisplayName(): string;
190
228
  filledStyleColor(): string;
@@ -313,8 +351,14 @@ declare class FlowNode {
313
351
  addInput(config: PortConfig): Port;
314
352
  addOutput(config: PortConfig): Port;
315
353
  addWidget(widget: Widget): void;
354
+ insertWidget(index: number, widget: Widget): void;
316
355
  removeWidget(widget: Widget): void;
356
+ removeWidgetAt(index: number): Widget | undefined;
357
+ moveWidget(from: number, to: number): void;
358
+ clearWidgets(): void;
317
359
  getWidget(index: number): Widget;
360
+ widgets(): ReadonlyArray<Widget>;
361
+ indexOfWidget(widget: Widget): number;
318
362
  widgetCount(): number;
319
363
  translate(delta: Vector2): void;
320
364
  inBounds(ctx: CanvasRenderingContext2D, camera: Camera, position: Vector2): NodeIntersection;
@@ -331,11 +375,36 @@ declare class FlowNode {
331
375
  outputs(): number;
332
376
  addMessage(message: NodeMessageConfig): void;
333
377
  clearMessages(): void;
378
+ hasBeenLaidOut(): boolean;
334
379
  render(ctx: CanvasRenderingContext2D, camera: Camera, state: NodeState, mousePosition: Vector2 | undefined, postProcess: PassSubsystem): void;
380
+ layoutPortsWithoutDrawing(bounds: Box, zoom: number): boolean;
335
381
  }
336
382
 
337
383
  type NodeAddedCallback = (node: FlowNode) => void;
338
384
  type NodeRemovedCallback = (node: FlowNode) => void;
385
+ /**
386
+ * A connection between two nodes of a copied set, by their position in that
387
+ * set rather than by node, so it can be rebuilt against the copies.
388
+ */
389
+ interface InternalConnection {
390
+ fromNode: number;
391
+ fromPort: number;
392
+ toNode: number;
393
+ toPort: number;
394
+ }
395
+ /**
396
+ * A connection that was dragged out and dropped on empty canvas. Describes
397
+ * what a node would have to offer to finish it, and joins the two up once
398
+ * one has been made.
399
+ */
400
+ interface DanglingConnection {
401
+ /** The side the new node has to offer. */
402
+ needs: PortType;
403
+ /** The types that side has to accept. Empty takes anything. */
404
+ types: Array<string>;
405
+ /** Wires the new node to the port the drag started from. */
406
+ connectTo: (node: FlowNode) => void;
407
+ }
339
408
  interface ConnectionRendererConfiguration {
340
409
  size?: number;
341
410
  color?: string;
@@ -358,6 +427,8 @@ declare class NodeSubsystem {
358
427
  clickStart(mousePosition: Vector2, camera: Camera, ctrlKey: boolean): boolean;
359
428
  mouseDragEvent(delta: Vector2, scale: number): boolean;
360
429
  clearNodeInputConnection(node: FlowNode, index: number): void;
430
+ connectionsWithin(nodes: Array<FlowNode>): Array<InternalConnection>;
431
+ setConnectionReleasedHandler(handler: (dangling: DanglingConnection) => void): void;
361
432
  clickEnd(): void;
362
433
  connectNodes(nodeOut: FlowNode, outPort: number, nodeIn: FlowNode, inPort: number): Connection | undefined;
363
434
  getNodes(): Array<FlowNode>;
@@ -380,9 +451,26 @@ declare class NodeSubsystem {
380
451
  removeNode(node: FlowNode): void;
381
452
  organize(ctx: CanvasRenderingContext2D): void;
382
453
  openContextMenu(ctx: CanvasRenderingContext2D, position: Vector2): ContextMenuConfig | null;
454
+ /** Drops the selection entirely. */
455
+ unselectAllNodes(): void;
456
+ /**
457
+ * Removes every selected node, and the connections that touched them.
458
+ */
459
+ removeSelectedNodes(): Array<FlowNode>;
383
460
  render(ctx: CanvasRenderingContext2D, camera: Camera, mousePosition: Vector2 | undefined): RenderResults | undefined;
384
461
  }
385
462
 
463
+ /**
464
+ * Narrows a "new node" menu to the nodes that could actually be wired to a
465
+ * particular port.
466
+ */
467
+ interface NodeMenuFilter {
468
+ /** Which side the new node has to offer. */
469
+ needs: PortType;
470
+ /** Types that side has to accept. An empty list takes anything. */
471
+ types: Array<string>;
472
+ onCreated?: (node: FlowNode) => void;
473
+ }
386
474
  interface PublisherNodes {
387
475
  [name: string]: FlowNodeConfig;
388
476
  }
@@ -398,7 +486,7 @@ declare class Publisher {
398
486
  nodes(): Map<string, FlowNodeConfig>;
399
487
  register(nodeType: string, config: FlowNodeConfig): void;
400
488
  unregister(nodeType: string): boolean;
401
- contextMenu(graph: NodeSubsystem, position: Vector2): ContextMenuConfig;
489
+ contextMenu(graph: NodeSubsystem, position: Vector2, filter?: NodeMenuFilter): ContextMenuConfig;
402
490
  create(nodeType: string): FlowNode;
403
491
  }
404
492
 
@@ -416,8 +504,8 @@ declare class NodeFactory {
416
504
  addOnNodeCreatedListener(callback: NodeCreatedCallback): void;
417
505
  addPublisher(identifier: string, publisher: Publisher): void;
418
506
  create(publisher: string, nodeType: string): FlowNode;
419
- newNodeSubmenus(graph: NodeSubsystem, position: Vector2): Array<ContextMenuConfig>;
420
- openMenu(graph: NodeSubsystem, position: Vector2): ContextMenuConfig;
507
+ newNodeSubmenus(graph: NodeSubsystem, position: Vector2, filter?: NodeMenuFilter): Array<ContextMenuConfig>;
508
+ openMenu(graph: NodeSubsystem, position: Vector2, filter?: NodeMenuFilter): ContextMenuConfig;
421
509
  }
422
510
 
423
511
  type NoteContentChangeCallback = (node: FlowNote, newContents: string) => void;
@@ -476,7 +564,33 @@ interface NoteSubsystemConfig {
476
564
  notes?: Array<FlowNoteConfig>;
477
565
  }
478
566
 
567
+ interface MinimapConfig {
568
+ enabled?: boolean;
569
+ width?: number;
570
+ height?: number;
571
+ margin?: number;
572
+ /** Overrides Theme.Minimap.BackgroundColor for this minimap alone. */
573
+ backgroundColor?: string;
574
+ /** Overrides Theme.Minimap.BorderColor for this minimap alone. */
575
+ borderColor?: string;
576
+ /** Overrides Theme.Minimap.NodeColor for this minimap alone. */
577
+ nodeColor?: string;
578
+ /** Overrides Theme.Minimap.ViewportColor for this minimap alone. */
579
+ viewportColor?: string;
580
+ }
581
+ declare class Minimap {
582
+ #private;
583
+ constructor(config?: MinimapConfig);
584
+ enabled(): boolean;
585
+ setEnabled(enabled: boolean): void;
586
+ contains(position: Vector2): boolean;
587
+ graphPositionOf(position: Vector2, out: Vector2): void;
588
+ render(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, camera: Camera, nodes: Array<FlowNode>): void;
589
+ }
590
+
479
591
  type GraphRenderer = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, position: Vector2, scale: number) => void;
592
+ type CopyCallback = (nodes: Array<FlowNode>, connections: Array<InternalConnection>) => void;
593
+ type PasteCallback = (position: Vector2) => void;
480
594
  interface FlowNodeGraphConfiguration {
481
595
  backgroundRenderer?: GraphRenderer;
482
596
  backgroundColor?: string;
@@ -484,6 +598,7 @@ interface FlowNodeGraphConfiguration {
484
598
  contextMenu?: ContextMenuConfig;
485
599
  nodes?: NodeFactoryConfig;
486
600
  board?: NoteSubsystemConfig;
601
+ minimap?: MinimapConfig;
487
602
  }
488
603
  declare class GraphView {
489
604
  #private;
@@ -498,11 +613,15 @@ declare class GraphView {
498
613
  declare class NodeFlowGraph {
499
614
  #private;
500
615
  constructor(canvas: HTMLCanvasElement, config?: FlowNodeGraphConfiguration);
616
+ addCopyListener(callback: CopyCallback): void;
617
+ addPasteListener(callback: PasteCallback): void;
501
618
  addNoteAddedListener(callback: NoteAddedCallback): void;
502
619
  addNoteRemovedListener(callback: NoteRemovedCallback): void;
503
620
  addNoteDragStartListener(callback: NoteDragStartCallback): void;
504
621
  addNoteDragStopListener(callback: NoteDragStopCallback): void;
505
622
  zoom(amount: number, anchor?: Vector2): void;
623
+ /** The minimap, so it can be turned on and sized by the host. */
624
+ minimap(): Minimap;
506
625
  currentView(): GraphView;
507
626
  organize(): void;
508
627
  addPublisher(identifier: string, publisher: Publisher): void;
@@ -512,6 +631,8 @@ declare class NodeFlowGraph {
512
631
  * state used by Ctrl+click and box-select styling via FlowNode.selected()).
513
632
  */
514
633
  getSelectedNodes(): Array<FlowNode>;
634
+ unselectAllNodes(): void;
635
+ removeSelectedNodes(): Array<FlowNode>;
515
636
  /**
516
637
  * Returns a copy of the current camera orientation (screen-space pan + zoom).
517
638
  */
@@ -541,6 +662,9 @@ declare class NodeFlowGraph {
541
662
  addOnNodeCreatedListener(callback: NodeCreatedCallback): void;
542
663
  addOnNodeAddedListener(callback: NodeAddedCallback): void;
543
664
  addOnNodeRemovedListener(callback: NodeRemovedCallback): void;
665
+ requestRender(): void;
666
+ renderContinuously(): () => void;
667
+ dispose(): void;
544
668
  }
545
669
 
546
670
  declare const Theme: {
@@ -604,6 +728,14 @@ declare const Theme: {
604
728
  FontColor: string;
605
729
  DisabledFontColor: string;
606
730
  };
731
+ Minimap: {
732
+ BackgroundColor: string;
733
+ BorderColor: string;
734
+ BorderRadius: number;
735
+ Padding: number;
736
+ NodeColor: string;
737
+ ViewportColor: string;
738
+ };
607
739
  Note: {
608
740
  FontColor: string;
609
741
  FontSize: number;
@@ -628,6 +760,9 @@ declare const Theme: {
628
760
  };
629
761
  };
630
762
 
763
+ declare function requestRender(): void;
764
+ declare function renderContinuously(): () => void;
765
+
631
766
  interface TextBoxStyleConfig {
632
767
  box?: BoxStyleConfig;
633
768
  text?: TextStyleConfig;
@@ -847,4 +982,4 @@ declare class WidgetFactory {
847
982
  }
848
983
  declare const GlobalWidgetFactory: WidgetFactory;
849
984
 
850
- export { ButtonWidget, type CameraOrientation, ColorWidget, type ContextMenuConfig, type ContextMenuItemConfig, ContextMenuItemState, FlowNode, type FlowNodeConfig, type FlowNodeGraphConfiguration, type FlowNodeStyle, type FlowNodeTitleConfig, FlowNote, type FlowNoteConfig, GlobalWidgetFactory, type GraphRenderer, ImageWidget, MessageType, NodeFlowGraph, type NodeIntersection, type NodeMessageConfig, NodeState, type NoteAddedCallback, type NoteContentChangeCallback, type NoteDragStartCallback, type NoteDragStopCallback, type NoteRemovedCallback, type NoteSubsystemConfig, type NoteWidthChangeCallback, NumberWidget, Publisher, type PublisherConfig, SliderWidget, StringWidget, TextWidget, Theme, ToggleWidget, type WidgetConfig };
985
+ export { type Box, ButtonWidget, type ButtonWidgetConfig, type CameraOrientation, ColorWidget, type ColorWidgetConfig, Connection, type ConnectionRenderer, type ConnectionRendererParams, type ContextMenuConfig, type ContextMenuItemConfig, ContextMenuItemState, type CopyCallback, type DanglingConnection, DefaultConnectionRenderer, FlowNode, type FlowNodeConfig, type FlowNodeGraphConfiguration, type FlowNodeStyle, type FlowNodeTitleConfig, FlowNote, type FlowNoteConfig, GlobalWidgetFactory, type GraphRenderer, ImageWidget, type ImageWidgetConfig, type InternalConnection, MessageType, Minimap, type MinimapConfig, type NodeCreatedCallback, NodeFactory, type NodeFactoryConfig, NodeFlowGraph, type NodeIntersection, type NodeMenuFilter, type NodeMessageConfig, NodeState, type NoteAddedCallback, type NoteContentChangeCallback, type NoteDragStartCallback, type NoteDragStopCallback, type NoteRemovedCallback, type NoteSubsystemConfig, type NoteWidthChangeCallback, NumberWidget, type NumberWidgetConfig, type PasteCallback, Port, type PortConfig, type PortStyle, PortType, Publisher, type PublisherConfig, SliderWidget, type SliderWidgetConfig, StringWidget, type StringWidgetConfig, TextWidget, type TextWidgetConfig, Theme, type ToggleStyleConfig, ToggleWidget, type ToggleWidgetConfig, type Vector2, type Widget, type WidgetBuilder, type WidgetConfig, WidgetFactory, portConfigTypes, portsCompatible, renderContinuously, requestRender, typeSetsCompatible };