10 KiB
| phase | plan | type |
|---|---|---|
| 28-node-defaults-ui | 1 | execute |
Purpose: Users can set their preferred default models for GenerateImage, GenerateVideo, and LLM nodes, so keyboard shortcuts (Shift+G, Shift+L) and quick-add create nodes with their preferred settings. Output: New "Node Defaults" tab in ProjectSetupModal with model selection UI for each node type.
<execution_context> ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/27-node-defaults-infrastructure/27-01-SUMMARY.mdKey files: @src/components/ProjectSetupModal.tsx @src/store/utils/localStorage.ts @src/types/nodes.ts @src/components/modals/ModelSearchDialog.tsx @src/components/nodes/LLMGenerateNode.tsx
Infrastructure from Phase 27:
NodeDefaultsConfigtype:{ generateImage?: GenerateImageNodeDefaults, generateVideo?: GenerateVideoNodeDefaults, llm?: LLMNodeDefaults }GenerateImageNodeDefaults:{ selectedModel?: { provider, modelId, displayName } }GenerateVideoNodeDefaults:{ selectedModel?: { provider, modelId, displayName } }LLMNodeDefaults:{ provider?: LLMProvider, model?: LLMModelType, temperature?: number, maxTokens?: number }loadNodeDefaults()/saveNodeDefaults()in localStorage.tscreateDefaultNodeData()already applies these defaults when creating nodes
Established patterns:
- Tab-based UI in ProjectSetupModal (project, providers tabs)
- Local state pattern: load from store on open, save on "Save" button
- Model selection: ModelSearchDialog opens in callback mode via
onModelSelected - LLM settings: dropdowns for provider/model, slider for temperature
-
Add local state for node defaults:
const [localNodeDefaults, setLocalNodeDefaults] = useState<NodeDefaultsConfig>({});- Import
NodeDefaultsConfig, loadNodeDefaults, saveNodeDefaultsfrom localStorage.ts - On modal open (in useEffect):
setLocalNodeDefaults(loadNodeDefaults());
-
Add state for model selection dialogs:
const [showImageModelDialog, setShowImageModelDialog] = useState(false);const [showVideoModelDialog, setShowVideoModelDialog] = useState(false);
-
Add handler for saving node defaults:
const handleSaveNodeDefaults = () => { saveNodeDefaults(localNodeDefaults); onClose(); }; -
Update handleSave to route to handleSaveNodeDefaults when activeTab === "nodeDefaults"
-
Create Node Defaults tab content (render when activeTab === "nodeDefaults"):
- Three sections: GenerateImage, GenerateVideo, LLM
- Each in a styled container matching provider tab style (p-3 bg-neutral-900 rounded-lg border border-neutral-700)
GenerateImage section:
- Label: "Default Image Model"
- Display current selection or "System default (Gemini nano-banana-pro)" if not set
- "Select Model" button that opens ModelSearchDialog with onModelSelected callback
- "Clear" button (only shown if selection exists) to reset to undefined
GenerateVideo section:
- Label: "Default Video Model"
- Display current selection or "None set (select model on first use)" if not set
- "Select Model" button that opens ModelSearchDialog with initialCapabilityFilter="video"
- "Clear" button to reset
LLM section:
- Label: "Default LLM Settings"
- Provider dropdown (Google, OpenAI) - use same PROVIDERS array as LLMGenerateNode
- Model dropdown (filtered by provider) - use same MODELS object as LLMGenerateNode
- Temperature slider (0-2, step 0.1) with label showing current value
- Display "Using system defaults" text if no overrides set
- "Clear" button to reset all LLM defaults
-
Handle model selection callbacks:
const handleImageModelSelected = (model: ProviderModel) => { setLocalNodeDefaults(prev => ({ ...prev, generateImage: { ...prev.generateImage, selectedModel: { provider: model.provider, modelId: model.id, displayName: model.name, } } })); setShowImageModelDialog(false); };Similar for video model.
-
Import ProviderModel from @/lib/providers/types
Do NOT add new imports for ModelSearchDialog - it's already used in FloatingActionBar, so check if we need portal or can render inline. Use portal pattern matching existing modal usage. npm run build passes, modal opens with three tabs, Node Defaults tab shows three sections ProjectSetupModal has Node Defaults tab with sections for GenerateImage, GenerateVideo, and LLM defaults
Task 2: Wire up model selection dialogs and LLM controls src/components/ProjectSetupModal.tsx 1. Add ModelSearchDialog renders (conditionally when showImageModelDialog/showVideoModelDialog is true): ```tsx {showImageModelDialog && ( setShowImageModelDialog(false)} onModelSelected={handleImageModelSelected} initialCapabilityFilter="image" /> )} {showVideoModelDialog && ( setShowVideoModelDialog(false)} onModelSelected={handleVideoModelSelected} initialCapabilityFilter="video" /> )} ```-
Implement LLM settings handlers:
const handleLLMProviderChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const newProvider = e.target.value as LLMProvider; const firstModelForProvider = MODELS[newProvider][0].value; setLocalNodeDefaults(prev => ({ ...prev, llm: { ...prev.llm, provider: newProvider, model: firstModelForProvider, } })); }; const handleLLMModelChange = (e: React.ChangeEvent<HTMLSelectElement>) => { setLocalNodeDefaults(prev => ({ ...prev, llm: { ...prev.llm, model: e.target.value as LLMModelType } })); }; const handleLLMTemperatureChange = (e: React.ChangeEvent<HTMLInputElement>) => { setLocalNodeDefaults(prev => ({ ...prev, llm: { ...prev.llm, temperature: parseFloat(e.target.value) } })); }; -
Implement clear handlers:
const handleClearImageDefaults = () => { setLocalNodeDefaults(prev => { const { generateImage, ...rest } = prev; return rest; }); }; // Similar for video and LLM -
Import PROVIDERS and MODELS arrays - copy from LLMGenerateNode.tsx (they're not exported). Place at top of file:
const LLM_PROVIDERS: { value: LLMProvider; label: string }[] = [ { value: "google", label: "Google" }, { value: "openai", label: "OpenAI" }, ]; const LLM_MODELS: Record<LLMProvider, { value: LLMModelType; label: string }[]> = { google: [ { value: "gemini-3-flash-preview", label: "Gemini 3 Flash" }, { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }, { value: "gemini-3-pro-preview", label: "Gemini 3.0 Pro" }, ], openai: [ { value: "gpt-4.1-mini", label: "GPT-4.1 Mini" }, { value: "gpt-4.1-nano", label: "GPT-4.1 Nano" }, ], }; -
Import types: LLMProvider, LLMModelType, NodeDefaultsConfig from @/types
-
Complete the UI for each section with proper styling:
- Use flex layout with justify-between for label and controls
- Model display shows provider icon + model name
- Buttons: "Select Model" (primary), "Clear" (text button, neutral-400) npm run build passes, can select models from dialogs, LLM dropdowns work, clear buttons reset to defaults Model selection dialogs open and save selections, LLM controls update local state, clear buttons reset defaults
<success_criteria>
- All tasks completed
- TypeScript builds without errors
- Node Defaults UI is functional and persists settings
- User verified functionality via checkpoint </success_criteria>