diff --git a/frontend/src/components/network/ContextMenu.tsx b/frontend/src/components/network/ContextMenu.tsx index ca4e4151..2b76a681 100644 --- a/frontend/src/components/network/ContextMenu.tsx +++ b/frontend/src/components/network/ContextMenu.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' import { - Copy, CopyPlus, Trash2, ClipboardPaste, BoxSelect, Maximize2, BringToFront, SendToBack, + Copy, CopyPlus, Trash2, ClipboardPaste, BoxSelect, Ungroup, Maximize2, BringToFront, SendToBack, AlignStartVertical, AlignCenterHorizontal, AlignEndVertical, AlignStartHorizontal, AlignCenterVertical, AlignEndHorizontal, AlignHorizontalSpaceAround, AlignVerticalSpaceAround, @@ -168,7 +168,7 @@ export function ContextMenu({ )} {canUngroup && ( { onUngroupSelection?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated"> - Ungroup + Ungroup )} > diff --git a/frontend/src/components/network/edges/ConnectionEdge.tsx b/frontend/src/components/network/edges/ConnectionEdge.tsx index 5b769eec..bc87b1be 100644 --- a/frontend/src/components/network/edges/ConnectionEdge.tsx +++ b/frontend/src/components/network/edges/ConnectionEdge.tsx @@ -54,7 +54,7 @@ function ConnectionEdgeComponent(props: EdgeProps) { {props.label && ( - + - + @@ -135,7 +135,7 @@ function DeviceNodeComponent({ id, data, selected, width, height }: NodeProps) { )} {CATEGORY_LABELS[category] ?? nodeData.deviceType.replace(/-/g, ' ')} diff --git a/frontend/src/components/network/nodes/GroupNode.tsx b/frontend/src/components/network/nodes/GroupNode.tsx index c65feac7..55156a4c 100644 --- a/frontend/src/components/network/nodes/GroupNode.tsx +++ b/frontend/src/components/network/nodes/GroupNode.tsx @@ -51,7 +51,7 @@ const GroupNodeComponent = ({ data, selected, id }: NodeProps) => { boxSizing: 'border-box', }} > - + {editing ? ( { if (e.key === 'Enter' || e.key === 'Escape') handleLabelCommit() e.stopPropagation() }} - className="text-[11px] font-medium bg-transparent border-none outline-none text-primary min-w-[40px] max-w-[200px]" + className="rounded-sm px-1.5 py-0.5 text-[11px] font-semibold bg-card/90 border-none outline-none min-w-[40px] max-w-[200px]" style={{ color }} /> ) : ( setEditing(true)} > diff --git a/frontend/src/components/network/panels/DeviceToolbar.tsx b/frontend/src/components/network/panels/DeviceToolbar.tsx index 22f59669..7ee2e0c7 100644 --- a/frontend/src/components/network/panels/DeviceToolbar.tsx +++ b/frontend/src/components/network/panels/DeviceToolbar.tsx @@ -151,22 +151,22 @@ export function DeviceToolbar({ deviceTypes, onDeviceTypesChange }: DeviceToolba {[ - { slug: 'subnet', label: 'Subnet' }, - { slug: 'vlan', label: 'VLAN' }, - { slug: 'site', label: 'Site' }, - { slug: 'dmz', label: 'DMZ' }, + { slug: 'subnet', label: 'Subnet', color: '#60a5fa' }, + { slug: 'vlan', label: 'VLAN', color: '#a78bfa' }, + { slug: 'site', label: 'Site', color: '#34d399' }, + { slug: 'dmz', label: 'DMZ', color: '#f87171' }, ].map(item => ( { - e.dataTransfer.setData('application/reactflow-group', JSON.stringify(item)) + e.dataTransfer.setData('application/reactflow-group', JSON.stringify({ slug: item.slug, label: item.label })) e.dataTransfer.effectAllowed = 'move' }} className="flex cursor-grab items-center gap-2 rounded px-2 py-1.5 text-xs text-primary hover:bg-elevated active:cursor-grabbing active:scale-[0.98] transition-transform" > - + {item.label} ))} diff --git a/frontend/src/components/network/panels/PropertiesPanel.tsx b/frontend/src/components/network/panels/PropertiesPanel.tsx index 0f870091..26c760eb 100644 --- a/frontend/src/components/network/panels/PropertiesPanel.tsx +++ b/frontend/src/components/network/panels/PropertiesPanel.tsx @@ -4,7 +4,7 @@ import { AlignStartVertical, AlignCenterHorizontal, AlignEndVertical, AlignStartHorizontal, AlignCenterVertical, AlignEndHorizontal, AlignHorizontalSpaceAround, AlignVerticalSpaceAround, - BoxSelect, Ungroup, + BoxSelect, Ungroup, MousePointer, } from 'lucide-react' import { cn } from '@/lib/utils' import type { DeviceProperties, DiagramEdge } from '@/types' @@ -235,12 +235,15 @@ export function PropertiesPanel({ if (!selectedNode && !selectedEdge) { return ( - - - Select a device or connection to edit its properties + + + + + + Select a device or connection - - Hover a device to preview its info + + Properties appear here. Hover a device to see a quick summary. ) diff --git a/frontend/src/components/network/ui/base-handle.tsx b/frontend/src/components/network/ui/base-handle.tsx index 4f57ca45..42cbb062 100644 --- a/frontend/src/components/network/ui/base-handle.tsx +++ b/frontend/src/components/network/ui/base-handle.tsx @@ -9,7 +9,7 @@ export function BaseHandle({ className, children, ...props }: ComponentProps = { } const STATUS_GLOW: Record = { - online: 'shadow-[0_0_8px_rgba(52,211,153,0.3)]', - offline: 'shadow-[0_0_8px_rgba(248,113,113,0.3)]', - degraded: 'shadow-[0_0_8px_rgba(250,204,21,0.3)]', + online: 'shadow-[0_0_6px_rgba(52,211,153,0.15)]', + offline: 'shadow-[0_0_6px_rgba(248,113,113,0.15)]', + degraded: 'shadow-[0_0_6px_rgba(250,204,21,0.15)]', unknown: '', } diff --git a/frontend/src/pages/NetworkDiagrams/DiagramEditor.tsx b/frontend/src/pages/NetworkDiagrams/DiagramEditor.tsx index ed3e1661..9b86d82a 100644 --- a/frontend/src/pages/NetworkDiagrams/DiagramEditor.tsx +++ b/frontend/src/pages/NetworkDiagrams/DiagramEditor.tsx @@ -853,11 +853,11 @@ function DiagramEditorInner() { {nodes.length === 0 && !loading && ( )} - {/* Keyboard shortcut hint button — bottom-right corner */} + {/* Keyboard shortcut hint button — above the MiniMap */} setShowShortcuts(true)} title="Keyboard shortcuts (?)" - className="absolute bottom-4 right-4 z-10 flex h-7 w-7 items-center justify-center rounded-full border border-default bg-card text-[11px] font-semibold text-muted-foreground hover:border-hover hover:text-primary" + className="absolute bottom-[175px] right-3 z-10 flex h-6 w-6 items-center justify-center rounded-full border border-default bg-card text-[11px] font-semibold text-muted-foreground hover:border-accent hover:text-accent transition-colors" > ? diff --git a/frontend/src/pages/NetworkDiagrams/index.tsx b/frontend/src/pages/NetworkDiagrams/index.tsx index 643a492d..e4771800 100644 --- a/frontend/src/pages/NetworkDiagrams/index.tsx +++ b/frontend/src/pages/NetworkDiagrams/index.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback, useMemo, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import { Plus, Search, Network, MoreHorizontal, Upload, ChevronDown, FileJson, FileOutput } from 'lucide-react' +import { Plus, Search, Network, MoreHorizontal, Upload, ChevronDown, FileJson, FileOutput, ExternalLink, Copy, Archive } from 'lucide-react' import { cn } from '@/lib/utils' import { networkDiagramsApi } from '@/api' import { toast } from '@/lib/toast' @@ -444,8 +444,16 @@ export default function NetworkDiagramsPage() { /> ) : ( - - + + + + + + + + + + )} {d.node_count > 0 && ( @@ -482,20 +490,24 @@ export default function NetworkDiagramsPage() { <> { e.stopPropagation(); navigate(`/network-diagrams/${d.id}`) }} - className="w-full px-3 py-1.5 text-left text-xs text-primary hover:bg-elevated" + className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-primary hover:bg-elevated" > + Open { e.stopPropagation(); handleDuplicate(d.id) }} - className="w-full px-3 py-1.5 text-left text-xs text-primary hover:bg-elevated" + className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-primary hover:bg-elevated" > + Duplicate + { e.stopPropagation(); setConfirmArchiveId(d.id) }} - className="w-full px-3 py-1.5 text-left text-xs text-red-400 hover:bg-elevated" + className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-red-400 hover:bg-elevated" > + Archive… >
- Select a device or connection to edit its properties +
+ Select a device or connection
- Hover a device to preview its info +
+ Properties appear here. Hover a device to see a quick summary.