Browse Source

docs(03): update plans for separate image/video nodes

- Rename target from Generate to GenerateImage
- Filter model selector to image capabilities only (text-to-image, image-to-image)
- Note GenerateVideo node deferred to Phase 6
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
fbdf6de3f1
  1. 43
      .planning/phases/03-generate-node-refactor/03-01-PLAN.md
  2. 30
      .planning/phases/03-generate-node-refactor/03-02-PLAN.md
  3. 14
      .planning/phases/03-generate-node-refactor/03-03-PLAN.md

43
.planning/phases/03-generate-node-refactor/03-01-PLAN.md

@ -5,10 +5,12 @@ type: execute
---
<objective>
Rename NanoBanana node to Generate and add multi-provider model selector.
Rename NanoBanana node to GenerateImage and add multi-provider model selector.
Purpose: Transform the hardcoded Gemini-only node into a generic generation node that can use any provider's models.
Output: Generate node component with provider/model dropdown, updated types, store functions renamed.
Purpose: Transform the hardcoded Gemini-only node into a multi-provider image generation node that filters to image-capable models only.
Output: GenerateImage node component with provider/model dropdown (image models only), updated types, store functions renamed.
Note: GenerateVideo node will be added in Phase 6 (Video & Polish) as a separate node type.
</objective>
<execution_context>
@ -61,18 +63,19 @@ Output: Generate node component with provider/model dropdown, updated types, sto
</task>
<task type="auto">
<name>Task 2: Rename component to GenerateNode with model selector UI</name>
<name>Task 2: Rename component to GenerateImageNode with model selector UI</name>
<files>src/components/nodes/NanoBananaNode.tsx, src/components/nodes/index.ts</files>
<action>
1. Rename file from NanoBananaNode.tsx to GenerateNode.tsx
1. Rename file from NanoBananaNode.tsx to GenerateImageNode.tsx
2. Rename component from NanoBananaNode to GenerateNode (keep function export name)
2. Rename component from NanoBananaNode to GenerateImageNode
3. Add model selector UI that shows:
- Provider dropdown (gemini, replicate, fal) - only show enabled providers
- Model dropdown (fetched from /api/models for selected provider)
- **Filter to image capabilities only**: text-to-image, image-to-image
- For Gemini, show existing hardcoded models (Nano Banana, Nano Banana Pro)
- For other providers, fetch models dynamically
- For other providers, fetch models dynamically with capability filter
4. When provider/model changes:
- Update node data with selectedModel: { provider, modelId, displayName }
@ -81,16 +84,16 @@ Output: Generate node component with provider/model dropdown, updated types, sto
5. Keep existing aspect ratio/resolution controls for Gemini models
- Hide them for non-Gemini models (parameters will come from provider schema later)
6. Update index.ts to export GenerateNode and alias it as NanoBananaNode for backward compatibility:
6. Update index.ts to export GenerateImageNode and alias as NanoBananaNode:
```typescript
export { GenerateNode } from "./GenerateNode";
export { GenerateNode as NanoBananaNode } from "./GenerateNode";
export { GenerateImageNode } from "./GenerateImageNode";
export { GenerateImageNode as NanoBananaNode } from "./GenerateImageNode";
```
WHY: The component rename reflects its new multi-provider purpose. The alias maintains backward compatibility with existing imports.
WHY: The component rename reflects its image-specific purpose. Video generation will be a separate GenerateVideoNode in Phase 6. The alias maintains backward compatibility.
</action>
<verify>npm run build succeeds, component renders in browser</verify>
<done>GenerateNode.tsx exists, shows provider selector, model dropdown fetches from API for non-Gemini providers</done>
<done>GenerateImageNode.tsx exists, shows provider selector, model dropdown shows only image-capable models</done>
</task>
<task type="auto">
@ -98,18 +101,18 @@ Output: Generate node component with provider/model dropdown, updated types, sto
<files>src/store/workflowStore.ts, src/components/WorkflowCanvas.tsx</files>
<action>
1. In workflowStore.ts:
- Rename saveNanoBananaDefaults to saveGenerateDefaults (keep old name as alias)
- Rename loadNanoBananaDefaults to loadGenerateDefaults (keep old name as alias)
- Rename saveNanoBananaDefaults to saveGenerateImageDefaults (keep old name as alias)
- Rename loadNanoBananaDefaults to loadGenerateImageDefaults (keep old name as alias)
- Update localStorage key comment but keep actual key for migration
- Update createDefaultNodeData for "nanoBanana" case to include default selectedModel for Gemini
2. In WorkflowCanvas.tsx:
- Update nodeTypes registration to use GenerateNode
- Keep "nanoBanana" as the type key (internal type name unchanged)
- Update nodeTypes registration to use GenerateImageNode
- Keep "nanoBanana" as the type key (internal type name unchanged for now)
- Update minimap color mapping if it references "nanoBanana"
3. In any file using NanoBananaNode import:
- Update import to use GenerateNode
- Update import to use GenerateImageNode
- Files: ConnectionDropMenu.tsx, FloatingActionBar.tsx, etc.
WHY: Internal references update to new names while keeping the node type string "nanoBanana" for workflow file compatibility.
@ -124,7 +127,8 @@ Output: Generate node component with provider/model dropdown, updated types, sto
Before declaring plan complete:
- [ ] `npm run build` succeeds without errors
- [ ] `npx tsc --noEmit` passes type checking
- [ ] GenerateNode renders with provider dropdown
- [ ] GenerateImageNode renders with provider dropdown
- [ ] Model selector shows only image-capable models (text-to-image, image-to-image)
- [ ] Gemini models show in model selector
- [ ] Existing workflows with nanoBanana nodes still load
</verification>
@ -133,7 +137,8 @@ Before declaring plan complete:
- All tasks completed
- All verification checks pass
- GenerateNode component exists with multi-provider UI
- GenerateImageNode component exists with multi-provider UI
- Model selector filters to image capabilities only
- Types support both legacy Gemini and new multi-provider selection
- Backward compatibility maintained for existing workflows
</success_criteria>

