Browse Source
Phase 28: Node Defaults UI - 1 plan created - 3 tasks defined (2 auto, 1 checkpoint) - Ready for executionhandoff-20260429-1057
1 changed files with 270 additions and 0 deletions
@ -0,0 +1,270 @@ |
|||
--- |
|||
phase: 28-node-defaults-ui |
|||
plan: 01 |
|||
type: execute |
|||
--- |
|||
|
|||
<objective> |
|||
Create a settings panel for configuring default node preferences per node type. |
|||
|
|||
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. |
|||
</objective> |
|||
|
|||
<execution_context> |
|||
~/.claude/get-shit-done/workflows/execute-phase.md |
|||
~/.claude/get-shit-done/templates/summary.md |
|||
</execution_context> |
|||
|
|||
<context> |
|||
@.planning/PROJECT.md |
|||
@.planning/ROADMAP.md |
|||
@.planning/STATE.md |
|||
@.planning/phases/27-node-defaults-infrastructure/27-01-SUMMARY.md |
|||
|
|||
**Key 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:** |
|||
- `NodeDefaultsConfig` type: `{ 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.ts |
|||
- `createDefaultNodeData()` 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 |
|||
</context> |
|||
|
|||
<tasks> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 1: Add Node Defaults tab to ProjectSetupModal</name> |
|||
<files>src/components/ProjectSetupModal.tsx</files> |
|||
<action> |
|||
1. Add "Node Defaults" as a third tab after "Providers": |
|||
- Tab button: `setActiveTab("nodeDefaults")` |
|||
- activeTab state now `"project" | "providers" | "nodeDefaults"` |
|||
|
|||
2. Add local state for node defaults: |
|||
- `const [localNodeDefaults, setLocalNodeDefaults] = useState<NodeDefaultsConfig>({});` |
|||
- Import `NodeDefaultsConfig, loadNodeDefaults, saveNodeDefaults` from localStorage.ts |
|||
- On modal open (in useEffect): `setLocalNodeDefaults(loadNodeDefaults());` |
|||
|
|||
3. Add state for model selection dialogs: |
|||
- `const [showImageModelDialog, setShowImageModelDialog] = useState(false);` |
|||
- `const [showVideoModelDialog, setShowVideoModelDialog] = useState(false);` |
|||
|
|||
4. Add handler for saving node defaults: |
|||
```typescript |
|||
const handleSaveNodeDefaults = () => { |
|||
saveNodeDefaults(localNodeDefaults); |
|||
onClose(); |
|||
}; |
|||
``` |
|||
|
|||
5. Update handleSave to route to handleSaveNodeDefaults when activeTab === "nodeDefaults" |
|||
|
|||
6. 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 |
|||
|
|||
7. Handle model selection callbacks: |
|||
```typescript |
|||
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. |
|||
|
|||
8. 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. |
|||
</action> |
|||
<verify>npm run build passes, modal opens with three tabs, Node Defaults tab shows three sections</verify> |
|||
<done>ProjectSetupModal has Node Defaults tab with sections for GenerateImage, GenerateVideo, and LLM defaults</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 2: Wire up model selection dialogs and LLM controls</name> |
|||
<files>src/components/ProjectSetupModal.tsx</files> |
|||
<action> |
|||
1. Add ModelSearchDialog renders (conditionally when showImageModelDialog/showVideoModelDialog is true): |
|||
```tsx |
|||
{showImageModelDialog && ( |
|||
<ModelSearchDialog |
|||
isOpen={showImageModelDialog} |
|||
onClose={() => setShowImageModelDialog(false)} |
|||
onModelSelected={handleImageModelSelected} |
|||
initialCapabilityFilter="image" |
|||
/> |
|||
)} |
|||
{showVideoModelDialog && ( |
|||
<ModelSearchDialog |
|||
isOpen={showVideoModelDialog} |
|||
onClose={() => setShowVideoModelDialog(false)} |
|||
onModelSelected={handleVideoModelSelected} |
|||
initialCapabilityFilter="video" |
|||
/> |
|||
)} |
|||
``` |
|||
|
|||
2. Implement LLM settings handlers: |
|||
```typescript |
|||
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) } |
|||
})); |
|||
}; |
|||
``` |
|||
|
|||
3. Implement clear handlers: |
|||
```typescript |
|||
const handleClearImageDefaults = () => { |
|||
setLocalNodeDefaults(prev => { |
|||
const { generateImage, ...rest } = prev; |
|||
return rest; |
|||
}); |
|||
}; |
|||
// Similar for video and LLM |
|||
``` |
|||
|
|||
4. Import PROVIDERS and MODELS arrays - copy from LLMGenerateNode.tsx (they're not exported). Place at top of file: |
|||
```typescript |
|||
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" }, |
|||
], |
|||
}; |
|||
``` |
|||
|
|||
5. Import types: LLMProvider, LLMModelType, NodeDefaultsConfig from @/types |
|||
|
|||
6. 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) |
|||
</action> |
|||
<verify>npm run build passes, can select models from dialogs, LLM dropdowns work, clear buttons reset to defaults</verify> |
|||
<done>Model selection dialogs open and save selections, LLM controls update local state, clear buttons reset defaults</done> |
|||
</task> |
|||
|
|||
<task type="checkpoint:human-verify" gate="blocking"> |
|||
<what-built>Node Defaults tab in Project Settings with: |
|||
- GenerateImage section: shows selected model or "System default", Select Model and Clear buttons |
|||
- GenerateVideo section: shows selected model or "None set", Select Model and Clear buttons |
|||
- LLM section: provider dropdown, model dropdown, temperature slider, Clear button |
|||
- Changes persist to localStorage and affect new node creation via keyboard shortcuts</what-built> |
|||
<how-to-verify> |
|||
1. Run: npm run dev |
|||
2. Open Project Settings (gear icon in header) |
|||
3. Click "Node Defaults" tab (third tab) |
|||
4. Verify three sections appear: Image Model, Video Model, LLM Settings |
|||
5. Click "Select Model" for Image - verify ModelSearchDialog opens with image filter |
|||
6. Select a model (e.g., fal.ai flux) - verify it shows in the section |
|||
7. Click "Save" to close modal |
|||
8. Press Shift+G to create GenerateImage node - verify it has the selected model |
|||
9. Re-open settings, go to Node Defaults, click "Clear" on Image defaults |
|||
10. Click "Save", press Shift+G - verify node uses system default (Gemini) |
|||
11. Test LLM defaults: change provider to OpenAI, select GPT-4.1 Mini |
|||
12. Save and press Shift+L - verify LLM node uses OpenAI provider |
|||
</how-to-verify> |
|||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal> |
|||
</task> |
|||
|
|||
</tasks> |
|||
|
|||
<verification> |
|||
Before declaring plan complete: |
|||
- [ ] `npm run build` succeeds without TypeScript errors |
|||
- [ ] ProjectSetupModal has three tabs: Project, Providers, Node Defaults |
|||
- [ ] Can select default image model from ModelSearchDialog |
|||
- [ ] Can select default video model from ModelSearchDialog |
|||
- [ ] Can configure LLM provider, model, and temperature |
|||
- [ ] Clear buttons reset each section to undefined/system defaults |
|||
- [ ] Settings persist across modal close/reopen |
|||
- [ ] New nodes created via keyboard shortcuts use stored defaults |
|||
</verification> |
|||
|
|||
<success_criteria> |
|||
|
|||
- All tasks completed |
|||
- TypeScript builds without errors |
|||
- Node Defaults UI is functional and persists settings |
|||
- User verified functionality via checkpoint |
|||
</success_criteria> |
|||
|
|||
<output> |
|||
After completion, create `.planning/phases/28-node-defaults-ui/28-01-SUMMARY.md` |
|||
</output> |
|||
Loading…
Reference in new issue