7.6 KiB
| phase | plan | type |
|---|---|---|
| 09-video-history | 1 | execute |
Purpose: Allow users to browse through previously generated videos without re-generating, improving workflow iteration speed. Output: GenerateVideoNode with carousel navigation controls and persistent video history stored to generations folder.
<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.mdKey reference files (pattern to follow):
@src/components/nodes/GenerateImageNode.tsx - Has carousel controls to copy pattern from (lines 295-334, 609-636) @src/types/index.ts - Has ImageHistoryItem/CarouselImageItem types (lines 117-134) @src/store/workflowStore.ts - Has imageHistory handling in executeWorkflow (lines 1201-1207, 1893-1899) @src/app/api/load-generation/route.ts - Currently only handles .png, needs video support @src/app/api/save-generation/route.ts - Already supports video (line 12-16), good reference
Files to modify:
@src/components/nodes/GenerateVideoNode.tsx
Task 1: Add video history types and store support src/types/index.ts, src/store/workflowStore.ts 1. In src/types/index.ts: - Add `CarouselVideoItem` type after `CarouselImageItem` (line ~135): ```typescript export interface CarouselVideoItem { id: string; timestamp: number; prompt: string; model: string; // Model ID for video (not ModelType since external providers) } ``` - Update `GenerateVideoNodeData` interface (line ~165) to add: ```typescript videoHistory: CarouselVideoItem[]; selectedVideoHistoryIndex: number; ```- In src/store/workflowStore.ts:
- Update
createDefaultNodeDatafor "generateVideo" case (around line 214) to include:videoHistory: [], selectedVideoHistoryIndex: 0, - In
executeWorkflowwhere generateVideo success is handled (around line 1392-1398), add video history tracking after setting outputVideo:- Generate unique ID:
const videoId = crypto.randomUUID(); - Create history item:
{ id: videoId, timestamp: Date.now(), prompt: text || '', model: nodeData.selectedModel?.modelId || '' } - Update history array (prepend new item):
videoHistory: [newHistoryItem, ...(nodeData.videoHistory || [])].slice(0, 50) - Set
selectedVideoHistoryIndex: 0 - Call save-generation API with video and videoId (async, don't await)
- Generate unique ID:
- Do the same for regenerateNode video handling (around line 2136-2142)
- Update
Import crypto from Node.js is not needed - use Math.random() fallback pattern already in codebase, or check if crypto.randomUUID exists.
TypeScript compiles: npm run build passes without type errors related to video history
GenerateVideoNodeData has videoHistory and selectedVideoHistoryIndex fields. Workflow execution saves videos to history and persists to generations folder.
-
Change the hardcoded
.pngextension logic (line 54) to search for the file by ID with multiple extensions:- Define supported extensions:
const extensions = ['png', 'jpg', 'jpeg', 'webp', 'gif', 'mp4', 'webm', 'mov']; - Loop through extensions to find the file that exists
- If no file found with any extension, return 404
- Define supported extensions:
-
Update the MIME type detection based on actual file extension found:
const extToMime: Record<string, string> = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp', gif: 'image/gif', mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', }; -
Return appropriate response field:
- For images:
{ success: true, image: dataUrl } - For videos:
{ success: true, video: dataUrl } - Include
contentTypefield to indicate type:contentType: extension.startsWith('mp4') || extension === 'webm' || extension === 'mov' ? 'video' : 'image'API handles video loading: Create a test .mp4 file in generations folder and verify the endpoint returns it correctly load-generation API can load both images and videos by ID, with correct MIME types and response fields
- For images:
-
Add state for loading:
const [isLoadingCarouselVideo, setIsLoadingCarouselVideo] = useState(false); -
Get generationsPath from store:
const generationsPath = useWorkflowStore((state) => state.generationsPath); -
Add loadVideoById callback (copy pattern from GenerateImageNode lines 266-293):
const loadVideoById = useCallback(async (videoId: string) => { if (!generationsPath) return null; try { const response = await fetch("/api/load-generation", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ directoryPath: generationsPath, imageId: videoId }), }); if (!response.ok) return null; const result = await response.json(); return result.success ? (result.video || result.image) : null; } catch { return null; } }, [generationsPath]); -
Add carousel navigation handlers (copy pattern from GenerateImageNode lines 295-333):
const handleCarouselPrevious = useCallback(async () => { ... }, [...]); const handleCarouselNext = useCallback(async () => { ... }, [...]);Adapt for video: use
videoHistory,selectedVideoHistoryIndex,outputVideo -
Add carousel UI after the video element, inside the outputVideo conditional (around line 388):
- Check
hasCarouselVideos = (nodeData.videoHistory || []).length > 1 - Add carousel controls div with prev/next buttons (copy from GenerateImageNode lines 609-636)
- Show "N / M" indicator using videoHistory length and selectedVideoHistoryIndex
- Check
-
Add loading overlay for carousel navigation (copy from GenerateImageNode lines 572-595):
- Inside the video preview div, add conditional overlay when isLoadingCarouselVideo is true Visual inspection: Generate 2+ videos with same node, verify carousel appears and navigation works GenerateVideoNode has prev/next carousel buttons showing after first regeneration, allowing users to browse video history
<success_criteria>
- All tasks completed
- All verification checks pass
- Video history works identically to image history pattern
- Carousel shows N/M indicator and prev/next navigation
- Videos saved to and loaded from generations folder </success_criteria>