30
.planning/phases/03-generate-node-refactor/03-02-PLAN.md

@ -5,10 +5,12 @@ type: execute
---
<objective>
Add provider-specific execution logic to the generate API route.
Add provider-specific execution logic to the generate API route for image generation.
Purpose: Enable the generate endpoint to route requests to the correct provider (Gemini, Replicate, fal.ai) based on the selected model.
Output: Updated /api/generate route that dispatches to provider-specific generation logic.
Purpose: Enable the generate endpoint to route requests to the correct provider (Gemini, Replicate, fal.ai) based on the selected model, filtering to image outputs only.
Output: Updated /api/generate route that dispatches to provider-specific image generation logic.
Note: Video generation will use a separate endpoint in Phase 6.
</objective>
<execution_context>
@ -59,12 +61,16 @@ Output: Updated /api/generate route that dispatches to provider-specific generat
3. Fetch output:
- Poll GET /predictions/{id} until status is "succeeded" or "failed"
- Convert output URL to base64 data URL
- Return in GenerationOutput format
- Return in GenerationOutput format with type: "image"
4. Filter to image models only:
- Only accept models with text-to-image or image-to-image capabilities
- Return error if video model passed (video generation is Phase 6)
WHY: Replicate requires polling for async predictions. The generate() method encapsulates this complexity.
WHY: Replicate requires polling for async predictions. The generate() method encapsulates this complexity. Image-only filter keeps this endpoint focused.
</action>
<verify>TypeScript compiles, generate() method exists and doesn't throw immediately</verify>
<done>Replicate provider has working generate() method that calls prediction API</done>
<done>Replicate provider has working generate() method that calls prediction API for image models</done>
</task>
<task type="auto">
@ -82,14 +88,18 @@ Output: Updated /api/generate route that dispatches to provider-specific generat
- API key optional but rate limited without it
3. Parse response:
- Extract image/video URL from response
- Extract image URL from response
- Convert to base64 data URL for consistency
- Handle different output formats (images array, video URL, etc.)
- Handle images array output format
4. Filter to image models only:
- Only accept models with text-to-image or image-to-image capabilities
- Return error if video model passed (video generation is Phase 6)
WHY: fal.ai's synchronous API is simpler than Replicate. The generate() method normalizes the response format.
WHY: fal.ai's synchronous API is simpler than Replicate. The generate() method normalizes the response format. Image-only filter keeps this endpoint focused.
</action>
<verify>TypeScript compiles, generate() method exists</verify>
<done>fal.ai provider has working generate() method that calls fal.run endpoint</done>
<done>fal.ai provider has working generate() method that calls fal.run endpoint for image models</done>
</task>
<task type="auto">

14
.planning/phases/03-generate-node-refactor/03-03-PLAN.md

@ -28,7 +28,7 @@ Output: Migration logic, verified backward compatibility, updated workflow execu
# Key source files:
@src/store/workflowStore.ts
@src/types/index.ts
@src/components/nodes/GenerateNode.tsx
@src/components/nodes/GenerateImageNode.tsx
**Tech stack available:** Zustand, workflow JSON persistence
**Established patterns:** Workflow load/save in workflowStore
@ -97,8 +97,8 @@ Output: Migration logic, verified backward compatibility, updated workflow execu
3. Handle response format:
- Provider generate() returns GenerationOutput with outputs array
- Extract first image/video output as outputImage
- For video outputs, store separately (Phase 6 handles video display)
- Extract first image output as outputImage
- Validate output type is "image" (video handled by separate node in Phase 6)
WHY: The store's execution logic needs to pass provider API keys (stored client-side) to the server route.
</action>
@ -107,8 +107,8 @@ Output: Migration logic, verified backward compatibility, updated workflow execu
</task>
<task type="auto">
<name>Task 3: Update GenerateNode to handle legacy data</name>
<files>src/components/nodes/GenerateNode.tsx</files>
<name>Task 3: Update GenerateImageNode to handle legacy data</name>
<files>src/components/nodes/GenerateImageNode.tsx</files>
<action>
1. When component mounts or data changes:
- If model exists but not selectedModel, derive selectedModel from model
@ -119,13 +119,13 @@ Output: Migration logic, verified backward compatibility, updated workflow execu
- Model dropdown shows correct Gemini model name
3. When saving defaults, save in both formats:
- saveGenerateDefaults should save both model and selectedModel
- saveGenerateImageDefaults should save both model and selectedModel
- Ensures new nodes get correct defaults
WHY: The UI must correctly display legacy nodes that were migrated on load.
</action>
<verify>Open workflow with old nanoBanana node, verify dropdowns show correct selection</verify>
<done>GenerateNode displays correct provider/model for both legacy and new nodes</done>
<done>GenerateImageNode displays correct provider/model for both legacy and new nodes</done>
</task>
</tasks>

Loading…
Cancel
Save