Commit Graph

761 Commits

Author SHA1 Message Date
Rémy 96bb048228 Merge pull request #204 from Pouzor/chore/bump-2.5.0
chore: bump version to 2.5.0
v2.5.0
2026-06-12 01:16:25 +02:00
Pouzor 88c006764a test: fix flaky per-service toggle assertion in SettingsModal
The toggle label renders before settingsApi.get() resolves, so reading
.checked immediately raced the async state update and failed on slower CI.
Wait for the resolved checked state instead.

ha-relevant: no
2026-06-12 01:05:48 +02:00
Pouzor 9c3dace12f chore: bump version to 2.5.0
ha-relevant: no
2026-06-12 00:54:52 +02:00
Rémy ec53363fdb Merge pull request #203 from Pouzor/feat/scan-history-modal
feat: move scan history into a modal
2026-06-12 00:40:19 +02:00
Pouzor 0e260ede73 feat: move scan history from sidebar panel into a modal
Replace the inline sidebar 'history' view with a dedicated ScanHistoryModal.
Same data and actions (refresh, auto-refresh while running, stop scan,
transition toasts) plus run duration, finished timestamp, and kind/status
filters. Scan/Zigbee start now surface a toast instead of force-switching
the sidebar view.

ha-relevant: yes
2026-06-12 00:11:16 +02:00
Rémy 60902167c0 Merge pull request #202 from Pouzor/feat/node-into-container
feat: drop node onto container node to nest it
2026-06-11 18:36:48 +02:00
Pouzor 592e7865c5 fix(deps): bump zeroconf to 0.149.12 for CVE-2026-48045
pip-audit (Security workflow) flagged zeroconf 0.149.7 as vulnerable to
CVE-2026-48045, fixed in 0.149.12.

ha-relevant: no
2026-06-11 18:24:59 +02:00
Pouzor f082c295fd feat: edit container parent in the node modal instead of detail panel
Replaces the detail-panel container selector with the existing "Parent
Container" dropdown in the edit modal (double-click). The selector now
shows for any node that is nested in — or can nest into — a container_mode
node, not just lxc/vm/docker_container, so a dragged-in node can be
re-targeted or detached ("None") from the modal.

- NodeModal: container_mode candidates count as valid parents; selector
  shown for nested nodes; submit/type-change keep valid container parents
- App: pass container_mode in parentCandidates
- revert detail-panel selector + setNodeParent store action (updateNode
  already handles parent attach/detach)

ha-relevant: yes
2026-06-11 17:25:44 +02:00
Pouzor 10b981ad1d feat: editable container selector to detach/re-parent a nested node
When a node is nested in a container, the detail panel now shows a
Container selector. Pick another container to move it, or "None" to
detach it back to the canvas (clears parentId).

- store: setNodeParent(childId, parentId|null) — attach/detach/re-parent
  via absolute coords, container_mode-only targets, history snapshot
- DetailPanel: Container <select>, shown only for nested nodes

ha-relevant: yes
2026-06-11 15:39:49 +02:00
Pouzor 2058e453ff feat: drop node onto container node to nest it
Dropping a top-level node over any container_mode node (proxmox,
docker_host, ...) now pops a confirm modal that nests it as a child
(sets parentId), mirroring the existing drop-onto-group flow.

- store: addToContainer(containerId, childId)
- CanvasContainer: detect container_mode intersection on drag stop
  (group still wins if both intersect)
- generalize ConfirmAddToGroupModal with a container variant

ha-relevant: yes
2026-06-11 14:59:25 +02:00
Rémy b776b6b8f1 Merge pull request #200 from Pouzor/feat/editable-node-groups
feat: editable node groups (add/remove members, description)
2026-06-11 02:05:34 +02:00
Pouzor fe3787f0cd fix: persist group description with Ctrl+S, not just on blur
The description field committed to the store only on textarea blur, so
saving via Ctrl+S (the primary shortcut, which never blurs the field)
dropped the edit. Make it controlled and commit on each change; snapshot
history once per edit session (on focus) so undo stays a single step.

ha-relevant: yes
2026-06-11 01:48:22 +02:00
Pouzor 7ea1fc2d69 feat: editable node groups (add/remove members, description)
Make Node Groups mutable instead of fixed-at-creation:
- removeFromGroup / addToGroup store actions (inverse pair, history-aware)
- right panel: per-member remove button + editable group description
  (reuses data.notes, no backend/serializer change)
- drag a node over a group → confirm modal to add it (getIntersectingNodes)

Tests: +21 (store, panel, canvas detection, modal).

