Inital commit

master
Jeremy Hayes 2026-06-05 11:13:33 -05:00
commit cfd64ddd02
2 changed files with 221 additions and 0 deletions

191
Readme.md Normal file
View File

@ -0,0 +1,191 @@
# NodeEditor.js API Documentation
`NodeEditor.js` is a lightweight, framework-agnostic, zero-dependency node editor library written in pure ES6 JavaScript. It leverages standard DOM elements for high-performance node manipulation and an SVG overlay plane for drawing scalable Bezier connections.
---
## 🚀 Quick Start
### 1. HTML Setup
Create a dedicated target container for the editor. Ensure the container has explicit dimensional bounds (`width` and `height`).
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Node Editor Instance</title>
<style>
body { margin: 0; background: #111; }
#editor-container {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="editor-container"></div>
<script type="module" src="./app.js"></script>
</body>
</html>
```
### 2. JavaScript Initialization (`app.js`)
Import the `NodeEditor` module, hook it to your DOM node, and instantiate your first pipeline.
```javascript
import { NodeEditor } from './NodeEditor.js';
// 1. Initialize the UI Engine
const container = document.getElementById('editor-container');
const editor = new NodeEditor(container);
// 2. Extract the State Manager
const graph = editor.getGraphManager();
// 3. Spawning Nodes
const nodeA = graph.addNode({
type: 'source_node',
title: 'Data Stream',
x: 100,
y: 150,
inputs: [],
outputs: [{ id: 'out_raw', name: 'Raw Buffer' }]
});
const nodeB = graph.addNode({
type: 'processing_node',
title: 'Transform Matrix',
x: 450,
y: 250,
inputs: [{ id: 'in_data', name: 'Payload' }],
outputs: [{ id: 'out_success', name: 'Success' }]
});
// 4. Create a programmatically driven wire connection
graph.connect(nodeA.id, 'out_raw', nodeB.id, 'in_data');
```
---
## 🛠️ Architecture Overview
The library enforces a strict separation of concerns via an asynchronous, decoupled state cycle:
```
[User Action / UI Pointer Interaction]
[GraphManager State Engine] ◄─── (Single Source of Truth)
▼ (Dispatches Event Hub Actions)
[NodeEditor Render Architecture]
┌──────────┴──────────┐
▼ ▼
[DOM Element Nodes] [SVG Vector Overlay Patches]
```
---
## 📚 API Reference
### `NodeEditor` (UI Controller)
The wrapper orchestration manager that structures layout elements, listens to user viewport transforms, and monitors state bindings.
* **`new NodeEditor(containerElement)`**
Instantiates a fresh node pipeline map inside a target DOM container.
* **`editor.getGraphManager()`**
Returns the underlying data `GraphManager` engine operating behind the target instance.
* **`editor.updateAllConnections()`**
Forces an absolute layout recalculation of all connection nodes in view. Useful if you change node positions outside the lifecycle API or resize your external container dynamically.
---
### `GraphManager` (State Controller)
The isolated data engine that processes, updates, validates, and serializes graph states.
#### Node Management APIs
* **`addNode(config)`**
Registers a new data node. Returns the instantiated `NodeSchema` object.
* `config.title`: (String) Header title text.
* `config.x` / `config.y`: (Numbers) Absolute starting canvas positioning.
* `config.inputs` / `config.outputs`: Array of port objects (`{ id: 'unique_port_id', name: 'Display Name' }`).
* `config.id`: *(Optional)* Provide a deterministic string ID; otherwise, an internal generic system identifier will be automatically assigned.
* **`deleteNode(nodeId)`**
Destroys the node element matching the identifier and automatically sweeps/prunes any associated broken dependency lines.
* **`updateNodePosition(nodeId, x, y)`**
Mutates raw positioning values inside the memory reference model and triggers view updating layers.
#### Connection Management APIs
* **`connect(fromNodeId, fromSocketId, toNodeId, toSocketId)`**
Wires an active logical curve bridge between an output port and an input port. Returns `ConnectionSchema` object or `null` if the connection is illegal (e.g. self-looping or double mapping onto an input).
* **`disconnect(connectionId)`**
Severs a trace path across nodes using its direct line identifier.
#### Serialization & Hydration
* **`getState()`**
Returns a deep, JSON-serializable clone of the entire active workspace graph network topology.
* **`loadState(graphState)`**
Clears all active rendered interface objects and loads a freshly mapped `GraphState` configuration layer.
```javascript
// Saving state to local storage
const currentSnapshot = graph.getState();
localStorage.setItem('my_graph', JSON.stringify(currentSnapshot));
// Restoring state later
const cachedSnapshot = JSON.parse(localStorage.getItem('my_graph'));
if (cachedSnapshot) {
graph.loadState(cachedSnapshot);
}
```
---
## 🎛️ Handling Graph Events
The `GraphManager` implements a lightweight publisher/subscriber layout. You can listen to lifecycle changes across your pipeline engine to trigger custom code:
```javascript
// Capture changes to map properties to external reactive modules
graph.on('stateChanged', (latestState) => {
console.log("Global modifications registered:", latestState);
});
// Trace isolated edge terminations
graph.on('connectionRemoved', (connId) => {
console.warn(`Connection item vanished from runtime: ${connId}`);
});
// Trace node structural changes
graph.on('nodeAdded', (node) => {
console.log(`New node generated: ${node.title}`);
});
```
### Available Event Signatures
| Event Key | Callback Arguments | Description |
| :--- | :--- | :--- |
| `'nodeAdded'` | `(nodeSchema)` | Dispatched when a node object joins the topology. |
| `'nodeRemoved'` | `(nodeId)` | Dispatched when a node is removed from the canvas. |
| `'nodeMoved'` | `(nodeSchema)` | Dispatched repeatedly when a node object moves coordinates. |
| `'connectionAdded'`| `(connectionSchema)` | Dispatched when two nodes are connected. |
| `'connectionRemoved'`| `(connectionId)` | Dispatched when an edge bridge is broken or deleted. |
| `'stateChanged'` | `(graphState)` | Global catch-all event executed immediately after any graph mutation. |
---
## ⌨️ User Interface Interactions
* **Pan Canvas:** Click and drag anywhere on the empty background or the SVG connection space.
* **Zoom Viewport:** Scroll your mouse wheel or trackpad up and down. The transformation framework calculates and pins vector values centered **directly on your mouse cursor location**.
* **Drag Nodes:** Click and hold the node header bar container element. Movement velocities match your active zoom ratio precisely.
* **Draw Wires:** Click a target circular socket connector on an output node and drag your mouse. Drop the temporary dashed wire onto a target input node socket to complete a bridge connection.
* **Destroy Wires:** Click directly on an active solid colored connection bridge path vector line to delete it from the running memory structure.
* **Destroy Nodes:** Click the small `✕` button positioned inside the upper-right corner of the node header frame.

30
app.js Normal file
View File

@ -0,0 +1,30 @@
import { NodeEditor } from './NodeEditor.js';
// 1. Initialize the UI Engine
const container = document.getElementById('editor-container');
const editor = new NodeEditor(container);
// 2. Extract the State Manager
const graph = editor.getGraphManager();
// 3. Spawning Nodes
const nodeA = graph.addNode({
type: 'source_node',
title: 'Data Stream',
x: 100,
y: 150,
inputs: [],
outputs: [{ id: 'out_raw', name: 'Raw Buffer' }]
});
const nodeB = graph.addNode({
type: 'processing_node',
title: 'Transform Matrix',
x: 450,
y: 250,
inputs: [{ id: 'in_data', name: 'Payload' }],
outputs: [{ id: 'out_success', name: 'Success' }]
});
// 4. Create a programmatically driven wire connection
graph.connect(nodeA.id, 'out_raw', nodeB.id, 'in_data');