nFlow β Visual Deep Learning Model Creator
A node-based, native desktop app for designing deep-learning models visually. Wire nodes on a canvas β see live tensor shapes β run CPU inference β export to PyTorch, Keras, or ONNX. Zero Python required.
Stack: Tauri v2 (Rust backend + WebView frontend) Β· React 19 Β· xyflow (React Flow) Β· Tailwind v4 Β· Zustand
Architecture spec & SRS:
krystv/nflow-architecture-spec
What works right now
| Feature | Status |
|---|---|
| Node-graph canvas (pan, zoom, drag, multi-select) | β |
| 90+ built-in operators (IO, Layer, Linalg, Activation, Norm, Pooling, Movement, Loss, Composite) | β |
| Live symbolic tensor shape inference (every wire shows shape + dtype) | β |
| Shape error diagnostics per node | β |
| Custom node creator (composite editor tab) | β |
| Custom node io.input / io.output β inline rename, dtype picker, rank badge | β |
| Composite validity bar (port count, error count, Rust diagnostics) | β |
| Snapshot-based undo / redo (all edit types) | β |
| Code export β PyTorch, Keras, ONNX text | β |
ONNX binary protobuf export (passes onnx.checker, runs in onnxruntime) |
β |
| CPU numeric forward pass (real tensor evaluation β Simulate button) | β |
.nfl project file β save / open / recent files |
β |
| Global composite library (reusable nodes across projects, persisted in redb) | β |
| Group-to-composite (select nodes β right-click β Create Block) | β |
| Double-click block node β inline to primitives | β |
| Double-click user composite β open editor tab | β |
| Rhai custom op scripting (define shape inference in-place, no recompile) | β |
| Multi-tab canvas (main project + one tab per open composite editor) | β |
| Training config (AdamW / SGD / Adam + loss β emits PyTorch training loop) | β |
Rust crate status
| Crate | What it does | Tests |
|---|---|---|
nflow-ir |
SSA graph IR, SlotMap nodes/values, Dim algebra, UserComposite | 6 β |
nflow-infer |
DimSolver (union-find), broadcast, cost model (params + FLOPs) | 7 β |
nflow-ops |
90+ operator registry β OpSpec + shape infer + Rhai custom ops | 14 β |
nflow-events |
Session (event-sourced undo/redo + live inference + view) + .nfl format + redb | 6 β |
nflow-codegen |
PyTorch / Keras / ONNX text + binary protobuf exporters | 5 β |
nflow-exec |
Backend trait, CPU evaluator (90/90 ops covered), GPU stub (burn+wgpu, --features wgpu) |
4 β |
42 tests, 0 failures, 0 warnings (cargo test at workspace root, no GPU needed).
Quick start
# Prerequisites: Rust stable, Node 20+, Tauri v2 system deps
# See: https://tauri.app/start/prerequisites/
npm install
# Desktop app (Rust backend + hot-reload frontend):
npm run tauri dev
# Frontend-only preview β uses in-browser mock engine, no Rust needed:
npm run dev
# Production bundle:
npm run tauri build
# Rust unit tests only (no webkit/GPU needed):
cargo test
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β React/xyflow frontend (src/) β
β ββββββββββββ ββββββββββββββ ββββββββββββββββββββββββ β
β βLeftPaletteβ β Editor β β RightInspector β β
β βOps/Custom β β (canvas) β β (attrs + ports) β β
β ββββββββββββ ββββββββββββββ ββββββββββββββββββββββββ β
β β β β β
β βββββββββββββββββ΄βββββββββββββββββββββ β
β β invoke(Intent) β
βββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ
β Tauri IPC
βββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ
β Rust backend (src-tauri/src/lib.rs) β
β β β
β Session::apply(intent) β
β β β β
β Graph mutation run_inference() β
β β β
β GraphView (nodes+edges+shapes) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The Rust core is the single source of truth. The frontend is a read-only projection β it sends Intent messages (add_node, connect, disconnect, move, delete, set_attr) and receives back a GraphView. The frontend never owns graph state; all mutation logic, shape inference, validation, and undo/redo live in Rust.
Frontend file map
src/
βββ main.tsx β bootstrap, global contextmenu suppression
βββ App.tsx β layout shell, multi-tab canvas mount
βββ ErrorBoundary.tsx β catches render crashes, shows readable error
βββ index.css β Tailwind v4, react-flow overrides, scrollbar, font
βββ lib/utils.ts β cn(), fmtShape, dtypeClass, IS_MAC, fmtShortcut
β
βββ bridge/
β βββ types.ts β all TS interfaces mirroring Rust types
β βββ invoke.ts β api.* β the ONLY place that calls invoke()
β βββ mock.ts β in-browser mock engine for npm run dev
β
βββ store/
β βββ graph.ts β main session state (view, catalog, file, undo/redo)
β βββ tabs.ts β tab system (main + composite editor tabs)
β βββ composites.ts β project + global composite library
β
βββ components/
β βββ layout/
β β βββ TopBar.tsx β file menu, undo/redo, export, simulate, settings
β β βββ TabBar.tsx β tab strip (main + editor tabs)
β β βββ LeftPalette.tsx β 28px strip + Ops panel + Custom panel + create modal
β β βββ RightInspector.tsxβ node inspector (attrs, ports, weights, cost)
β β βββ BottomMetrics.tsx β collapsible metrics bar (params, FLOPs, event log)
β βββ editor/
β βββ Editor.tsx β ReactFlow canvas, all callbacks useCallback-wrapped
β βββ ContextMenu.tsx β right-click menus (node / edge / canvas)
β βββ nodes/
β βββ PrimitiveNode.tsx β leaf op node (read-only display)
β βββ BlockNode.tsx β composite/block node (double-click to expand)
β βββ IoNode.tsx β io.input / io.output (editable label + dtype)
β
βββ panels/
βββ CompositeEditorToolbar.tsx β toolbar for editor tabs (save/discard/undo/promote)
βββ CompositeValidityBar.tsx β validity strip above each editor canvas
βββ ExportModal.tsx β code export (PyTorch / Keras / ONNX)
βββ RunPanel.tsx β forward pass result + coverage report
βββ TrainPanel.tsx β training config (optimizer, loss, metrics)
βββ SettingsPanel.tsx β app settings (shortcuts, canvas, general)
βββ NodeLibraryPanel.tsx β full node library panel
βββ Toast.tsx β error/info toasts
Custom node creation flow
LeftPalette β Custom tab β "New" button
β
ββ QuickCreate (inline)
β Type name β Enter β createComposite() β loadProjectComposites() β openEditorTab()
β
ββ Advancedβ¦ link β AdvancedCreateModal
Name, uid, category, colour, port counts, doc, paper URL
β createComposite() β loadProjectComposites() β openEditorTab()
openEditorTab(uc):
1. api.openCompositeEditor(uid) β Rust creates sub-Session with inner_graph
2. tabs store: push new tab, set editorViews[uid] = GraphView
3. App.tsx: mounts <Editor editorUid={uid} /> (display:flex, others display:none)
4. CompositeValidityBar renders above canvas (port count, valid/invalid, errors)
5. io.input / io.output nodes render as IoNode (click label to rename, dtype picker)
Critical bugs fixed (session history)
| Bug | Symptom | Fix |
|---|---|---|
onSelectionChange inline arrow |
Maximum update depth (infinite loop) | Wrapped in useCallback with [] deps |
nodeTypes inside component |
All nodes remount every render | Moved to module-level const |
onClick={void go} |
Create button does nothing | Changed to onClick={() => void go()} |
App.tsx single <Editor> |
Editor tab canvas never appeared | All tab canvases mounted simultaneously, display:none for inactive |
mockCreateUserComposite always returned Mamba |
New composite had wrong uid | Mock now inserts real entry into store |
open_composite_editor ignored uid |
Editor always showed Mamba graph | mockEditorViewForUid(uid) generates correct io nodes |
Graph unused import in lib.rs |
Rust compiler warning | Removed; use Definition directly |
import { useEffect } after usage |
Module ordering issue | All imports hoisted to top of file |
CreateCompositeModal imported but never rendered in App.tsx |
Advanced modal inaccessible | Inlined as AdvancedCreateModal in LeftPalette.tsx |
Design system
- Canvas bg:
#0a0c10Β· Panel bg:#14161bΒ· Strip bg:#0e1016Β· Border:#2d313a - Fonts: Inter (body) Β· JetBrains Mono (code/values) β defined in
@themeCSS vars - 28px strip pattern: LeftPalette and RightInspector use 28px permanent strips with rotated text
- Port hover:
box-shadowglow only β nevertransform: scale()(causes positional jump) - Wire z-index:
.react-flow__connection-line { z-index: 1001 }β above port handles
Repo layout
crates/ Rust workspace (pure, no GUI deps β cargo test works anywhere)
nflow-ir/ SSA graph IR + UserComposite + Dim algebra
nflow-infer/ DimSolver + shape broadcast + cost model
nflow-ops/ Operator registry (90+ ops) + Rhai custom op scripting
nflow-events/ Session (undo/redo + inference) + .nfl format + redb DB
nflow-codegen/ PyTorch + Keras + ONNX exporters
nflow-exec/ CPU evaluator + GPU Backend trait (wgpu feature)
src-tauri/ Tauri v2 IPC command layer (~30 commands)
src/ React 19 + xyflow + Tailwind v4 + Zustand frontend
docs/ Architecture docs, SRS, operator reference
License
Apache-2.0
Generated by ML Intern
This repository was built and maintained by ML Intern.
- Try ML Intern: https://smolagents-ml-intern.hf.space
- Source code: https://github.com/huggingface/ml-intern