Browse Source

feat(40-01): define OutputGallery types and register node in all touchpoints

- Add outputGallery to NodeType union in types/nodes.ts
- Add OutputGalleryNodeData interface with images array field
- Add outputGallery to WorkflowNodeData union
- Register in nodeDefaults: dimensions 320x360, default data with empty images array
- Export OutputGalleryNode from nodes/index.ts
- Register outputGallery in WorkflowCanvas nodeTypes with pink minimap color (#ec4899)
- Add outputGallery to ConnectionDropMenu IMAGE_TARGET_OPTIONS with grid icon
- Add outputGallery case to executeWorkflow in workflowStore
- Add outputGallery to getNodeHandles (image input, no outputs)
- Add outputGallery dimensions to keyboard shortcut default dimensions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
a5979c6c7d
  1. 30
      .planning/STATE.md
  2. 9
      src/components/ConnectionDropMenu.tsx
  3. 7
      src/components/WorkflowCanvas.tsx
  4. 1
      src/components/nodes/index.ts
  5. 6
      src/store/utils/nodeDefaults.ts
  6. 6
      src/store/workflowStore.ts
  7. 13
      src/types/nodes.ts

30
.planning/STATE.md

@ -9,19 +9,19 @@ See: .planning/PROJECT.md (updated 2026-01-09)
## Current Position ## Current Position
Phase: 35 of 35 (Large Workflow Handling) Phase: 40 of 40 (Node Enhancements)
Plan: 3 of 3 in current phase Plan: 2 of 4 in current phase
Status: Complete Status: In Progress
Last activity: 2026-01-31 - Completed plan 03 (Client-side wiring and selection chip) Last activity: 2026-02-01 - Completed 40-02-PLAN.md (Connection numbering)
Progress: ██████████ 100% Progress: ███████████░ 94%
## Performance Metrics ## Performance Metrics
**Velocity:** **Velocity:**
- Total plans completed: 37 - Total plans completed: 38
- Average duration: 6.9 min - Average duration: 6.7 min
- Total execution time: 4.25 hours - Total execution time: 4.28 hours
**By Phase:** **By Phase:**
@ -57,10 +57,11 @@ Progress: ██████████ 100%
| 33. Workflow Edit Safety | 2/2 | 5 min | 5 min | | 33. Workflow Edit Safety | 2/2 | 5 min | 5 min |
| 34. Agentic Workflow Editing | 3/3 | 13 min | 4.3 min | | 34. Agentic Workflow Editing | 3/3 | 13 min | 4.3 min |
| 35. Large Workflow Handling | 3/3 | 18 min | 6 min | | 35. Large Workflow Handling | 3/3 | 18 min | 6 min |
| 40. Node Enhancements | 2/4 | 2 min | 2 min |
**Recent Trend:** **Recent Trend:**
- Last 5 plans: 8 min, <1 min, 10 min, 3 min, 10 min, 5 min - Last 5 plans: <1 min, 10 min, 3 min, 10 min, 5 min, 2 min
- Trend: Phase 35 complete - large workflow handling with selection-aware chat context (TDD + execution) - Trend: Phase 40 in progress - node enhancements (connection numbering complete)
## Accumulated Context ## Accumulated Context
@ -180,6 +181,9 @@ Recent decisions affecting current work:
- Ref fields (imageRef, outputImageRef, etc.) completely removed from LLM context - Ref fields (imageRef, outputImageRef, etc.) completely removed from LLM context
- All node parameters, positions, and model settings preserved in stripped context - All node parameters, positions, and model settings preserved in stripped context
- Enhanced WorkflowContext includes full StrippedNode[] with all non-binary data - Enhanced WorkflowContext includes full StrippedNode[] with all non-binary data
- 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
### Deferred Issues ### Deferred Issues
@ -215,7 +219,7 @@ Recent decisions affecting current work:
## Session Continuity ## Session Continuity
Last session: 2026-01-31 Last session: 2026-02-01
Stopped at: Completed Phase 35 Plan 03 (Client-side wiring and selection chip) - Phase 35 COMPLETE Stopped at: Completed 40-02-PLAN.md (Connection numbering)
Resume file: None Resume file: None
Next action: Phase 35 completes Milestone v1.4 (Agentic Workflow Builder). Ready for Milestone v1.5 (Store Refactoring) or new priorities. Next action: Continue Phase 40 - 2 more plans remaining (OutputGallery, ImageCompare, PromptConstructor)

9
src/components/ConnectionDropMenu.tsx

@ -70,6 +70,15 @@ const IMAGE_TARGET_OPTIONS: MenuOption[] = [
</svg> </svg>
), ),
}, },
{
type: "outputGallery",
label: "Output Gallery",
icon: (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z" />
</svg>
),
},
]; ];
const TEXT_TARGET_OPTIONS: MenuOption[] = [ const TEXT_TARGET_OPTIONS: MenuOption[] = [

7
src/components/WorkflowCanvas.tsx

@ -28,6 +28,7 @@ import {
LLMGenerateNode, LLMGenerateNode,
SplitGridNode, SplitGridNode,
OutputNode, OutputNode,
OutputGalleryNode,
} from "./nodes"; } from "./nodes";
import { EditableEdge, ReferenceEdge } from "./edges"; import { EditableEdge, ReferenceEdge } from "./edges";
import { ConnectionDropMenu, MenuAction } from "./ConnectionDropMenu"; import { ConnectionDropMenu, MenuAction } from "./ConnectionDropMenu";
@ -52,6 +53,7 @@ const nodeTypes: NodeTypes = {
llmGenerate: LLMGenerateNode, llmGenerate: LLMGenerateNode,
splitGrid: SplitGridNode, splitGrid: SplitGridNode,
output: OutputNode, output: OutputNode,
outputGallery: OutputGalleryNode,
}; };
const edgeTypes: EdgeTypes = { const edgeTypes: EdgeTypes = {
@ -96,6 +98,8 @@ const getNodeHandles = (nodeType: string): { inputs: string[]; outputs: string[]
return { inputs: ["image"], outputs: ["reference"] }; return { inputs: ["image"], outputs: ["reference"] };
case "output": case "output":
return { inputs: ["image"], outputs: [] }; return { inputs: ["image"], outputs: [] };
case "outputGallery":
return { inputs: ["image"], outputs: [] };
default: default:
return { inputs: [], outputs: [] }; return { inputs: [], outputs: [] };
} }
@ -887,6 +891,7 @@ export function WorkflowCanvas() {
llmGenerate: { width: 320, height: 360 }, llmGenerate: { width: 320, height: 360 },
splitGrid: { width: 300, height: 320 }, splitGrid: { width: 300, height: 320 },
output: { width: 320, height: 320 }, output: { width: 320, height: 320 },
outputGallery: { width: 320, height: 360 },
}; };
const dims = defaultDimensions[nodeType]; const dims = defaultDimensions[nodeType];
addNode(nodeType, { x: centerX - dims.width / 2, y: centerY - dims.height / 2 }); addNode(nodeType, { x: centerX - dims.width / 2, y: centerY - dims.height / 2 });
@ -1374,6 +1379,8 @@ export function WorkflowCanvas() {
return "#f59e0b"; return "#f59e0b";
case "output": case "output":
return "#ef4444"; return "#ef4444";
case "outputGallery":
return "#ec4899";
default: default:
return "#94a3b8"; return "#94a3b8";
} }

