From c002c5a4f139ae9a37abf98d70d0b845360c484c Mon Sep 17 00:00:00 2001 From: anti Date: Wed, 29 Apr 2026 00:29:46 -0400 Subject: [PATCH] feat(ui): forwards_l3 toggle in Inspector with destructive-recreate confirm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit W5's apply_update_decky now accepts a forwards_l3 flip on a live topology only when payload['force'] is true (the unforced flip raises MutationError to keep half-thinking operators from killing in-container state). Until this commit there was no UI surface that could even submit such a flip. Inspector grows a 'PROMOTE TO GATEWAY' / 'DEMOTE GATEWAY' button when a (non-observed) decky is selected. The handler: * On pending topologies → submits via editor.updateDecky immediately. No confirm dialog; no live containers to disturb. * On active/degraded topologies → window.confirm() explaining the destructive base recreate ('In-container state is lost; active sessions to it drop'), then submits with extras.force=true. useTopologyEditor.updateDecky grows an optional extras arg that threads force: true into the queued mutation payload. The pending CRUD path ignores it (no force needed when no containers exist). MazeNET.tsx wires a toggleGateway callback that handles the optimistic local state update, surfaces an enqueue toast on the active path, and lets the SSE forwarder reconcile when mutation.applied lands. --- .../src/components/MazeNET/Inspector.tsx | 50 +++++++++++++++++++ decnet_web/src/components/MazeNET/MazeNET.tsx | 35 +++++++++++++ .../components/MazeNET/useTopologyEditor.ts | 8 ++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/decnet_web/src/components/MazeNET/Inspector.tsx b/decnet_web/src/components/MazeNET/Inspector.tsx index 492c68b6..8022c5b0 100644 --- a/decnet_web/src/components/MazeNET/Inspector.tsx +++ b/decnet_web/src/components/MazeNET/Inspector.tsx @@ -34,6 +34,11 @@ interface Props { onLiveRemoveService?: (nodeName: string, slug: string) => Promise; /** Per-decky-eligible service slugs, fetched via useServiceRegistry. */ availableServices?: string[]; + /** Toggle ``forwards_l3`` (gateway) on the selected decky. When the + * topology is active/degraded the caller is responsible for the + * destructive-recreate confirm dialog and the ``force: true`` submit + * — this prop just relays the user's intent. */ + onToggleGateway?: (nodeId: string, nextValue: boolean) => Promise; onAddDecky?: (netId: string) => void; setSelection?: (sel: Selection) => void; pendingChanges?: number; @@ -44,6 +49,7 @@ const Inspector: React.FC = ({ selection, nets, nodes, edges, topologyStatus, onClose, onDeleteNet, onDeleteNode, onDeleteEdge, onRemoveService, onLiveAddService, onLiveRemoveService, availableServices = [], + onToggleGateway, onAddDecky, setSelection, pendingChanges = 0, className = '', @@ -257,6 +263,50 @@ const Inspector: React.FC = ({
NO EDGES
)} + {onToggleGateway && !isObserved && ( + + )} {onDeleteNode && (