---
phase: 04-model-search-dialog
plan: 02
type: execute
---
Create a searchable model browser dialog with filtering and selection.
Purpose: Let users browse and search models from Replicate and fal.ai, then add a GenerateImage node with the selected model.
Output: Functional ModelSearchDialog component that searches models, displays results, and creates nodes on selection.
~/.claude/get-shit-done/workflows/execute-phase.md
./summary.md
~/.claude/get-shit-done/references/checkpoints.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-model-search-dialog/04-01-SUMMARY.md
# Key source files:
@src/components/FloatingActionBar.tsx
@src/store/workflowStore.ts
@src/app/api/models/route.ts
@src/components/nodes/GenerateImageNode.tsx
@src/types/index.ts
@src/lib/providers/types.ts
**Tech stack available:** React, Zustand, /api/models endpoint with search and capabilities filtering
**Established patterns:** Modal rendering via createPortal, model fetching with API key headers, SelectedModel type
**Constraining decisions:**
- [Phase 2]: /api/models endpoint accepts X-Replicate-Key and X-Fal-Key headers
- [Phase 2]: capabilities parameter filters models (text-to-image, image-to-image, text-to-video, image-to-video)
- [Phase 3]: SelectedModel type: { provider, modelId, displayName }
Task 1: Create ModelSearchDialog component
src/components/modals/ModelSearchDialog.tsx, src/components/FloatingActionBar.tsx
Create ModelSearchDialog.tsx in src/components/modals/:
1. Component structure:
- Use createPortal to render outside React Flow stacking context (like other modals)
- Increment/decrement modalCount in store on mount/unmount
- Full-screen overlay with centered dialog (max-w-2xl)
2. Dialog layout:
- Header: "Browse Models" title + close button (X)
- Filter bar:
- Search input (debounced 300ms)
- Provider filter dropdown (All / Replicate / fal.ai)
- Capability filter (All / Image / Video)
- Model grid/list:
- Show cover image thumbnail (if available), model name, provider badge, description (truncated)
- Loading state while fetching
- Empty state for no results
3. Data fetching:
- Fetch from /api/models with query params:
- search: search query (if not empty)
- provider: provider filter (if not "all")
- capabilities: "text-to-image,image-to-image" for Image, "text-to-video,image-to-video" for Video
- Pass API keys via headers:
- Get from providerSettings in store
- X-Replicate-Key: providerSettings.providers.replicate?.apiKey
- X-Fal-Key: providerSettings.providers.fal?.apiKey
- Show loading spinner while fetching
- Handle errors gracefully (show error message, allow retry)
4. Styling (match existing dark theme):
- Background: bg-neutral-800
- Borders: border-neutral-700
- Text: text-neutral-100 for headers, text-neutral-300 for body
- Inputs: bg-neutral-700 border-neutral-600
- Cards: bg-neutral-700/50 hover:bg-neutral-700
5. Update FloatingActionBar.tsx:
- Replace placeholder with actual ModelSearchDialog import and render
- Pass modelSearchProvider as initial provider filter
Open dialog via provider icon; search input filters results; provider dropdown changes results; models display with thumbnails
ModelSearchDialog renders with search, filtering, and model display working
Task 2: Add model selection behavior
src/components/modals/ModelSearchDialog.tsx, src/store/workflowStore.ts
1. Add click handler to model cards:
- When model clicked, create a new GenerateImage node at canvas center
- Set the node's selectedModel to: { provider: model.provider, modelId: model.id, displayName: model.name }
- Close the dialog after creating node
2. In workflowStore.ts, modify addNode to optionally accept initial data:
- Add optional second parameter: `initialData?: Partial`
- Merge initialData into default node data when creating node
- This allows setting selectedModel when adding from model browser
3. Update ModelSearchDialog to call addNode with selectedModel:
```typescript
const handleSelectModel = (model: ProviderModel) => {
const center = getPaneCenter(); // reuse from FloatingActionBar
const position = screenToFlowPosition({
x: center.x + Math.random() * 100 - 50,
y: center.y + Math.random() * 100 - 50,
});
addNode("nanoBanana", position, {
selectedModel: {
provider: model.provider,
modelId: model.id,
displayName: model.name,
}
});
setModelSearchOpen(false);
};
```
4. Add visual feedback:
- Cursor pointer on model cards
- Hover state on cards (slight brightness increase)
- Optional: toast/notification when node created
Click model in dialog; GenerateImage node created at canvas center with selectedModel set; dialog closes automatically
Model selection creates configured GenerateImage node and closes dialog
Model search dialog with search, filtering, and node creation
1. Run: npm run dev
2. Configure a Replicate API key in settings (if not already)
3. Look at floating action bar - should see provider icon(s)
4. Click a provider icon - dialog should open
5. Test search: type "flux" or "stable diffusion" - results should filter
6. Test provider filter: switch between All/Replicate/fal.ai
7. Test capability filter: switch between All/Image/Video
8. Click a model - should create a GenerateImage node on canvas
9. Check the new node - should have correct provider/model selected
Type "approved" to continue, or describe issues to fix
Before declaring plan complete:
- [ ] `npm run build` succeeds without errors
- [ ] Model search dialog opens and closes correctly
- [ ] Search filters results (debounced)
- [ ] Provider filter works
- [ ] Capability filter works
- [ ] Clicking model creates GenerateImage node with correct selectedModel
- [ ] No console errors during normal operation
- All tasks completed
- All verification checks pass
- Human verification approved
- No TypeScript errors
- Users can browse, search, filter, and select models from the dialog
- Phase 4 complete