glideline
v0.0.2
Published
A feature-rich, React + Vite diagramming library
Maintainers
Readme
◈ glideline
A feature-rich, React + Vite diagramming library — built as a React counterpart to ng-diagram, with additional features.
✨ Features
| Feature | ng-diagram | glideline | | ------------------------------------------------- | :--------: | :-------: | | Draggable nodes | ✅ | ✅ | | Port-based connections | ✅ | ✅ | | Bezier / Straight / Orthogonal / SmoothStep edges | ✅ | ✅ | | Custom node types | ✅ | ✅ | | Selection box (marquee) | ✅ | ✅ | | Zoom & pan | ✅ | ✅ | | Undo / Redo (50 levels) | ✅ | ✅ | | Copy / Paste / Cut | ✅ | ✅ | | Drag-and-drop palette | ✅ | ✅ | | Node groups | ✅ | ✅ | | Minimap | ❌ | ✅ | | Auto-layout (Grid / Dagre / Tree) | ❌ | ✅ | | Export to JSON | ❌ | ✅ | | Export to SVG | ❌ | ✅ | | Import from JSON | ❌ | ✅ | | Animated edges | ❌ | ✅ | | Per-edge marker styles (arrow, dot, diamond) | ❌ | ✅ | | Node lock / hide | ❌ | ✅ | | Inline node label editing (double-click) | ❌ | ✅ | | Properties panel for nodes & edges | ❌ | ✅ | | Keyboard shortcuts | partial | ✅ Full | | History panel | ❌ | ✅ | | Theme toggle (dark / light) | ❌ | ✅ |
🚀 Quick Start
Requirements
- Node.js 18+
- npm 9+
Install & Run
# 1. Clone / extract the project
cd glideline
# 2. Install dependencies
npm install
# 3. Start dev server
npm run dev
# → http://localhost:5173
# 4. Build for production
npm run build🏗 Architecture
src/
├── components/
│ ├── Canvas.tsx # Main infinite canvas (pan, zoom, drop zone)
│ ├── NodeComponent.tsx # Node renderer + port handles
│ ├── EdgeComponent.tsx # SVG edge renderer + connection line
│ ├── Toolbar.tsx # Top toolbar with all controls
│ ├── Palette.tsx # Drag-and-drop node palette (left sidebar)
│ └── PropertiesPanel.tsx # Properties editor (right sidebar)
├── store/
│ └── diagramStore.ts # Zustand store — all diagram state + actions
├── types/
│ └── index.ts # TypeScript types
├── utils/
│ ├── geometry.ts # Path generation, coordinate transforms
│ └── defaults.ts # Default nodes, edges, palette items
└── hooks/
└── useKeyboardShortcuts.tsState Management
All diagram state lives in a single Zustand store with Immer for immutable updates. No context providers needed — import useDiagramStore anywhere:
import { useDiagramStore } from './store/diagramStore';
function MyComponent() {
const { model, addNode, removeSelected } = useDiagramStore();
// ...
}🎮 Usage
Adding Nodes Programmatically
const { addNode } = useDiagramStore();
// addNode(type, position, data?)
addNode('process', { x: 100, y: 200 }, {
label: 'My Node',
color: '#6366f1',
description: 'Optional subtitle'
});Available Node Types
| Type | Shape | Ports |
| ---------- | -------------- | ------------------- |
| default | Rectangle | L, R, T, B |
| input | Pill (rounded) | R, B |
| output | Pill (rounded) | L, T |
| decision | Diamond | T (in), R/B/L (out) |
| process | Rectangle | L (in), R (out) |
| database | Cylinder | T, B |
| cloud | Cloud | L, R, B |
| note | Sticky note | none |
Connecting Nodes
- Hover a node — colored dots appear on its edges (purple = source, green = target)
- Click-drag from a purple (source) port
- Release over any other node or its green (target) port
Edge Types
Change edge routing globally in the toolbar, or per-edge in the properties panel:
- Bezier — smooth curves (default)
- Straight — direct lines
- Orthogonal — right-angle routing
- Smooth Step — stepped with rounded corners
Importing a Diagram
const { importJSON } = useDiagramStore();
importJSON(JSON.stringify(myModel));Exporting
const { exportJSON, exportSVG } = useDiagramStore();
const json = exportJSON(); // full diagram JSON
const svg = exportSVG(); // SVG string⌨️ Keyboard Shortcuts
| Shortcut | Action |
| --------------------------- | ---------------------------- |
| Del / Backspace | Delete selected |
| Ctrl+Z | Undo |
| Ctrl+Y / Ctrl+Shift+Z | Redo |
| Ctrl+C | Copy |
| Ctrl+V | Paste |
| Ctrl+X | Cut |
| Ctrl+A | Select all |
| Ctrl+D | Duplicate selected |
| Ctrl+G | Group selected |
| Ctrl++ | Zoom in |
| Ctrl+- | Zoom out |
| F | Fit view |
| Esc | Deselect / cancel connection |
| Double-click node | Edit label inline |
| Alt+drag / Middle mouse | Pan canvas |
| Scroll | Zoom in/out |
| Shift+click | Multi-select |
📦 DiagramModel Schema
interface DiagramModel {
nodes: DiagramNode[];
edges: DiagramEdge[];
groups: DiagramGroup[];
viewport: { x: number; y: number; zoom: number };
}
interface DiagramNode {
id: string;
type: 'default' | 'input' | 'output' | 'decision' | 'process' | 'database' | 'cloud' | 'note';
position: { x: number; y: number };
size: { width: number; height: number };
data: { label?: string; color?: string; description?: string; [key: string]: unknown };
ports: Port[];
selected: boolean;
locked: boolean;
hidden: boolean;
zIndex: number;
rotation: number;
groupId?: string;
}
interface DiagramEdge {
id: string;
source: string; // node id
sourcePort: string; // port id
target: string;
targetPort: string;
data: { label?: string; animated?: boolean; color?: string };
routing: 'bezier' | 'straight' | 'orthogonal' | 'smoothstep';
markerStart: 'none' | 'arrow' | 'arrowclosed' | 'dot' | 'diamond';
markerEnd: 'none' | 'arrow' | 'arrowclosed' | 'dot' | 'diamond';
selected: boolean;
hidden: boolean;
zIndex: number;
}🛠 Store API
// Viewport
store.zoomIn()
store.zoomOut()
store.zoomTo(zoom, center?)
store.fitView()
store.panBy(dx, dy)
// Nodes
store.addNode(type, position, data?) → DiagramNode
store.updateNode(id, updates)
store.moveNode(id, position)
store.removeNode(id)
store.removeSelected()
store.duplicateSelected()
store.selectNode(id, multiSelect?)
store.selectAll()
store.deselectAll()
store.lockNode(id, locked)
store.toggleNodeVisibility(id)
store.resizeNode(id, size)
// Edges
store.addEdge(srcNodeId, srcPortId, tgtNodeId, tgtPortId)
store.updateEdge(id, updates)
store.removeEdge(id)
store.selectEdge(id, multiSelect?)
// Clipboard
store.copy()
store.paste()
store.cut()
// History
store.undo()
store.redo()
// Groups
store.groupSelected()
store.ungroupNodes(groupId)
store.toggleGroupCollapse(groupId)
// Layout
store.autoLayout('dagre' | 'grid' | 'tree')
// Settings
store.updateSettings({ snapToGrid, gridSize, showGrid, showMinimap, edgeRouting, theme, readonly, ... })
// Import / Export
store.exportJSON() → string
store.importJSON(json)
store.exportSVG() → string🎨 Theming
The app uses CSS custom properties and inline styles. Toggle between dark/light with the toolbar button, or set directly:
store.updateSettings({ theme: 'light' }); // or 'dark'📁 Examples
See the examples/ folder for self-contained HTML demos:
examples/01-basic-flow.html— Simple flow diagram from scratchexamples/02-data-pipeline.html— ETL pipeline with database nodesexamples/03-network-topology.html— Network diagram with cloud nodesexamples/04-load-json.html— Load a pre-built diagram from JSON
📄 License
MIT © glideline contributors
