Frontend¶
SvelteKit 2 app on Svelte 5 (runes-friendly), bundled with Vite, styled with Tailwind CSS, with @xyflow/svelte (Svelte Flow) powering the lab canvas.
Module layout¶
frontend/
├── src/
│ ├── routes/
│ │ └── labs/[...id]/+page.svelte # The lab editor — top bar, canvas, console workspace
│ └── lib/
│ ├── api.ts # Typed wrappers over the backend REST API
│ ├── components/
│ │ ├── ToastStack.svelte # Top-level toast renderer
│ │ └── canvas/ # Every editor component (see below)
│ ├── stores/ # Svelte writables (auth, ws, info panels, drag state, toasts)
│ ├── services/ # Pure-TS helpers (port layout, edge routing, naming, etc.)
│ └── types/ # Hand-rolled TS types mirroring backend Pydantic models
├── tests/ # Vitest unit tests for components + stores
└── package.json
Canvas components¶
The canvas lives entirely under src/lib/components/canvas/. Key files:
| File | What it does |
|---|---|
TopologyCanvas.svelte |
Top-level canvas orchestrator. Owns the Svelte Flow viewport, applies layout, drives edges from links. |
CustomNode.svelte |
One node card (Docker / QEMU / IOL). Renders status badge, template, console handle, and the bottom-left info (i) button. |
NetworkNode.svelte |
One network pill. Renders count badge and the (i) button next to it. |
Port.svelte |
One port handle on a node card. Owns hover tooltip, click-vs-drag detection, and the pinned label for connected ports. |
PortLayer.svelte |
Positions ports around a node card's perimeter and dispatches port events to the canvas. |
NetworkPort.svelte |
One connection slot on a network pill (N + 1 slots: linked + always-open). |
LinkEdge.svelte |
One edge between two endpoints. Owns hover boldening + wide invisible hit-area. |
LinkPreview.svelte |
The temporary line that follows the cursor during a link drag. |
LinkConfirmModal.svelte |
Modal that surfaces metric / style / label config for a link. |
PortTooltip.svelte |
Floating hover tooltip with planned + live MAC. |
PortInfoPopover.svelte |
Per-port info popover (single-click on a port). |
NodeConfigModal.svelte |
Add / edit a node — template picker, image picker, NIC count, interface naming. |
InfoPanel.svelte |
One floating draggable info panel. Pointer-capture drag + viewport clamp. |
InfoPanelStack.svelte |
Renders the up-to-4 open info panels in a stack. |
Stores¶
| Store | Lives in | Purpose |
|---|---|---|
auth |
src/lib/stores/auth.ts |
Current-user + role state. |
session |
src/lib/stores/session.ts |
Per-session UI prefs (console layout, etc.). |
labWs |
src/lib/stores/labWs.ts |
WebSocket subscription to the backend runtime feed. |
dragLink |
src/lib/stores/dragLink.ts |
Active link-drag state machine. |
portInfo |
src/lib/stores/portInfo.ts |
Per-port popover state. |
infoPanels |
src/lib/stores/infoPanels.ts |
Open info panel list (max 4, LRU drop, drag positions). |
toasts |
src/lib/stores/toasts.ts |
Toast notification queue. |
Building¶
# Dev server with hot reload (proxies API + WS to a local backend on :8000)
cd frontend && npm run dev
# Production build (used by install.sh)
npm run build
# Output: frontend/build/ — gets rsync'd into /var/lib/nova-ve/www/
Type checking & tests¶
Vitest tests render components with @testing-library/svelte against the JSDOM environment. End-to-end tests under tests/e2e/ run with Playwright and need a live backend.
Design system¶
A Tailwind-based component set with a small token palette. See Design system adoption for the architectural notes around how it was wired in.