- New GroupRectNode: rounded rect with NodeResizer (8 handles), no
connection handles, always behind network nodes via negative zIndex
- GroupRectModal: label, font preset (Inter/Mono/Serif), 3×3 text
position grid, text/border/background color pickers, z-order 1–9
- Sidebar: "Add Rectangle" button (below Add Node), group rects
excluded from node count stats
- Save/load: size persisted in custom_colors.width/height, no backend
schema changes required
- elevateNodesOnSelect=false on ReactFlow so selected rects never
pop above network nodes
- Tests: 3 new store tests + 9 GroupRectModal tests (66 total, all pass)
Backend:
- Add MAC OUI lookup table (QEMU 52:54:00, Proxmox bc:24:11, VMware, VBox, Hyper-V)
- suggest_node_type() now accepts mac param and uses OUI as a low-priority hint
Frontend:
- Detect VM type from MAC prefix on pending device cards
- Show colored badge (QEMU / PVE / VMware / VBox / Hyper-V) in orange with tooltip
- Show recognized service name (Home Assistant, Synology, Proxmox, etc.) as
card title instead of IP when detected via service signatures
- Keep IP displayed smaller below when title differs from IP
- Add SSH / HTTP / HTTPS badges as colored pills
- Show +N badge for additional services beyond the three common ones
- nodeColors.ts: add missing camera/printer/computer/cpl entries to Record<NodeType, NodeColors>
- CanvasContainer.tsx: use ConnectionMode enum instead of bare string literal
- export.ts: cast style object to Partial<CSSStyleDeclaration> for CSS custom property
- Sidebar.tsx: cast suggested_type to NodeType to satisfy NodeData type
- Click any pending device row to open a detail modal
- Modal shows IP, hostname, MAC, OS, suggested type, discovered_at
- Service list with port/protocol/service_name, colored dot by category
- Approve / Hide / Delete buttons with matching color coding
- List items now show IP, service count, hostname and suggested type at a glance
Backend:
- asyncio.to_thread(_nmap_scan) — nmap no longer blocks the event loop
- Commit each discovered device immediately (previously one bulk commit at end)
- Update ScanRun.devices_found after each device so history panel shows live count
- broadcast_scan_update() pushes {type: scan_device_found} WS event per device
- Refactor broadcast_status to shared _broadcast() helper, adds type: "status" field
Frontend:
- useStatusPolling handles both WS message types (status / scan_device_found)
- canvasStore: scanEventTs + notifyScanDeviceFound() action
- PendingDevicesPanel auto-refreshes when WS scan event is received
- scanner.py: re-raise nmap exceptions so they reach ScanRun.error
(previously silently returned [] hiding the root cause)
- Sidebar: after triggering scan, auto-switch to History tab
- ScanHistoryPanel: auto-refresh every 3s while any run is 'running';
show error toast when a run transitions running→error; show spinner
on running runs; error message fully visible (no truncation)
- scripts/run_scan.py: standalone scan script to run with sudo for
nmap OS detection / SYN scans on macOS