Browse Source
Phase 09: Video History - 1 plan created - 3 total tasks defined - Ready for executionhandoff-20260429-1057
2 changed files with 188 additions and 3 deletions
@ -0,0 +1,185 @@ |
|||||
|
--- |
||||
|
phase: 09-video-history |
||||
|
plan: 01 |
||||
|
type: execute |
||||
|
--- |
||||
|
|
||||
|
<objective> |
||||
|
Add history carousel for generated videos in GenerateVideoNode, matching the image history pattern. |
||||
|
|
||||
|
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. |
||||
|
</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 |
||||
|
|
||||
|
# Key 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 |
||||
|
</context> |
||||
|
|
||||
|
<tasks> |
||||
|
|
||||
|
<task type="auto"> |
||||
|
<name>Task 1: Add video history types and store support</name> |
||||
|
<files>src/types/index.ts, src/store/workflowStore.ts</files> |
||||
|
<action> |
||||
|
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; |
||||
|
``` |
||||
|
|
||||
|
2. In src/store/workflowStore.ts: |
||||
|
- Update `createDefaultNodeData` for "generateVideo" case (around line 214) to include: |
||||
|
```typescript |
||||
|
videoHistory: [], |
||||
|
selectedVideoHistoryIndex: 0, |
||||
|
``` |
||||
|
- In `executeWorkflow` where 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) |
||||
|
- Do the same for regenerateNode video handling (around line 2136-2142) |
||||
|
|
||||
|
Import `crypto` from Node.js is not needed - use Math.random() fallback pattern already in codebase, or check if crypto.randomUUID exists. |
||||
|
</action> |
||||
|
<verify>TypeScript compiles: `npm run build` passes without type errors related to video history</verify> |
||||
|
<done>GenerateVideoNodeData has videoHistory and selectedVideoHistoryIndex fields. Workflow execution saves videos to history and persists to generations folder.</done> |
||||
|
</task> |
||||
|
|
||||
|
<task type="auto"> |
||||
|
<name>Task 2: Update load-generation API to support video formats</name> |
||||
|
<files>src/app/api/load-generation/route.ts</files> |
||||
|
<action> |
||||
|
Update the load-generation API to handle video files in addition to images: |
||||
|
|
||||
|
1. Change the hardcoded `.png` extension 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 |
||||
|
|
||||
|
2. Update the MIME type detection based on actual file extension found: |
||||
|
```typescript |
||||
|
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', |
||||
|
}; |
||||
|
``` |
||||
|
|
||||
|
3. Return appropriate response field: |
||||
|
- For images: `{ success: true, image: dataUrl }` |
||||
|
- For videos: `{ success: true, video: dataUrl }` |
||||
|
- Include `contentType` field to indicate type: `contentType: extension.startsWith('mp4') || extension === 'webm' || extension === 'mov' ? 'video' : 'image'` |
||||
|
</action> |
||||
|
<verify>API handles video loading: Create a test .mp4 file in generations folder and verify the endpoint returns it correctly</verify> |
||||
|
<done>load-generation API can load both images and videos by ID, with correct MIME types and response fields</done> |
||||
|
</task> |
||||
|
|
||||
|
<task type="auto"> |
||||
|
<name>Task 3: Add carousel controls to GenerateVideoNode</name> |
||||
|
<files>src/components/nodes/GenerateVideoNode.tsx</files> |
||||
|
<action> |
||||
|
Add carousel UI and navigation to GenerateVideoNode, following GenerateImageNode pattern: |
||||
|
|
||||
|
1. Add state for loading: |
||||
|
```typescript |
||||
|
const [isLoadingCarouselVideo, setIsLoadingCarouselVideo] = useState(false); |
||||
|
``` |
||||
|
|
||||
|
2. Get generationsPath from store: |
||||
|
```typescript |
||||
|
const generationsPath = useWorkflowStore((state) => state.generationsPath); |
||||
|
``` |
||||
|
|
||||
|
3. Add loadVideoById callback (copy pattern from GenerateImageNode lines 266-293): |
||||
|
```typescript |
||||
|
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]); |
||||
|
``` |
||||
|
|
||||
|
4. Add carousel navigation handlers (copy pattern from GenerateImageNode lines 295-333): |
||||
|
```typescript |
||||
|
const handleCarouselPrevious = useCallback(async () => { ... }, [...]); |
||||
|
const handleCarouselNext = useCallback(async () => { ... }, [...]); |
||||
|
``` |
||||
|
Adapt for video: use `videoHistory`, `selectedVideoHistoryIndex`, `outputVideo` |
||||
|
|
||||
|
5. 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 |
||||
|
|
||||
|
6. Add loading overlay for carousel navigation (copy from GenerateImageNode lines 572-595): |
||||
|
- Inside the video preview div, add conditional overlay when isLoadingCarouselVideo is true |
||||
|
</action> |
||||
|
<verify>Visual inspection: Generate 2+ videos with same node, verify carousel appears and navigation works</verify> |
||||
|
<done>GenerateVideoNode has prev/next carousel buttons showing after first regeneration, allowing users to browse video history</done> |
||||
|
</task> |
||||
|
|
||||
|
</tasks> |
||||
|
|
||||
|
<verification> |
||||
|
Before declaring phase complete: |
||||
|
- [ ] `npm run build` succeeds without errors |
||||
|
- [ ] GenerateVideoNode shows carousel controls after 2+ generations |
||||
|
- [ ] Carousel navigation loads previous videos from disk |
||||
|
- [ ] Videos persist across page refresh (stored in generations folder) |
||||
|
- [ ] No TypeScript errors |
||||
|
</verification> |
||||
|
|
||||
|
<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> |
||||
|
|
||||
|
<output> |
||||
|
After completion, create `.planning/phases/09-video-history/09-01-SUMMARY.md` following summary template. |
||||
|
</output> |
||||
Loading…
Reference in new issue