--- 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 After completion, create `.planning/phases/04-model-search-dialog/04-02-SUMMARY.md`