diff --git a/src/components/flows/flow-builder.tsx b/src/components/flows/flow-builder.tsx index 8010ae8..d644d76 100644 --- a/src/components/flows/flow-builder.tsx +++ b/src/components/flows/flow-builder.tsx @@ -1,4 +1,4 @@ -"use client"; +'use client'; /** * Linear-list flow editor. @@ -17,7 +17,7 @@ * are list-only and have no canvas analogue. */ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { CircleAlert, Plus, @@ -25,41 +25,41 @@ import { ChevronDown, ChevronUp, CornerDownRight, -} from "lucide-react"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; +} from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from '@/components/ui/select'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { cn } from "@/lib/utils"; -import { type ValidationIssue } from "@/lib/flows/validate"; +} from '@/components/ui/dropdown-menu'; +import { cn } from '@/lib/utils'; +import { type ValidationIssue } from '@/lib/flows/validate'; import { NODE_META, NodeIconChip, + groupNodeTypesByCategory, nodeColors, slugify, summarizeNode, type BuilderNode, type NodeType, -} from "./shared"; -import { NodeConfigForm } from "./forms/node-config-form"; -import { NodeKeySelect } from "./forms/fields"; -import { IssueLine } from "./validation-panel"; -import { - useFlowEditor, - type BuilderState, -} from "./flow-editor-state"; +} from './shared'; +import { NodeConfigForm } from './forms/node-config-form'; +import { NodeKeySelect } from './forms/fields'; +import { IssueLine } from './validation-panel'; +import { useFlowEditor, type BuilderState } from './flow-editor-state'; // ============================================================ // Local state shape — mirrors the DB but the configs are typed @@ -87,7 +87,7 @@ export function FlowBuilder() { // jump-to-node. The flash itself is read from context (flashKey) // so canvas + list share the same source of truth. const [expanded, setExpanded] = useState>( - () => new Set(state.nodes.map((n) => n.node_key)), + () => new Set(state.nodes.map((n) => n.node_key)) ); const nodeRefs = useRef>(new Map()); @@ -99,7 +99,7 @@ export function FlowBuilder() { const key = addNodeCtx(type); setExpanded((prev) => new Set([...prev, key])); }, - [addNodeCtx], + [addNodeCtx] ); const removeNode = useCallback( @@ -111,7 +111,7 @@ export function FlowBuilder() { return next; }); }, - [removeNodeCtx], + [removeNodeCtx] ); const toggleExpanded = useCallback((key: string) => { @@ -128,7 +128,7 @@ export function FlowBuilder() { if (el) nodeRefs.current.set(key, el); else nodeRefs.current.delete(key); }, - [], + [] ); // React to validator jumps via the shared flashKey. We DERIVE the @@ -147,7 +147,7 @@ export function FlowBuilder() { // committed any expand-induced layout shift. requestAnimationFrame(() => { const el = nodeRefs.current.get(flashKey); - el?.scrollIntoView({ behavior: "smooth", block: "center" }); + el?.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); }, [flashKey]); @@ -156,24 +156,24 @@ export function FlowBuilder() { i.scope === "trigger")} + triggerIssues={issues.filter((i) => i.scope === 'trigger')} />
-

+

Nodes ({state.nodes.length})

{state.nodes.length === 0 ? ( -
- Add a Start node, then a Send buttons - {" "}node, then a Handoff — that's the welcome-menu - shape from the brief. +
+ Add a Start node, then a{' '} + Send buttons node, then a Handoff{' '} + — that's the welcome-menu shape from the brief.
) : ( state.nodes.map((node) => ( @@ -186,7 +186,7 @@ export function FlowBuilder() { isFlashed={flashKey === node.node_key} cardRef={setNodeRef(node.node_key)} issues={issues.filter( - (i) => i.scope === "node" && i.node_key === node.node_key, + (i) => i.scope === 'node' && i.node_key === node.node_key )} onToggle={() => toggleExpanded(node.node_key)} onUpdate={(patch) => updateNode(node.node_key, patch)} @@ -203,7 +203,6 @@ export function FlowBuilder() { ); } - // ============================================================ // Keyword trigger input // ============================================================ @@ -225,14 +224,14 @@ function KeywordsInput({ keywords: string[]; onChange: (keywords: string[]) => void; }) { - const [draft, setDraft] = useState(keywords.join(", ")); + const [draft, setDraft] = useState(keywords.join(', ')); function commit() { const parsed = draft - .split(",") + .split(',') .map((k) => k.trim()) .filter(Boolean); - setDraft(parsed.join(", ")); + setDraft(parsed.join(', ')); onChange(parsed); } @@ -242,7 +241,7 @@ function KeywordsInput({ onChange={(e) => setDraft(e.target.value)} onBlur={commit} onKeyDown={(e) => { - if (e.key === "Enter") { + if (e.key === 'Enter') { e.preventDefault(); commit(); } @@ -267,19 +266,21 @@ function TriggerPanel({ triggerIssues: ValidationIssue[]; }) { return ( -
-

Trigger

+
+

Trigger

- +
- {state.trigger_type === "keyword" && ( + {state.trigger_type === 'keyword' && (
-