diff --git a/frontend/src/components/network/nodes/deviceRegistry.ts b/frontend/src/components/network/nodes/deviceRegistry.ts
index d8e6124f..df3d2f33 100644
--- a/frontend/src/components/network/nodes/deviceRegistry.ts
+++ b/frontend/src/components/network/nodes/deviceRegistry.ts
@@ -20,7 +20,7 @@ export interface DeviceRenderConfig {
// Cloud (cyan) — external/internet-connected
// Infra (steel) — physical/passive hardware
export const NETWORK_COLOR = '#60a5fa'
-export const SECURITY_COLOR = '#f97316'
+export const SECURITY_COLOR = '#f87171'
export const COMPUTE_COLOR = '#34d399'
export const ENDPOINT_COLOR = '#fbbf24'
export const STORAGE_COLOR = '#a78bfa'
diff --git a/frontend/src/components/network/panels/AIAssistPanel.tsx b/frontend/src/components/network/panels/AIAssistPanel.tsx
index 53db5c3b..3ec8a96e 100644
--- a/frontend/src/components/network/panels/AIAssistPanel.tsx
+++ b/frontend/src/components/network/panels/AIAssistPanel.tsx
@@ -16,11 +16,13 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
const [mode, setMode] = useState<'replace' | 'merge'>('replace')
const [loading, setLoading] = useState(false)
const [error, setError] = useState
(null)
+ const [replaceConfirm, setReplaceConfirm] = useState(false)
const handleGenerate = useCallback(async () => {
if (!description.trim()) return
setLoading(true)
setError(null)
+ setReplaceConfirm(false)
try {
const result = await networkDiagramsApi.aiGenerate({
description: description.trim(),
@@ -38,6 +40,14 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
}
}, [description, mode, onGenerate, getExistingBounds])
+ // Reset confirm state when mode changes or panel collapses
+ const handleModeChange = (newMode: 'replace' | 'merge') => {
+ setMode(newMode)
+ setReplaceConfirm(false)
+ }
+
+ const needsReplaceConfirm = mode === 'replace' && hasNodes
+
if (!expanded) {
return (
@@ -60,7 +70,10 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
AI Generate
-
@@ -68,7 +81,7 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
setMode('replace')}
+ onClick={() => handleModeChange('replace')}
className={cn(
'rounded px-3 py-1 text-xs font-medium transition-colors',
mode === 'replace'
@@ -79,7 +92,7 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
Generate New
setMode('merge')}
+ onClick={() => handleModeChange('merge')}
className={cn(
'rounded px-3 py-1 text-xs font-medium transition-colors',
mode === 'merge'
@@ -91,7 +104,7 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
- {mode === 'replace' && hasNodes && (
+ {needsReplaceConfirm && (
@@ -113,8 +126,32 @@ export function AIAssistPanel({ onGenerate, getExistingBounds, hasNodes }: AIAss
{loading ? (
-
-
Generating your network diagram...
+
+
Generating your network diagram…
+
+ ) : needsReplaceConfirm && !replaceConfirm ? (
+
setReplaceConfirm(true)}
+ disabled={!description.trim()}
+ className="rounded border border-yellow-500/40 bg-yellow-500/10 px-4 py-2 text-xs font-medium text-yellow-400 hover:bg-yellow-500/20 disabled:opacity-50"
+ >
+ Replace Diagram…
+
+ ) : needsReplaceConfirm && replaceConfirm ? (
+
+ setReplaceConfirm(false)}
+ className="flex-1 rounded border border-default px-3 py-2 text-xs text-primary hover:bg-elevated"
+ >
+ Cancel
+
+
+ Yes, Replace
+
) : (
{ setDeleteConfirm(false) }, [selectedNode?.id, selectedEdge?.id])
+
const handlePropertyChange = useCallback((field: keyof DeviceProperties, value: string) => {
if (!selectedNode) return
const nodeData = selectedNode.data as unknown as DeviceNodeData
@@ -213,13 +218,33 @@ export function PropertiesPanel({