@nodius/client
v1.0.1
Published
[](https://www.npmjs.com/package/@nodius/client) [](https://www.typescriptlang.org/) [
- React DOM: Version 19.2+ (peer dependency)
- React Hot Toast: Version 2.6+ (peer dependency)
- Browser: WebGPU support (Chrome 113+, Edge 113+, etc.)
Quick Start
Minimal Configuration
# From packages/client folder
npm run devThe application starts on:
- URL:
https://localhost:5173(or first available port) - API: Automatically configured via generated
.env
Custom Configuration
The .env file is automatically generated by the startVite.mjs script, but you can customize it:
# .env
VITE_API_URL=https://api.example.com
VITE_WS_URL=wss://api.example.com/wsVerify Application is Running
- Open
https://localhost:5173in a WebGPU-supporting browser - Login with an admin account (see server README to create an admin)
- Create a new workflow
- Start editing
Architecture
The client application is built around several key components:
┌─────────────────────────────────────────────────────┐
│ Nodius Client (React) │
├─────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Context Providers │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Theme │ │ Project │ │ User │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────┬─────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────┐ │
│ │ App Component │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ HomeWorkflow │ │ SchemaEditor │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └────────────────────┬─────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────┐ │
│ │ WebGPU Rendering Engine │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ Nodes │ │ Edges │ │Background│ │ │
│ │ └────────┘ └────────┘ └────────┘ │ │
│ └────────────────────┬─────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────┐ │
│ │ WebSocket Synchronization │ │
│ │ - Real-time collaboration │ │
│ │ - Instruction-based sync │ │
│ │ - Auto-reconnect │ │
│ └──────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘Data Flow
- User Interaction → UI Component → Instruction Builder
- Instruction → WebSocket → Server → Broadcast to all clients
- Received Instruction → Project Context → Motor → Visual rendering
- Auto-Save → Server (every 30s via WebSocket)
Main Components
SchemaEditor
The main editor for creating and modifying graphical workflows.
Features
- Drag & Drop: Add nodes from library
- Visual Connections: Create edges between nodes
- Multi-Selection: Select and move multiple nodes
- Resize: Dynamically resize nodes
- Undo/Redo: Complete modification history (Ctrl+Z / Ctrl+Y)
- Copy/Paste: Copy-paste nodes (Ctrl+C / Ctrl+V)
- Left Panel: Node library, history, data types
- Right Panel: Selected node properties
- Code Editor: Process code editing with CodeMirror
Usage
import { SchemaEditor } from '@nodius/client';
import { ProjectContext } from '@nodius/client';
// In a component
const Project = useContext(ProjectContext);
// Load a workflow
useEffect(() => {
Project.dispatch({
field: 'graph',
value: loadedWorkflow
});
}, []);
// Display editor
<SchemaEditor />Editor Panels
Left Panel
- Node Library: Library of available node types
- Component Tree: Hierarchical tree of workflow nodes
- History: Modification history with undo/redo
- Type Editor: Custom data type editor
- Enum Editor: Enumeration editor
Right Panel
- Component Editor: Edit selected node properties
- Tag Editor: Modify HTML tag (for HTML nodes)
- Content Editor: Edit text content
- CSS Editor: Visual CSS style editing
- Anchor Editor: Positioning configuration
- Events Editor: DOM event management
- Icon Editor: Lucide icon selection
- Image Editor: Image management
- Handle Config: Connection point (handles) configuration
WebGPU Motor
High-performance rendering engine using WebGPU to display workflow canvas.
Architecture
// packages/client/src/schema/motor/webGpuMotor/
├── index.ts // Entry point, initialization
├── types.ts // TypeScript types
├── nodeRenderer.ts // Node rendering
├── edgeRenderer.ts // Connection rendering
├── backgroundRenderer.ts // Background rendering (grid/dots)
├── inputHandler.ts // Interaction handling (mouse/touch)
└── cameraAnimator.ts // Camera animation (zoom/pan)Features
- GPU Rendering: Uses WebGPU for optimal performance
- Zoom & Pan: Smooth canvas navigation
- Selection: Visual node selection
- Hover Effects: Hover effects
- Background Types: Grid, dots, or solid
- Responsive: Automatic container size adaptation
- 60 FPS: Smooth 60 frames per second animation
Initialization
import { WebGpuMotor } from '@nodius/client';
const motor = new WebGpuMotor();
await motor.init(containerElement, canvasElement, {
backgroundType: 'dotted' // 'grid' | 'dotted' | 'solid'
});
motor.resetViewport();
motor.enableInteractive(true);Motor API
// Rendering
motor.render(nodes, edges, selectedNodeIds);
// Viewport
motor.resetViewport();
motor.zoomToFit(nodes);
motor.setZoom(level);
motor.setPan(x, y);
// Interaction
motor.enableInteractive(enabled);
motor.getNodeAtPosition(x, y);
// Cleanup
motor.dispose();Context Providers
The client uses three React contexts to manage global state:
ThemeContext
Application theme management.
import { useContext } from 'react';
import { ThemeContext } from '@nodius/client';
const Theme = useContext(ThemeContext);
// Theme state
const isDarkMode = Theme.state.darkMode;
// Modify theme
Theme.dispatch({
field: 'darkMode',
value: true
});ThemeContext State:
interface ThemeContextType {
darkMode: boolean;
primaryColor: string;
secondaryColor: string;
fontSize: number;
// ... other properties
}ProjectContext
Project/workflow state management.
import { useContext } from 'react';
import { ProjectContext } from '@nodius/client';
const Project = useContext(ProjectContext);
// Current workflow
const currentWorkflow = Project.state.graph;
// Selected nodes
const selectedNodes = Project.state.nodeSelection;
// Modify state
Project.dispatch({
field: 'graph',
value: newWorkflow
});ProjectContext State:
interface ProjectContextType {
graph?: Graph; // Current workflow
selectedSheetId?: string; // Active sheet
nodeSelection: Set<string>; // Selected nodes
editedNode?: Node<any>; // Edited node
editedHtml?: HtmlEditContext; // HTML editing context
getMotor?: () => GraphicalMotor; // Rendering engine access
backAction?: () => Promise<void>; // Undo
aheadAction?: () => Promise<void>; // Redo
// ... other properties
}UserContext
Authentication and user info management.
import { useContext } from 'react';
import { UserContext } from '@nodius/client';
const User = useContext(UserContext);
// Logged in user
const currentUser = User.state.user;
// JWT token
const token = User.state.token;
// Logout
User.dispatch({
field: 'user',
value: null
});WebSocket Sync
Custom hook for real-time synchronization via WebSocket.
Operation
import { useSocketSync } from '@nodius/client';
// In a component
const App = () => {
useSocketSync(); // Activate WebSocket synchronization
// Hook automatically handles:
// - WebSocket server connection
// - Sending local instructions
// - Receiving remote instructions
// - Applying changes
// - Reconnection on disconnect
};WebSocket Messages
The hook sends and receives messages in the format:
// Sending instructions
{
type: 'instructions',
workflowKey: 'workflow_123',
instructions: [...],
clientId: 'unique_client_id',
timestamp: Date.now()
}
// Receiving updates
{
type: 'update',
workflowKey: 'workflow_123',
instructions: [...],
sourceClientId: 'other_client_id'
}Custom Hooks
useCreateReducer
Hook to create a reducer with type-safe dispatch.
import { useCreateReducer } from '@nodius/client';
interface MyState {
count: number;
name: string;
}
const MyComponent = () => {
const state = useCreateReducer<MyState>({
initialState: { count: 0, name: 'John' }
});
// Modify state
state.dispatch({
field: 'count',
value: state.state.count + 1
});
return <div>{state.state.count}</div>;
};useWebSocket
Hook to manage a WebSocket connection.
import { useWebSocket } from '@nodius/client';
const MyComponent = () => {
const { ws, connected, send } = useWebSocket('wss://api.example.com/ws', {
onOpen: () => console.log('Connected'),
onMessage: (data) => console.log('Received:', data),
onClose: () => console.log('Disconnected'),
onError: (error) => console.error('Error:', error)
});
const sendMessage = () => {
send({ type: 'ping' });
};
return <div>Connected: {connected ? 'Yes' : 'No'}</div>;
};useElementSize
Hook to monitor element size.
import { useElementSize } from '@nodius/client';
import { useRef } from 'react';
const MyComponent = () => {
const ref = useRef<HTMLDivElement>(null);
const { width, height } = useElementSize(ref);
return (
<div ref={ref}>
Size: {width} x {height}
</div>
);
};useImageUpload
Hook to manage image uploads.
import { useImageUpload } from '@nodius/client';
const MyComponent = () => {
const { uploading, progress, uploadImage } = useImageUpload();
const handleUpload = async (file: File) => {
const result = await uploadImage(file);
console.log('Uploaded:', result.imageKey);
};
return (
<div>
{uploading && <div>Uploading: {progress}%</div>}
<input type="file" onChange={(e) => {
if (e.target.files?.[0]) {
handleUpload(e.target.files[0]);
}
}} />
</div>
);
};useDynamicClass
Hook to manage dynamic CSS classes.
import { useDynamicClass } from '@nodius/client';
const MyComponent = () => {
const className = useDynamicClass({
base: 'my-component',
active: isActive,
disabled: isDisabled
});
return <div className={className}>Content</div>;
};UI Components
Form Components
Button
import { Button } from '@nodius/client';
<Button
onClick={() => console.log('Clicked')}
variant="primary" // 'primary' | 'secondary' | 'danger'
disabled={false}
>
Click Me
</Button>Input
import { Input } from '@nodius/client';
<Input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter text..."
type="text"
/>Select
import { Select } from '@nodius/client';
<Select
value={selectedValue}
onChange={(e) => setSelectedValue(e.target.value)}
options={[
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' }
]}
/>Card
import { Card } from '@nodius/client';
<Card
title="My Card"
subtitle="Subtitle"
onClick={() => console.log('Card clicked')}
>
Card content here
</Card>Animation Components
Fade
import { Fade } from '@nodius/client';
<Fade show={isVisible} duration={300}>
<div>Content to fade</div>
</Fade>Collapse
import { Collapse } from '@nodius/client';
<Collapse isOpen={isOpen}>
<div>Collapsible content</div>
</Collapse>MultiFade
import { MultiFade } from '@nodius/client';
<MultiFade currentIndex={activeIndex}>
<div>Panel 1</div>
<div>Panel 2</div>
<div>Panel 3</div>
</MultiFade>Code Editor
CodeEditorModal
import { CodeEditorModal } from '@nodius/client';
<CodeEditorModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
code={code}
onSave={(newCode) => {
setCode(newCode);
setIsOpen(false);
}}
language="javascript"
title="Edit Process Code"
/>EditorBlock
import { EditorBlock } from '@nodius/client';
<EditorBlock
value={code}
onChange={(value) => setCode(value)}
language="javascript"
height="400px"
readOnly={false}
/>Image Manager
import { ImageManagerModal } from '@nodius/client';
<ImageManagerModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
onSelect={(imageKey) => {
console.log('Selected image:', imageKey);
setIsOpen(false);
}}
/>API Client
Fetch Middleware
The client automatically initializes a fetch middleware that prefixes all requests with the API URL:
import { initializeFetchMiddleware } from '@nodius/client';
// Initialization (already done in main.tsx)
initializeFetchMiddleware();
// Usage
const response = await fetch('/api/workflow');
// Automatically becomes: https://api.example.com/api/workflowImage API
import { uploadImage, getImageUrl, deleteImage } from '@nodius/client';
// Upload
const result = await uploadImage(file, 'my-image.png');
console.log(result.imageKey);
// Get URL
const url = getImageUrl('image_123');
// Delete
await deleteImage('image_123');Workflow API
// List workflows
const response = await fetch('/api/workflow');
const { workflows } = await response.json();
// Get a workflow
const response = await fetch('/api/workflow/workflow_123');
const { workflow } = await response.json();
// Create a workflow
const response = await fetch('/api/workflow', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'New Workflow' })
});Development
Package Structure
packages/client/
├── src/
│ ├── component/ # Reusable UI components
│ │ ├── animate/ # Animation components
│ │ ├── code/ # Code editors
│ │ └── form/ # Form components
│ ├── hooks/ # Custom hooks
│ │ └── contexts/ # Context providers
│ ├── menu/ # Menus and navigation
│ │ └── homeWorkflow/ # Main workflow menu
│ ├── pages/ # Application pages
│ │ └── Login.tsx
│ ├── schema/ # Workflow editor
│ │ ├── editor/ # Editing components
│ │ │ └── menu/ # Left/right panels
│ │ ├── hook/ # Editor hooks
│ │ ├── manager/ # Managers (animations, etc.)
│ │ └── motor/ # Rendering engine
│ │ └── webGpuMotor/ # WebGPU implementation
│ ├── utils/ # Utilities
│ ├── public/ # Static assets
│ │ └── css/
│ ├── App.tsx # Root component
│ ├── AuthWrapper.tsx # Authentication wrapper
│ ├── main.tsx # Entry point
│ └── index.ts # Exports
├── cli/
│ └── startVite.mjs # Vite startup script
├── vite.config.mjs # Vite configuration
├── package.json
├── tsconfig.json
└── README.mdDevelopment Scripts
# Start in development mode
npm run dev
# Build for production
npm run build
# Generate barrel files
npm run barrelize
# Preview build
npm run previewAdding a New Component
- Create the component in
src/component/:
// src/component/MyComponent.tsx
import { FC } from 'react';
interface MyComponentProps {
title: string;
onClick?: () => void;
}
export const MyComponent: FC<MyComponentProps> = ({ title, onClick }) => {
return (
<div onClick={onClick}>
<h2>{title}</h2>
</div>
);
};- Export in
src/index.ts:
export * from './component/MyComponent';- Rebuild barrel files:
npm run barrelizeBuild and Deployment
Production Build
# Build all packages
npm run build
# Build only client
npm run build --workspace=@nodius/clientThe build generates:
dist/: Compiled code for npm publishingdist-vite/: Built web application (if using Vite)
Deployment
Serve with Nginx
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
root /var/www/nodius-client;
index index.html;
# SPA routing
location / {
try_files $uri $uri/ /index.html;
}
# Asset caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Deployment with Docker
FROM node:18-alpine as builder
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build --workspace=@nodius/client
FROM nginx:alpine
COPY --from=builder /app/packages/client/dist-vite /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Production Environment Variables
Create a .env.production file:
VITE_API_URL=https://api.production.example.com
VITE_WS_URL=wss://api.production.example.com/wsBuild with production configuration:
npm run build -- --mode productionWebGPU Compatibility
The rendering engine uses WebGPU, which requires:
- Chrome/Edge: Version 113+
- Firefox: Experimental support (via flag)
- Safari: Support in development
To check WebGPU support:
const supportsWebGPU = 'gpu' in navigator;
if (!supportsWebGPU) {
console.error('WebGPU not supported');
// Fallback to canvas 2D or display message
}Keyboard Shortcuts
- Ctrl + Z: Undo
- Ctrl + Y: Redo
- Ctrl + C: Copy selected node/HTML
- Ctrl + V: Paste
- Delete: Delete selection
- Escape: Deselect all
Contributing
Contributions are welcome! To contribute:
- Respect the modular architecture
- Use strict TypeScript
- Document new components
- Test on different browsers
Support
- Issues: https://github.com/Nodius-kit/Nodius/issues
- Supported Browsers: Chrome 113+, Edge 113+
- WebGPU: Required for rendering
License
ISC - See LICENSE
Creator
Hugo MATHIEU
- Email: [email protected]
- LinkedIn: https://www.linkedin.com/in/hugo-mathieu-fullstack/
Note: This application requires a browser supporting WebGPU. Make sure to use a recent version of Chrome or Edge for optimal experience.