ha-relevant: yes
2026-06-10 16:50:36 +02:00
Rémy 71cc183efb Merge pull request #199 from Pouzor/feat/multiline-edge-labels
feat: multi-line edge labels
2026-06-10 15:13:12 +02:00
Pouzor 7873f8b94f feat: multi-line edge labels
Connection labels now accept line breaks. The label field in EdgeModal
is a textarea (Enter inserts a newline) and the rendered edge label uses
whitespace-pre-line so newlines display as multiple lines.

Closes #183

ha-relevant: yes
2026-06-10 12:04:10 +02:00
Rémy b34b40794d Merge pull request #198 from Pouzor/fix/status-ping-service-color
feat: per-service status checks + ping/service-color fixes (#196)
2026-06-10 11:13:06 +02:00
Pouzor ed310f0d8d fix(status): keep non-HTTP services grey instead of red
Per-service checks now only probe HTTP(S)-reachable services. SSH (22) and
other non-web ports (DB, mail, DNS, raw TCP) stay 'unknown' (grey category
colour) rather than going red — an open TCP socket doesn't prove the service
is healthy, and a firewalled port flapped red misleadingly.

ha-relevant: yes
2026-06-10 10:48:03 +02:00
Pouzor 2db173cd56 fix(ws): idempotent connection removal, release slot on any error
The status WebSocket pool removed connections with list.remove(), which
raises ValueError on a double-remove (broadcast already dropped a dead
socket, then disconnect tries again), and only released a slot on
WebSocketDisconnect — any other error leaked the socket into the
broadcast pool. Centralise removal in an idempotent _drop() called from
a finally block and from _broadcast.

ha-relevant: yes
2026-06-10 09:58:06 +02:00
Pouzor d032103b7d fix(types): add type params to broadcast_service_status services arg
ha-relevant: yes
2026-06-10 02:44:06 +02:00
Pouzor bcc488993d feat: per-service status checks with offline colouring
Adds optional live status checking per service (not just per node),
requested as a follow-up to issue #196.

Backend:
- New check_service / check_services: HTTP(S) GET for web services, TCP
  connect otherwise; UDP and port-less non-web services stay 'unknown'.
- New scheduler job 'service_checks', independent interval (default 300s),
  added/removed live via set_service_checks_enabled.
- Settings gain service_check_enabled + service_check_interval (>=30s),
  persisted to scan_config.json. New WS message type 'service_status'.

Frontend:
- Live per-service status overlay in canvasStore (not persisted, so it
  never round-trips through canvas save), fed by the WS message.
- DetailPanel + canvas node service rows: offline service turns red
  (#f85149), otherwise keeps its category colour.
- SettingsModal: toggle + interval input (default 300s / 5 min).

Off by default — no behaviour change until enabled.

ha-relevant: yes
2026-06-10 02:22:48 +02:00
Pouzor aa0a97c245 fix: reduce status flapping, add IPv6 ping, color manual web services
Addresses three reports from issue #196:

- Ping now sends 2 probes with a ~2s timeout (was 1 probe / 1s) so a
  single dropped packet or a slow IoT/ESPHome device no longer flaps a
  node offline (#196.1, #196.2).
- IPv6-only devices (e.g. Alexa) are now pinged over IPv6: ping6 on
  macOS, -6 flag on Linux/Windows, detected via inet_pton (#196.3).
- Manually-added services carry no category and so always rendered grey
  even when they were reachable HTTP/HTTPS. A resolvable web URL now
  falls back to the web colour (#196.9).

ha-relevant: yes
2026-06-09 21:20:05 +02:00
Rémy a39d4d4406 Merge pull request #197 from Pouzor/some-fixes
Security fixes: Dependabot + code scanning alerts
2026-06-09 17:26:56 +02:00
Pouzor 7c2417f5a9 fix(security): resolve code scanning alerts
Add least-privilege 'permissions: contents: read' to quality, security
and docker-ci workflows (actions/missing-workflow-permissions).

Harden markdown table cell escaping in exportMarkdown: escape backslash
before pipe and collapse newlines so untrusted values can't break the
table (js/incomplete-sanitization). Add regression tests.

ha-relevant: maybe
2026-06-09 17:15:26 +02:00
Pouzor ea66e6c9c7 chore: bump hono to 4.12.21+ and ignore versioned db backups
Add npm override forcing hono>=4.12.21 to resolve 4 Dependabot alerts
(transitive via shadcn CLI, dev-only). Add *.db.back-* to .gitignore
to exclude versioned database backups.

ha-relevant: no
2026-06-09 16:29:52 +02:00
Rémy f78bac6e6b Merge pull request #192 from Pouzor/chore/bump-2.4.0
chore: bump version to 2.4.0
v2.4.0
2026-06-05 16:24:23 +02:00
Pouzor a0bfb97517 chore: bump version to 2.4.0
ha-relevant: no
2026-06-05 16:21:43 +02:00
Rémy ff9c4735e0 Merge pull request #191 from Pouzor/fix/persist-show-port-numbers
fix(nodes): persist show_port_numbers across reloads
2026-06-05 16:04:27 +02:00
Pouzor 2f3e986d4a fix(nodes): persist show_port_numbers across reloads
The Show Port Numbers toggle reset on every reload because the backend
never stored it: the column was missing from the Node model and the field
was stripped by NodeSave/NodeBase/NodeUpdate schemas, so canvas/save
silently dropped it.

Add the show_port_numbers column (idempotent migration), and the field to
the node schemas so it round-trips through save/load.

Add regression tests covering persistence and the default-false case.

Fixes #184

ha-relevant: yes
2026-06-05 15:53:58 +02:00
Rémy 0a4bac30ac Merge pull request #190 from Pouzor/fix/save-button-event-arg
fix(canvas): Save button no-op due to leaked click event
2026-06-05 15:29:51 +02:00
Pouzor ba2f6564ec fix(canvas): Save button no-op due to leaked click event
The Toolbar and Sidebar Save buttons wired onClick={onSave} directly, so
React passed the MouseEvent as the first argument. handleSave treats its
first arg as a designIdOverride, corrupting design_id and making the save
fail silently. Ctrl+S worked because it calls handleSave() with no args.

Wrap both handlers as onClick={() => onSave()} so no event leaks through.

Add regression tests asserting onSave is called with zero arguments.

Fixes #186

ha-relevant: no
2026-06-05 15:22:13 +02:00
Rémy 754a79d21f Merge pull request #189 from Pouzor/feat/cross-design-copy-paste
feat(canvas): cross-design copy/paste + persisted Hide-IP in Settings
2026-06-05 14:24:33 +02:00
Pouzor b52bbc6d9f feat(settings): move Hide IP toggle into Settings modal, persist it
Hide-IP was a sidebar button held only in memory, so it reset on reload.
Moved it into the Settings modal Canvas section and persist it to
localStorage (new ipDisplay util); the canvas store now seeds hideIp from
storage and writes through on toggleHideIp/setHideIp. Settings is now also
reachable in standalone (no-backend) builds, with the backend-only status
interval guarded so the modal still works there.

ha-relevant: yes
2026-06-05 11:26:28 +02:00
Pouzor c67b1775a5 feat(canvas): copy/paste nodes across designs
Clipboard now holds nodes + internal edges and survives design switches
(loadCanvas no longer clears it), so a selection copied in one design can
be pasted into another. Copy pulls in children of selected groups/
containers; paste remaps node/edge/parent IDs and lands the bounding-box
center under the cursor (or viewport center). Shortcut handling moved into
CanvasContainer for flow-coordinate projection.

ha-relevant: yes
2026-06-05 10:52:44 +02:00
Rémy 5940e11860 Merge pull request #188 from Pouzor/refactor/settings-modal
refactor(settings): move sidebar settings into a modal
2026-06-05 10:17:22 +02:00
Pouzor fa8f949b45 refactor(settings): move sidebar settings into a modal
Clicking Settings now opens a dialog with the status-check interval and
canvas snap options plus Save/Cancel, replacing the inline left-panel view.
Removes the 'settings' Sidebar view and SettingsPanel component.

ha-relevant: maybe
2026-06-05 10:00:39 +02:00
Rémy 6e17a2afc6 Merge pull request #187 from Pouzor/fix/edge-anim-css
refactor(edges): CSS animations for snake/flow edges (drop SVG SMIL)
2026-06-05 02:03:06 +02:00
Pouzor 2375efa6b7 refactor(edges): use CSS animations for snake/flow edges instead of SVG SMIL
Snake and flow edge animations used SVG SMIL <animate repeatCount="indefinite">.
SMIL keeps running while a tab is hidden (CSS animations pause) and grows memory
in Chrome over long-lived background tabs. Convert both to CSS @keyframes
(homelable-snake / homelable-flow), matching the existing basic-dash pattern.

Identical visuals and timings; bidirectional snake yo-yos via `alternate`.
Adds a regression test asserting animated edges emit zero <animate> nodes and a
CSS animation instead.

ha-relevant: yes
2026-06-05 01:54:54 +02:00
Rémy 61fbebb13e Merge pull request #185 from Pouzor/feat/liveview-design-param
feat(liveview): header View link opens active design as read-only canvas
2026-06-04 21:35:08 +02:00
Pouzor 05fb67e7f0 feat(liveview): header View link opens active design as read-only canvas
Add an authenticated GET /api/v1/liveview/config endpoint exposing the
configured LIVEVIEW_KEY to logged-in admins, so the UI builds a
ready-to-use share link. The header View button opens
/view?key=...&design=<activeDesignId>; LiveView forwards ?design=<id>
to the public endpoint, which renders that design's canvas.

ha-relevant: maybe
2026-06-04 14:10:29 +02:00
Rémy 1a426f6301 Merge pull request #177 from pranjal-joshi/feat/electrical
feat: Multi Design Canvas + new node type for electrical devices
2026-06-04 13:20:22 +02:00
Pouzor f56dfea838 fix(liveview): allow zoom-out to 0.25 so large infra fits
View-only mode set no minZoom, so React Flow defaulted to 0.5 and big
canvases couldn't zoom out far enough to fit. Match the editor's bounds
(minZoom 0.25, maxZoom 2.5). Add a regression test asserting the props.

ha-relevant: maybe
2026-06-04 13:00:29 +02:00
Pouzor 6784b8f6be refactor(ui): harden pending-devices keyboard handler
Address review nits on the close/Enter fix:
- Add statusFilter to the keydown effect deps so Enter dispatches the
  correct bulk action regardless of whether the device list changes
- Drop redundant title="Close" on the X (aria-label is enough)

ha-relevant: maybe
2026-06-03 01:31:50 +02:00
Pouzor 01fce64b2c fix(ui): Enter restores hidden devices instead of approving them
In the Hidden view the only bulk action is Restore, but the Enter
shortcut in select mode always called handleBulkApprove — approving
hidden devices. Dispatch on statusFilter: restore when hidden, approve
when pending. Add regression tests for both views.

ha-relevant: maybe
2026-06-03 01:09:36 +02:00
Pouzor 79093fdcfb fix(ui): close pending/hidden devices modal via the X on Firefox
The close X used a raw <button onClick={onClose}>, bypassing Base UI's
dismissal. Outside-click worked (it goes through onOpenChange) but the
synthetic click on the X was dropped on Firefox/Windows. Route the X
through Base UI's DialogClose, the same path as outside-click and the
library's default close button. Add a regression test.

ha-relevant: maybe
2026-06-03 00:59:28 +02:00
Pouzor 4ce8a12720 test: guard legacy -> multi-design migration (no data loss on upgrade)
Builds a real pre-designs database (nodes/edges without design_id, integer
canvas_state PK) and asserts init_db() adopts everything into a single
default Network Topology design with the viewport preserved, and that the
migration is idempotent across reboots. The rest of the suite uses create_all
and never exercised this in-place upgrade path.

ha-relevant: yes
2026-06-02 16:52:42 +02:00
Pouzor cc2cc72482 Merge remote-tracking branch 'origin/main' into feat/electrical 2026-06-02 16:10:46 +02:00
Pouzor 12d527aad6 feat: manage canvases with custom name and icon
Make designs (canvases) fully user-manageable: create with a chosen name
and icon, rename, change icon, and delete. Replaces the hardcoded
"New Electrical Design" button with a generic "New Canvas" flow.

- Add Design.icon column + migration that backfills legacy rows
  (electrical -> zap, others -> dashboard)
- DesignModal: name input + curated lucide icon picker (create + edit)
- Sidebar switcher gains per-canvas edit/delete; delete guards the last
  canvas and confirms
- designStore: addDesign/updateDesign/removeDesign with active reassignment
- Fix data loss on design switch: abort load when the save fails and keep
  unsaved edits; skip the save-old step when the previous canvas was deleted
- designsApi create/update carry icon; design_type kept for back-compat

Tests: backend design CRUD (icon + cascade + last-canvas guard), designStore
actions, designIcons resolver, DesignModal create/edit/validation.

ha-relevant: yes
2026-06-02 15:47:57 +02:00
Rémy 73b479a408 Merge pull request #182 from Pouzor/chore/dependabot-qs-brace-expansion
chore(deps): fix Dependabot alerts (qs, brace-expansion)
2026-06-02 11:04:27 +02:00
Pouzor 16183285b5 chore(deps): bump qs to 6.15.2 and brace-expansion to 5.0.6
Fixes Dependabot alerts #1 (brace-expansion DoS) and #2 (qs stringify
DoS). Lockfile-only, transitive deps via shadcn/ts-morph/express.

ha-relevant: no
2026-06-02 10:10:04 +02:00
Rémy 3b3cc1eba0 Merge pull request #181 from Pouzor/chore/bump-2.3.0
chore: bump version to 2.3.0
v2.3.0
2026-05-31 23:58:05 +02:00