From 338a006e64ee29ea0533da02735b354a5704a42c Mon Sep 17 00:00:00 2001 From: TerrifiedBug Date: Thu, 5 Mar 2026 11:56:00 +0000 Subject: [PATCH 1/3] fix: sanitize component key input in pipeline editor Automatically convert spaces to underscores and strip invalid characters as users type component keys. Adds helper text showing the allowed format. Prevents cryptic server-side validation errors from Vector's identifier requirements. --- src/components/flow/detail-panel.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/flow/detail-panel.tsx b/src/components/flow/detail-panel.tsx index 46f58245..e5f53604 100644 --- a/src/components/flow/detail-panel.tsx +++ b/src/components/flow/detail-panel.tsx @@ -140,9 +140,13 @@ export function DetailPanel() { ); const handleKeyChange = useCallback( - (key: string) => { + (raw: string) => { if (selectedNodeId) { - updateNodeKey(selectedNodeId, key); + const sanitized = raw + .replace(/\s+/g, "_") + .replace(/[^a-zA-Z0-9_]/g, "") + .replace(/^(\d+)/, "_$1"); + updateNodeKey(selectedNodeId, sanitized); } }, [selectedNodeId, updateNodeKey], @@ -266,6 +270,9 @@ export function DetailPanel() { onChange={(e) => handleKeyChange(e.target.value)} disabled={isSystemLocked} /> +

+ Letters, numbers, and underscores only (e.g. traefik_logs) +

{/* Enabled toggle */} From 86add35aa9fcbef9980157484a64576b05d832b7 Mon Sep 17 00:00:00 2001 From: TerrifiedBug Date: Thu, 5 Mar 2026 12:33:40 +0000 Subject: [PATCH 2/3] fix: address greptile review findings --- src/components/flow/detail-panel.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/flow/detail-panel.tsx b/src/components/flow/detail-panel.tsx index e5f53604..204330dc 100644 --- a/src/components/flow/detail-panel.tsx +++ b/src/components/flow/detail-panel.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useMemo } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { useParams } from "next/navigation"; import { Copy, Trash2, Lock, Info } from "lucide-react"; import { useFlowStore } from "@/stores/flow-store"; @@ -122,6 +122,13 @@ export function DetailPanel() { ? nodes.find((n) => n.id === selectedNodeId) : null; + const storeKey = (selectedNode?.data as { componentKey?: string })?.componentKey ?? ""; + const [displayKey, setDisplayKey] = useState(storeKey); + + useEffect(() => { + setDisplayKey(storeKey); + }, [storeKey]); + const upstream = useMemo( () => selectedNodeId @@ -141,12 +148,13 @@ export function DetailPanel() { const handleKeyChange = useCallback( (raw: string) => { + setDisplayKey(raw); if (selectedNodeId) { const sanitized = raw .replace(/\s+/g, "_") .replace(/[^a-zA-Z0-9_]/g, "") .replace(/^(\d+)/, "_$1"); - updateNodeKey(selectedNodeId, sanitized); + if (sanitized) updateNodeKey(selectedNodeId, sanitized); } }, [selectedNodeId, updateNodeKey], @@ -266,7 +274,7 @@ export function DetailPanel() { handleKeyChange(e.target.value)} disabled={isSystemLocked} /> From 79a4e710fc2be2fc960892ec70086d6c0416ee38 Mon Sep 17 00:00:00 2001 From: TerrifiedBug Date: Thu, 5 Mar 2026 12:40:50 +0000 Subject: [PATCH 3/3] fix: reset displayKey to storeKey when sanitized input is empty --- src/components/flow/detail-panel.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/flow/detail-panel.tsx b/src/components/flow/detail-panel.tsx index 204330dc..e93a1ad4 100644 --- a/src/components/flow/detail-panel.tsx +++ b/src/components/flow/detail-panel.tsx @@ -148,16 +148,20 @@ export function DetailPanel() { const handleKeyChange = useCallback( (raw: string) => { - setDisplayKey(raw); if (selectedNodeId) { const sanitized = raw .replace(/\s+/g, "_") .replace(/[^a-zA-Z0-9_]/g, "") .replace(/^(\d+)/, "_$1"); - if (sanitized) updateNodeKey(selectedNodeId, sanitized); + if (sanitized) { + setDisplayKey(raw); + updateNodeKey(selectedNodeId, sanitized); + } else { + setDisplayKey(storeKey); + } } }, - [selectedNodeId, updateNodeKey], + [selectedNodeId, updateNodeKey, storeKey], ); const handleDelete = useCallback(() => {