diff --git a/src/app/api/models/route.ts b/src/app/api/models/route.ts index 7b943a64..e5670103 100644 --- a/src/app/api/models/route.ts +++ b/src/app/api/models/route.ts @@ -972,7 +972,17 @@ export async function GET( includeGemini = true; } else if (providerFilter === "kie") { // Only Kie requested - no external API calls needed (hardcoded models) - includeKie = true; + if (kieKey) { + includeKie = true; + } else { + return NextResponse.json( + { + success: false, + error: "Kie API key required. Add KIE_API_KEY to .env.local or configure in Settings.", + }, + { status: 400 } + ); + } } else if (providerFilter === "wavespeed") { if (wavespeedKey) { // WaveSpeed requested with key - fetch from API diff --git a/src/components/ProjectSetupModal.tsx b/src/components/ProjectSetupModal.tsx index a8f6ba4d..2aa64262 100644 --- a/src/components/ProjectSetupModal.tsx +++ b/src/components/ProjectSetupModal.tsx @@ -383,12 +383,13 @@ export function ProjectSetupModal({ return (
{ + if (e.target === e.currentTarget) onClose(); + }} onWheelCapture={(e) => e.stopPropagation()} >
e.stopPropagation()} onKeyDown={handleKeyDown} >
diff --git a/src/components/edges/EditableEdge.tsx b/src/components/edges/EditableEdge.tsx index 32e57291..eee67525 100644 --- a/src/components/edges/EditableEdge.tsx +++ b/src/components/edges/EditableEdge.tsx @@ -18,11 +18,17 @@ interface EdgeData extends WorkflowEdgeData { } // Colors for different connection types (dimmed for softer appearance) -const EDGE_COLORS = { - image: "#0d9668", // Dimmed green for image connections - prompt: "#2563eb", // Dimmed blue for prompt connections - default: "#64748b", // Dimmed gray for unknown - pause: "#ea580c", // Dimmed orange for paused edges +const EDGE_COLORS: Record = { + image: "#0d9668", // Green for image connections + prompt: "#2563eb", // Blue for prompt connections + default: "#64748b", // Gray for unknown + pause: "#ea580c", // Orange for paused edges + reference: "#52525b", // Gray for reference connections + video: "#a855f7", // Purple for video connections + audio: "#f97316", // Orange for audio connections + text: "#2563eb", // Blue for text connections + "3d": "#06b6d4", // Cyan for 3D connections + easeCurve: "#f59e0b", // Amber for ease curve connections }; export function EditableEdge({ @@ -69,15 +75,22 @@ export function EditableEdge({ const edgeColor = useMemo(() => { if (hasPause) return EDGE_COLORS.pause; // Use source handle to determine color (or target if source is not available) - const handleType = sourceHandleId || targetHandleId; - if (handleType === "image") return EDGE_COLORS.image; - if (handleType === "prompt") return EDGE_COLORS.prompt; - return EDGE_COLORS.default; + // Strip numeric suffixes (e.g., "image-0" -> "image") for lookup + const handleType = sourceHandleId || targetHandleId || ""; + const normalizedType = handleType.replace(/-\d+$/, ""); + return EDGE_COLORS[normalizedType] || EDGE_COLORS.default; }, [hasPause, sourceHandleId, targetHandleId]); // Reference shared gradient by color key + selection state const gradientId = useMemo(() => { - const colorKey = hasPause ? "pause" : (sourceHandleId || targetHandleId || "default"); + if (hasPause) { + const selectionKey = isConnectedToSelection ? "active" : "dimmed"; + return getSharedGradientId("pause", selectionKey); + } + const handleType = sourceHandleId || targetHandleId || "default"; + const normalizedType = handleType.replace(/-\d+$/, ""); + // Use the normalized type if it exists in EDGE_COLORS, otherwise fall back to "default" + const colorKey = normalizedType in EDGE_COLORS ? normalizedType : "default"; const selectionKey = isConnectedToSelection ? "active" : "dimmed"; return getSharedGradientId(colorKey, selectionKey); }, [hasPause, sourceHandleId, targetHandleId, isConnectedToSelection]); diff --git a/src/components/edges/SharedEdgeGradients.tsx b/src/components/edges/SharedEdgeGradients.tsx index 174ff1e7..d41f09dd 100644 --- a/src/components/edges/SharedEdgeGradients.tsx +++ b/src/components/edges/SharedEdgeGradients.tsx @@ -10,6 +10,11 @@ const EDGE_COLORS: Record = { default: "#64748b", pause: "#ea580c", reference: "#52525b", + video: "#a855f7", + audio: "#f97316", + text: "#2563eb", + "3d": "#06b6d4", + easeCurve: "#f59e0b", }; const SELECTION_STATES = ["active", "dimmed"] as const; diff --git a/src/components/nodes/ControlPanel.tsx b/src/components/nodes/ControlPanel.tsx index 888b155d..aa526284 100644 --- a/src/components/nodes/ControlPanel.tsx +++ b/src/components/nodes/ControlPanel.tsx @@ -884,6 +884,7 @@ function EaseCurveControls({ node }: { node: Node }) { const removeEdge = useWorkflowStore((state) => state.removeEdge); const [showPresets, setShowPresets] = useState(false); const presetsButtonRef = useRef(null); + const presetsPopupRef = useRef(null); useEffect(() => { if (!showPresets) return; @@ -893,6 +894,7 @@ function EaseCurveControls({ node }: { node: Node }) { }; const handleClickOutside = (e: MouseEvent) => { if (presetsButtonRef.current?.contains(e.target as HTMLElement)) return; + if (presetsPopupRef.current?.contains(e.target as HTMLElement)) return; setShowPresets(false); }; @@ -1022,6 +1024,7 @@ function EaseCurveControls({ node }: { node: Node }) { {showPresets && typeof document !== 'undefined' && createPortal(