Compare commits

...

No commits in common. "master" and "main" have entirely different histories.
master ... main

5 changed files with 144 additions and 222 deletions

132
.gitignore vendored Normal file
View File

@ -0,0 +1,132 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2026 Cybernomad
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# node-editor
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.

191
Readme.md
View File

@ -1,191 +0,0 @@
# 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.

31
app.js
View File

@ -1,31 +0,0 @@
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')