From 8ee262c6e9f20c0e14e4215cb14f7c198fffb912 Mon Sep 17 00:00:00 2001 From: flosch62 Date: Tue, 30 Jun 2026 15:51:34 +0200 Subject: [PATCH 1/4] clear label filter --- .../frontend/src/components/AppToolbar.tsx | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/cable_map/frontend/src/components/AppToolbar.tsx b/cable_map/frontend/src/components/AppToolbar.tsx index 06f89bf..8e14fc3 100644 --- a/cable_map/frontend/src/components/AppToolbar.tsx +++ b/cable_map/frontend/src/components/AppToolbar.tsx @@ -16,6 +16,7 @@ import MenuItem from '@mui/material/MenuItem'; import Checkbox from '@mui/material/Checkbox'; import ListItemText from '@mui/material/ListItemText'; import SearchIcon from '@mui/icons-material/SearchOutlined'; +import CloseIcon from '@mui/icons-material/CloseOutlined'; import LightModeIcon from '@mui/icons-material/LightModeOutlined'; import DarkModeIcon from '@mui/icons-material/DarkModeOutlined'; import SensorsIcon from '@mui/icons-material/SensorsOutlined'; @@ -98,25 +99,55 @@ function AppToolbar({ )} {ready && nodeLabelOptions.length > 0 && ( - + + + {selectedLabels.length > 0 && ( + + { e.preventDefault(); e.stopPropagation(); }} + onClick={(e) => { e.stopPropagation(); onSelectedLabelsChange([]); }} + sx={{ + position: 'absolute', + right: 28, + top: '50%', + transform: 'translateY(-50%)', + zIndex: 1, + width: 24, + height: 24, + p: 0.25, + color: 'text.secondary', + '&:hover': { color: 'text.primary' }, + }} + > + + + + )} + )} {namespaces.length > 1 && namespace && ( From 2fd713a81c921423d15a2e806d4332bc0694b3d1 Mon Sep 17 00:00:00 2001 From: flosch62 Date: Tue, 30 Jun 2026 16:01:04 +0200 Subject: [PATCH 2/4] better legend --- .../frontend/src/components/CanvasLegend.tsx | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/cable_map/frontend/src/components/CanvasLegend.tsx b/cable_map/frontend/src/components/CanvasLegend.tsx index 1d46533..b5ce7b7 100644 --- a/cable_map/frontend/src/components/CanvasLegend.tsx +++ b/cable_map/frontend/src/components/CanvasLegend.tsx @@ -11,7 +11,7 @@ import { CABLE_COLOR, LLDP_COLOR } from '../graph'; import { useLldp } from '../lldp'; const LEGEND: Array<[string, string]> = [ - ['Fabric (ISL)', CABLE_COLOR.isl], ['Edge', CABLE_COLOR.edge], ['Local LAG', CABLE_COLOR.lag], ['ESI / MH-LAG', CABLE_COLOR.mlag], + ['InterSwitch', CABLE_COLOR.isl], ['Edge', CABLE_COLOR.edge], ['Local LAG', CABLE_COLOR.lag], ['Multihome LAG', CABLE_COLOR.mlag], ]; const LLDP_LEGEND: Array<[string, string]> = [ ['LLDP confirmed', LLDP_COLOR.ok], ['Mismatch', LLDP_COLOR.mismatch], ['No neighbor', LLDP_COLOR.none], @@ -32,7 +32,7 @@ export default function CanvasLegend() { - + {(lldpOn ? LLDP_LEGEND : LEGEND).map(([label, swatch]) => ( @@ -40,11 +40,20 @@ export default function CanvasLegend() { ))} - - {lldpOn - ? 'LLDP overlay — cables coloured by neighbour match vs the configured remote' - : 'Hover to trace · click a cable or port to pin · click a switch for its panel'} - + {lldpOn ? ( + + LLDP overlay — cables coloured by neighbour match vs the configured remote + + ) : ( + + + Select a node to highlight its connections + + + Double click on a node to see the front-panel layout + + + )} From 0e393f4ac3cb16b006a24ca4c678c8b270f4bc1b Mon Sep 17 00:00:00 2001 From: flosch62 Date: Tue, 30 Jun 2026 16:07:12 +0200 Subject: [PATCH 3/4] show icon and node sync state --- cable_map/frontend/src/SwitchNode.tsx | 32 +++++++++++++++++++++++--- cable_map/frontend/src/graph/layout.ts | 8 +++++++ cable_map/frontend/src/graph/types.ts | 2 ++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cable_map/frontend/src/SwitchNode.tsx b/cable_map/frontend/src/SwitchNode.tsx index 4f2cd72..2be2728 100644 --- a/cable_map/frontend/src/SwitchNode.tsx +++ b/cable_map/frontend/src/SwitchNode.tsx @@ -1,4 +1,4 @@ -import { memo, useCallback, useMemo, useState, type ReactNode } from 'react'; +import { memo, useCallback, useMemo, useState, type CSSProperties, type ReactNode } from 'react'; import { Handle, Position, type NodeProps } from '@xyflow/react'; import { CABLE_COLOR, LLDP_COLOR, COMPACT_SWITCH_H, COMPACT_SWITCH_W, @@ -16,6 +16,32 @@ const RoleIcons: Record = { spine: spineIcon, superspine: superspineIcon, leaf: leafIcon, borderleaf: leafIcon, }; +type RoleIconStyle = CSSProperties & { + '--color-icon-bg': string; + '--color-icon-fg': string; +}; + +const roleIconStyle = (size: number, synced?: boolean): RoleIconStyle => ({ + lineHeight: 0, + width: size, + height: size, + flexShrink: 0, + '--color-icon-bg': synced ? 'var(--eda-green-600)' : 'var(--cell-free-bg)', + '--color-icon-fg': synced ? '#fff' : 'var(--muted)', +}); + +function RoleIcon({ icon, size, role, nodeState, synced }: { icon?: string; size: number; role: string; nodeState?: string; synced?: boolean }) { + if (!icon) return null; + const state = nodeState ? ` · node-state: ${nodeState}` : ''; + return ( + + ); +} + const CELL = 13, CELL_GAP = 3; // occupied cells fill with their cable-kind colour, or their LLDP status while the overlay is on const fillFor = (info: PortInfo, lldp: boolean) => @@ -285,7 +311,7 @@ function CompactSwitchNode({ id, data, selected }: NodeProps) {
- {icon && } + {d.name}
@@ -370,7 +396,7 @@ function DetailedSwitchNode({ id, data, selected }: NodeProps) { }} >
- {icon && } + {d.name} {d.platform} diff --git a/cable_map/frontend/src/graph/layout.ts b/cable_map/frontend/src/graph/layout.ts index 310b8e8..38d4d99 100644 --- a/cable_map/frontend/src/graph/layout.ts +++ b/cable_map/frontend/src/graph/layout.ts @@ -27,6 +27,11 @@ function podKey(n: Topology['nodes'][number]): string { return labelOf(n, 'eda.nokia.com/pod') ?? 'pod'; } +function nodeStateOf(n: Topology['nodes'][number]): string | undefined { + const value = n.raw?.status?.['node-state']; + return value == null ? undefined : String(value); +} + function breakoutsWithInferredChannels(explicit: Record | undefined, ports: PortInfo[]): Record | undefined { const inferred = new Map(); for (const port of ports) { @@ -154,6 +159,7 @@ export function layoutGraph({ topo, compact, tiered, endpoints, switchPorts, ste const rfNodes: RFNode[] = topo.nodes.map((n) => { const nodePorts = switchPorts.get(n.name) ?? []; + const nodeState = nodeStateOf(n); return { id: n.name, type: 'switch', position: pos.get(n.name) ?? { x: 0, y: 0 }, width: swW(n.name), @@ -162,6 +168,8 @@ export function layoutGraph({ topo, compact, tiered, endpoints, switchPorts, ste initialHeight: swH(n.name), data: { name: n.name, role: n.role, platform: n.platform, + nodeState, + synced: nodeState?.trim().toLowerCase() === 'synced', portCount: portCountOf(stencilOf(n.name)), rows: rowsOf(stencilOf(n.name)), ports: nodePorts, layout: compact ? undefined : layoutOf(stencilOf(n.name)), aspect: aspectOf(stencilOf(n.name)), breakouts: compact ? undefined : breakoutsWithInferredChannels(n.breakouts, nodePorts), compact, diff --git a/cable_map/frontend/src/graph/types.ts b/cable_map/frontend/src/graph/types.ts index 26381dc..faaff3a 100644 --- a/cable_map/frontend/src/graph/types.ts +++ b/cable_map/frontend/src/graph/types.ts @@ -20,6 +20,8 @@ export type PortInfo = { export type SwitchData = { name: string; role: string; platform: string; + nodeState?: string; + synced?: boolean; portCount: number; rows: number; // physical cage rows, mirrored on the canvas ports: PortInfo[]; // occupied ports only (free cells inferred) From 0b8e4fff99bb63f5a37b5158afcefbd6e02e872a Mon Sep 17 00:00:00 2001 From: flosch62 Date: Tue, 30 Jun 2026 16:08:38 +0200 Subject: [PATCH 4/4] bump version --- cable_map/manifest.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cable_map/manifest.yaml b/cable_map/manifest.yaml index 5406064..8032038 100644 --- a/cable_map/manifest.yaml +++ b/cable_map/manifest.yaml @@ -15,11 +15,11 @@ spec: # The Go operator runtime image (serves the SPA + topology API, reads EDA state via EDK). # edabuilder bakes this container into the single OCI app image at publish/deploy time. - container: - image: ghcr.io/eda-labs/cable-map/operators/cable-map:v0.1.9 + image: ghcr.io/eda-labs/cable-map/operators/cable-map:v0.2.0 name: cable-map description: Cable-map topology and front-panel view for EDA fabrics. group: cable-map.eda.labs - image: ghcr.io/eda-labs/cable-map:v0.1.9 + image: ghcr.io/eda-labs/cable-map:v0.2.0 supportedCoreVersions: - v5.0.0-0 title: Cable Map