Implement live routing feedback with WebSocket and UI enhancements#37
Implement live routing feedback with WebSocket and UI enhancements#37ndorin wants to merge 16 commits into
Conversation
…d display alert for untrusted certificates
…on component feat: update Routing to support synthetic tie lines for live routing feedback feat: modify Versions component layout for better responsiveness feat: extend apiSlice and routingFeedbackSlice to handle multiple sink routes
…and integrate with routing feedback
There was a problem hiding this comment.
Pull request overview
This PR adds live routing feedback and richer routing visualization in the UI by introducing a dedicated Redux slice/middleware for routing-feedback WebSocket messages, plus new multiview layout UI (draggable panel + canvas). It also includes small UX/layout improvements to the login form (password visibility toggle) and the Versions page (scrollable table container).
Changes:
- Add routing-feedback Redux state + middleware to manage a routing WebSocket session and ingest snapshot/delta messages.
- Enhance the Routing graph with live-only synthetic edges, signal-path tracing/highlighting, internal-node route overlays, and draggable multiview layout panels.
- Improve UI/UX: password visibility toggle on login and a more robust scrollable Versions table layout.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/store/store.ts | Wires the new routing-feedback reducer and middleware into the app store. |
| src/store/routingFeedbackSlice.ts | Adds Redux state/actions for routing snapshots, route deltas, layouts, and connection status. |
| src/store/routingFeedbackMiddleware.ts | Implements the routing feedback WebSocket lifecycle, reconnection, and message dispatching. |
| src/store/apiSlice.ts | Extends API types to include live routing feedback messages and multiview layout state. |
| src/shared/components/EyeIcon.tsx | Adds a lightweight inline eye/eye-slash icon for password visibility toggles. |
| src/features/Versions.tsx | Fixes layout so the versions table can scroll within a flex container. |
| src/features/RoutingDeviceNode.tsx | Adds layout toggle button support and an internal SVG route overlay for midpoint routes. |
| src/features/RoutingDeviceNode.module.scss | Styles the layout toggle button, route overlay, and label layering for readability. |
| src/features/Routing.tsx | Adds WebSocket session connection logic, live-only synthetic edges, edge/node path tracing/highlighting, and floating multiview panels. |
| src/features/MultiviewLayoutPanel.tsx | Introduces a draggable floating panel wrapper for per-device multiview layouts. |
| src/features/MultiviewLayoutPanel.module.scss | Styles the floating panel (light/dark variants, title bar, close button). |
| src/features/MultiviewLayoutCanvas.tsx | Renders the multiview canvas mock-up with clickable/labelled tiles and selection highlight. |
| src/features/MultiviewLayoutCanvas.module.scss | Styles the multiview canvas and tiles (hover/selected/empty states). |
| src/features/LoginForm.tsx | Adds a password visibility toggle button next to the password input. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
src/features/Routing.tsx:291
- TypeScript/ESM imports must be at the top level. This mid-file
import typewill cause a compile error. Move the MidpointRoute import to the main import block at the top of the file and remove this statement.
// ─── Signal path tracing ─────────────────────────────────────────────────────
import type { MidpointRoute } from "../store/apiSlice";
src/features/LoginForm.tsx:123
- The password visibility toggle button is removed from the tab order via
tabIndex={-1}, which makes it unreachable for keyboard users. Since this is an interactive control (and already has an aria-label), it should be focusable.
disabled={isLoading}
aria-label={showPassword ? "Hide password" : "Show password"}
aria-pressed={showPassword}
>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot please add an empty commit with the message "feat: increment minor version" so that the symantic release workflow will generate a new build |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/features/Routing.tsx:671
- The dagre layout effect is meant to run only when source data/filters change, but the dependency array includes
handleTileClick(andresolveSourceName).handleTileClickdepends onmidpointRoutes, so every WebSocket route update will recreate the callback, re-run dagre, rebuild nodes/edges, and clear selection (setSelectedEdgeIds(new Set()))—reintroducing the jitter/flicker issue from the previous review thread. Remove these unused deps so the effect doesn’t indirectly depend on live feedback state.
hideUnconnectedPorts,
darkMode,
sinkRoutes,
resolveSourceName,
handleTileClick,
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Done — added the empty commit |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/features/Routing.tsx:292
importstatements must be at the top-level of the module. This mid-file type import will cause a TS/ESM parse error and also duplicates the existing MidpointRoute import at the top of the file.
// ─── Signal path tracing ─────────────────────────────────────────────────────
/**
* Given a clicked edge, traces the full signal path from source to sink through
src/features/Routing.tsx:673
- This dagre layout effect is intended to run only when the underlying graph/filter inputs change, but the dependency list includes
resolveSourceNameandhandleTileClickeven though they are not used in the effect. BecausehandleTileClickdepends onmidpointRoutes, this reintroduces frequent dagre re-layouts and selection resets on WebSocket updates.
handleTileClick,
setNodes,
setEdges,
]);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
src/features/Routing.tsx:672
- The dagre layout effect will still re-run on every WebSocket midpoint update because the dependency array includes
handleTileClick(which is recreated whenevermidpointRouteschanges) andresolveSourceName, even though neither is used inside the effect. This reintroduces layout rebuilds and clears selection frequently. Remove the unused dependencies and preserve any existing selection by intersecting it with the new edge IDs instead of always resetting it.
setEdges(layoutEdges);
setSelectedEdgeIds(new Set());
}, [
data,
hiddenTypes,
hideUnconnected,
hiddenDevices,
hideUnconnectedPorts,
darkMode,
sinkRoutes,
resolveSourceName,
handleTileClick,
setNodes,
setEdges,
]);
src/features/RoutingDeviceNode.tsx:71
- This icon-only layout toggle button relies on
titlefor labeling, which is not a reliable accessible name. Add an explicitaria-label(and settype="button"to avoid any accidental form-submit behavior if this component is ever rendered inside a form).
<button
className={`nodrag ${styles.layoutToggleBtn}`}
onClick={(e) => {
e.stopPropagation();
onToggleLayoutPanel?.();
src/features/MultiviewLayoutPanel.tsx:72
- The drag logic registers
pointermove/pointeruplisteners onwindowand only removes them onpointerup. If the panel unmounts mid-drag (e.g. route change or close action while dragging), those listeners can remain attached and keep firing, causing leaks and unexpected state updates. Consider using Pointer Capture or an AbortController-based listener registration with a cleanup on unmount.
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", handlePointerUp);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/features/Routing.tsx:670
- The dagre layout effect is intended to avoid re-running on WebSocket updates, but
handleTileClickis in the dependency array and it changes whenevermidpointRouteschanges (it depends onmidpointRoutes). This will re-runbuildGraph/dagre and resetselectedEdgeIdson every routing feedback message, reintroducing layout jitter/selection flicker.
darkMode,
sinkRoutes,
resolveSourceName,
handleTileClick,
setNodes,
| // Build lookup maps: | ||
| // "deviceKey:portKey" → edge that ARRIVES at that input port | ||
| const edgeByDestPort = new Map<string, Edge>(); | ||
| // "deviceKey:portKey" → edge that LEAVES from that output port | ||
| const edgeBySrcPort = new Map<string, Edge>(); |
| <div | ||
| key={tile.tileNumber} | ||
| className={`${styles.tile}${isEmpty ? ` ${styles.tileEmpty}` : ""}${isSelected ? ` ${styles.tileSelected}` : ""}`} | ||
| style={{ | ||
| left: `${(tile.x / layout.canvasWidth) * 100}%`, | ||
| top: `${(tile.y / layout.canvasHeight) * 100}%`, | ||
| width: `${(tile.width / layout.canvasWidth) * 100}%`, | ||
| height: `${(tile.height / layout.canvasHeight) * 100}%`, | ||
| zIndex: tile.zOrder, | ||
| }} | ||
| title={`Tile ${tile.tileNumber}: ${sourceName}`} | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| onTileClick?.(tile); | ||
| }} | ||
| > |
| <button className={styles.closeBtn} onClick={onClose} title="Close"> | ||
| × | ||
| </button> |
This pull request introduces several new UI features and improvements, primarily focused on enhancing the routing device visualization and user experience. The main highlights include the addition of a draggable multiview layout panel and canvas, improved password visibility toggling on the login form, and visual enhancements for routing devices and version display.
New multiview layout visualization:
Added
MultiviewLayoutCanvasandMultiviewLayoutPanelcomponents, allowing users to view and interact with a visual representation of a device's multiview/tile layout in a floating, draggable panel. This includes full styling and support for both light and dark modes. [1] [2] [3] [4]Integrated a toggle button into
RoutingDeviceNodeto show/hide the multiview layout panel for devices that support it, and added an internal SVG overlay to visualize active routes within a device node. [1] [2] [3] [4] [5] [6] [7]User experience improvements:
EyeIconcomponent to avoid extra dependencies. [1] [2] [3] [4]Styling and layout fixes:
Versionspage layout to ensure the table is scrollable and headers remain visible, fixing overflow and flex issues. [1] [2]