1
src/components/nodes/index.ts

@ -6,4 +6,5 @@ export { GenerateVideoNode } from "./GenerateVideoNode";
export { LLMGenerateNode } from "./LLMGenerateNode"; export { LLMGenerateNode } from "./LLMGenerateNode";
export { SplitGridNode } from "./SplitGridNode"; export { SplitGridNode } from "./SplitGridNode";
export { OutputNode } from "./OutputNode"; export { OutputNode } from "./OutputNode";
export { OutputGalleryNode } from "./OutputGalleryNode";
export { GroupNode } from "./GroupNode"; export { GroupNode } from "./GroupNode";

6
src/store/utils/nodeDefaults.ts

@ -8,6 +8,7 @@ import {
LLMGenerateNodeData, LLMGenerateNodeData,
SplitGridNodeData, SplitGridNodeData,
OutputNodeData, OutputNodeData,
OutputGalleryNodeData,
WorkflowNodeData, WorkflowNodeData,
GroupColor, GroupColor,
SelectedModel, SelectedModel,
@ -27,6 +28,7 @@ export const defaultNodeDimensions: Record<NodeType, { width: number; height: nu
llmGenerate: { width: 320, height: 360 }, llmGenerate: { width: 320, height: 360 },
splitGrid: { width: 300, height: 320 }, splitGrid: { width: 300, height: 320 },
output: { width: 320, height: 320 }, output: { width: 320, height: 320 },
outputGallery: { width: 320, height: 360 },
}; };
/** /**
@ -157,5 +159,9 @@ export const createDefaultNodeData = (type: NodeType): WorkflowNodeData => {
image: null, image: null,
outputFilename: "", outputFilename: "",
} as OutputNodeData; } as OutputNodeData;
case "outputGallery":
return {
images: [],
} as OutputGalleryNodeData;
} }
}; };

6
src/store/workflowStore.ts

@ -1774,6 +1774,12 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
} }
break; break;
} }
case "outputGallery": {
const { images } = getConnectedInputs(node.id);
updateNodeData(node.id, { images });
break;
}
} }
} }

13
src/types/nodes.ts

@ -30,7 +30,8 @@ export type NodeType =
| "generateVideo" | "generateVideo"
| "llmGenerate" | "llmGenerate"
| "splitGrid" | "splitGrid"
| "output"; | "output"
| "outputGallery";
/** /**
* Node execution status * Node execution status
@ -165,6 +166,13 @@ export interface OutputNodeData extends BaseNodeData {
outputFilename?: string; // Custom filename for saved outputs (without extension) outputFilename?: string; // Custom filename for saved outputs (without extension)
} }
/**
* Output Gallery node - displays scrollable thumbnail grid of images with lightbox
*/
export interface OutputGalleryNodeData extends BaseNodeData {
images: string[]; // Array of base64 data URLs from connected nodes
}
/** /**
* Split Grid node - splits image into grid cells for parallel processing * Split Grid node - splits image into grid cells for parallel processing
*/ */
@ -202,7 +210,8 @@ export type WorkflowNodeData =
| GenerateVideoNodeData | GenerateVideoNodeData
| LLMGenerateNodeData | LLMGenerateNodeData
| SplitGridNodeData | SplitGridNodeData
| OutputNodeData; | OutputNodeData
| OutputGalleryNodeData;
/** /**
* Workflow node with typed data (extended with optional groupId) * Workflow node with typed data (extended with optional groupId)

Loading…
Cancel
Save