diff --git a/.planning/STATE.md b/.planning/STATE.md index 0f92ef8c..fc7f8463 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -10,18 +10,18 @@ See: .planning/PROJECT.md (updated 2026-01-09) ## Current Position Phase: 40 of 40 (Node Enhancements) -Plan: 2 of 4 in current phase +Plan: 1 of 4 in current phase Status: In Progress -Last activity: 2026-02-01 - Completed 40-02-PLAN.md (Connection numbering) +Last activity: 2026-02-01 - Completed 40-01-PLAN.md (OutputGallery node) -Progress: ███████████░ 94% +Progress: ███████████░ 95% ## Performance Metrics **Velocity:** -- Total plans completed: 38 -- Average duration: 6.7 min -- Total execution time: 4.28 hours +- Total plans completed: 39 +- Average duration: 6.6 min +- Total execution time: 4.37 hours **By Phase:** @@ -57,11 +57,11 @@ Progress: ███████████░ 94% | 33. Workflow Edit Safety | 2/2 | 5 min | 5 min | | 34. Agentic Workflow Editing | 3/3 | 13 min | 4.3 min | | 35. Large Workflow Handling | 3/3 | 18 min | 6 min | -| 40. Node Enhancements | 2/4 | 2 min | 2 min | +| 40. Node Enhancements | 1/4 | 5 min | 5 min | **Recent Trend:** -- Last 5 plans: <1 min, 10 min, 3 min, 10 min, 5 min, 2 min -- Trend: Phase 40 in progress - node enhancements (connection numbering complete) +- Last 5 plans: 10 min, 3 min, 10 min, 5 min, 2 min, 5 min +- Trend: Phase 40 in progress - node enhancements (OutputGallery complete) ## Accumulated Context @@ -184,6 +184,10 @@ Recent decisions affecting current work: - createdAt timestamp on edge data for stable connection ordering - Image edge sequence numbers shown only when 2+ connections to same target - EdgeToolbar displays "Image N" labels for multi-image connections +- OutputGallery node uses createPortal for full-screen lightbox (avoids z-index conflicts) +- Real-time image collection pattern: useMemo watching edges/nodes for live updates +- nowheel class for scroll isolation inside React Flow nodes +- Pink minimap color (#ec4899) for OutputGallery nodes ### Deferred Issues @@ -220,6 +224,6 @@ Recent decisions affecting current work: ## Session Continuity Last session: 2026-02-01 -Stopped at: Completed 40-02-PLAN.md (Connection numbering) +Stopped at: Completed 40-01-PLAN.md (OutputGallery node) Resume file: None -Next action: Continue Phase 40 - 2 more plans remaining (OutputGallery, ImageCompare, PromptConstructor) +Next action: Continue Phase 40 - 3 more plans remaining (Connection numbering, ImageCompare, PromptConstructor) diff --git a/src/components/ConnectionDropMenu.tsx b/src/components/ConnectionDropMenu.tsx index 9f4ff10f..ee6084f1 100644 --- a/src/components/ConnectionDropMenu.tsx +++ b/src/components/ConnectionDropMenu.tsx @@ -91,6 +91,15 @@ const TEXT_TARGET_OPTIONS: MenuOption[] = [ ), }, + { + type: "promptConstructor", + label: "Prompt Constructor", + icon: ( + + + + ), + }, { type: "nanoBanana", label: "Generate Image", @@ -161,6 +170,15 @@ const TEXT_SOURCE_OPTIONS: MenuOption[] = [ ), }, + { + type: "promptConstructor", + label: "Prompt Constructor", + icon: ( + + + + ), + }, { type: "llmGenerate", label: "LLM Generate", diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index 557ef427..73a2b9ee 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -23,6 +23,7 @@ import { ImageInputNode, AnnotationNode, PromptNode, + PromptConstructorNode, GenerateImageNode, GenerateVideoNode, LLMGenerateNode, @@ -48,6 +49,7 @@ const nodeTypes: NodeTypes = { imageInput: ImageInputNode, annotation: AnnotationNode, prompt: PromptNode, + promptConstructor: PromptConstructorNode, nanoBanana: GenerateImageNode, generateVideo: GenerateVideoNode, llmGenerate: LLMGenerateNode, @@ -88,6 +90,8 @@ const getNodeHandles = (nodeType: string): { inputs: string[]; outputs: string[] return { inputs: ["image"], outputs: ["image"] }; case "prompt": return { inputs: ["text"], outputs: ["text"] }; + case "promptConstructor": + return { inputs: ["text"], outputs: ["text"] }; case "nanoBanana": return { inputs: ["image", "text"], outputs: ["image"] }; case "generateVideo": @@ -886,6 +890,7 @@ export function WorkflowCanvas() { imageInput: { width: 300, height: 280 }, annotation: { width: 300, height: 280 }, prompt: { width: 320, height: 220 }, + promptConstructor: { width: 340, height: 280 }, nanoBanana: { width: 300, height: 300 }, generateVideo: { width: 300, height: 300 }, llmGenerate: { width: 320, height: 360 }, @@ -1369,6 +1374,8 @@ export function WorkflowCanvas() { return "#8b5cf6"; case "prompt": return "#f97316"; + case "promptConstructor": + return "#f472b6"; case "nanoBanana": return "#22c55e"; case "generateVideo": diff --git a/src/components/nodes/PromptNode.tsx b/src/components/nodes/PromptNode.tsx index 30dc26d5..2067ac5c 100644 --- a/src/components/nodes/PromptNode.tsx +++ b/src/components/nodes/PromptNode.tsx @@ -25,6 +25,10 @@ export function PromptNode({ id, data, selected }: NodeProps) { const [localPrompt, setLocalPrompt] = useState(nodeData.prompt); const [isEditing, setIsEditing] = useState(false); + // Variable naming dialog state + const [showVarDialog, setShowVarDialog] = useState(false); + const [varNameInput, setVarNameInput] = useState(nodeData.variableName || ""); + // Check if this node has any incoming text connections const hasIncomingTextConnection = useMemo(() => { return edges.some((edge) => edge.target === id && edge.targetHandle === "text"); @@ -82,6 +86,23 @@ export function PromptNode({ id, data, selected }: NodeProps) { [id, updateNodeData] ); + const handleSaveVariableName = useCallback(() => { + updateNodeData(id, { variableName: varNameInput || undefined }); + setShowVarDialog(false); + }, [id, varNameInput, updateNodeData]); + + const handleClearVariableName = useCallback(() => { + setVarNameInput(""); + updateNodeData(id, { variableName: undefined }); + setShowVarDialog(false); + }, [id, updateNodeData]); + + const handleVariableNameChange = useCallback((e: React.ChangeEvent) => { + // Allow only alphanumeric and underscore, max 30 chars + const sanitized = e.target.value.replace(/[^a-zA-Z0-9_]/g, "").slice(0, 30); + setVarNameInput(sanitized); + }, []); + return ( <> ) { data-handletype="text" /> -