/
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,43 +0,0 @@
|
||||
# Pull Request Template
|
||||
|
||||
## Description
|
||||
<!-- Briefly describe the purpose of this PR. -->
|
||||
|
||||
## Motivation
|
||||
<!-- Explain why this change is needed. What problem does it solve? -->
|
||||
|
||||
## Type of Change
|
||||
- [ ] New Feature
|
||||
- [ ] Bug Fix
|
||||
- [ ] Refactor / Code Cleanup
|
||||
- [ ] Documentation Update
|
||||
- [ ] Other (please specify)
|
||||
|
||||
## Related Issue(s)
|
||||
<!-- Link to any related issue(s) (e.g., #123) -->
|
||||
|
||||
## Screenshots / Video
|
||||
<!-- Include screenshots or a short video demonstrating the change. If the change adds a new UI feature, attach an image. If it adds functionality best shown via video, embed a video. -->
|
||||
|
||||
**Screenshot** (if applicable):
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
**Video** (if applicable):
|
||||
|
||||
```html
|
||||
<video src="path/to/video.mp4" controls width="600"></video>
|
||||
```
|
||||
|
||||
## Testing
|
||||
<!-- Describe how reviewers can test the changes. Include steps, commands, or environment setup. -->
|
||||
|
||||
## Checklist
|
||||
- [ ] I have performed a self-review of my code.
|
||||
- [ ] I have added any necessary screenshots or videos.
|
||||
- [ ] I have linked related issue(s) and updated the changelog if applicable.
|
||||
|
||||
---
|
||||
*Thank you for contributing!*
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
name: Build Electron App
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install app dependencies
|
||||
run: npx electron-builder install-app-deps
|
||||
|
||||
- name: Build Windows app
|
||||
run: npm run build:win
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload Windows build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-installer
|
||||
path: release/**/*.exe
|
||||
retention-days: 30
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install app dependencies
|
||||
run: npx electron-builder install-app-deps
|
||||
|
||||
- name: Build macOS app
|
||||
run: npm run build:mac
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload macOS build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-installer
|
||||
path: release/**/*.dmg
|
||||
retention-days: 30
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install app dependencies
|
||||
run: npx electron-builder install-app-deps
|
||||
|
||||
- name: Build Linux app
|
||||
run: npm run build:linux
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload Linux build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-installer
|
||||
path: release/**/*.AppImage
|
||||
retention-days: 30
|
||||
|
||||
+6
-11
@@ -182,13 +182,12 @@ local.settings.json
|
||||
# ============================================================================
|
||||
# KIRO AI ASSISTANT
|
||||
# ============================================================================
|
||||
# Settings contain personal API tokens - NEVER commit
|
||||
.kiro/settings/
|
||||
.kiro/
|
||||
# Keep specs, steering, and hooks (shared with team)
|
||||
# .kiro/specs/ - COMMIT
|
||||
# .kiro/steering/ - COMMIT
|
||||
# .kiro/hooks/ - COMMIT
|
||||
|
||||
# ============================================================================
|
||||
# GITHUB
|
||||
# ============================================================================
|
||||
.github/
|
||||
|
||||
# ============================================================================
|
||||
# TEMPORARY & BACKUP FILES
|
||||
@@ -207,11 +206,7 @@ temp/
|
||||
# EDITOR & IDE
|
||||
# ============================================================================
|
||||
# VS Code
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
.vscode/
|
||||
.history/
|
||||
*.vsix
|
||||
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
# Design Document: GIF Export Feature
|
||||
|
||||
## Overview
|
||||
|
||||
This design extends the existing video export system to support animated GIF output. The implementation introduces a new export format selection UI, a GIF encoder module using the `gif.js` library for client-side GIF encoding, and modifications to the existing export pipeline to support both MP4 and GIF workflows.
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph UI["Export Dialog UI"]
|
||||
FS[Format Selector]
|
||||
MP4Opts[MP4 Options Panel]
|
||||
GIFOpts[GIF Options Panel]
|
||||
Progress[Progress Display]
|
||||
end
|
||||
|
||||
subgraph Exporter["Export Pipeline"]
|
||||
VE[VideoExporter]
|
||||
GE[GifExporter]
|
||||
FR[FrameRenderer]
|
||||
VD[VideoDecoder]
|
||||
end
|
||||
|
||||
subgraph Output["Output"]
|
||||
MP4[MP4 File]
|
||||
GIF[GIF File]
|
||||
end
|
||||
|
||||
FS --> |MP4 Selected| MP4Opts
|
||||
FS --> |GIF Selected| GIFOpts
|
||||
MP4Opts --> VE
|
||||
GIFOpts --> GE
|
||||
VE --> FR
|
||||
GE --> FR
|
||||
FR --> VD
|
||||
VE --> MP4
|
||||
GE --> GIF
|
||||
```
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### 1. Export Format Types
|
||||
|
||||
```typescript
|
||||
// src/lib/exporter/types.ts
|
||||
|
||||
export type ExportFormat = 'mp4' | 'gif';
|
||||
|
||||
export type GifFrameRate = 10 | 15 | 20 | 25 | 30;
|
||||
|
||||
export type GifSizePreset = 'small' | 'medium' | 'large' | 'original';
|
||||
|
||||
export interface GifExportConfig {
|
||||
frameRate: GifFrameRate;
|
||||
loop: boolean;
|
||||
sizePreset: GifSizePreset;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface ExportSettings {
|
||||
format: ExportFormat;
|
||||
// MP4 settings
|
||||
quality?: ExportQuality;
|
||||
// GIF settings
|
||||
gifConfig?: GifExportConfig;
|
||||
}
|
||||
|
||||
export const GIF_SIZE_PRESETS: Record<GifSizePreset, { maxHeight: number; label: string }> = {
|
||||
small: { maxHeight: 480, label: 'Small (480p)' },
|
||||
medium: { maxHeight: 720, label: 'Medium (720p)' },
|
||||
large: { maxHeight: 1080, label: 'Large (1080p)' },
|
||||
original: { maxHeight: Infinity, label: 'Original' },
|
||||
};
|
||||
|
||||
export const GIF_FRAME_RATES: { value: GifFrameRate; label: string }[] = [
|
||||
{ value: 10, label: '10 FPS - Smaller file' },
|
||||
{ value: 15, label: '15 FPS - Balanced' },
|
||||
{ value: 20, label: '20 FPS - Smooth' },
|
||||
{ value: 25, label: '25 FPS - Very smooth' },
|
||||
{ value: 30, label: '30 FPS - Maximum' },
|
||||
];
|
||||
```
|
||||
|
||||
### 2. GIF Exporter Module
|
||||
|
||||
```typescript
|
||||
// src/lib/exporter/gifExporter.ts
|
||||
|
||||
interface GifExporterConfig {
|
||||
videoUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
frameRate: GifFrameRate;
|
||||
loop: boolean;
|
||||
wallpaper: string;
|
||||
zoomRegions: ZoomRegion[];
|
||||
trimRegions?: TrimRegion[];
|
||||
cropRegion: CropRegion;
|
||||
annotationRegions?: AnnotationRegion[];
|
||||
// ... other effect settings
|
||||
onProgress?: (progress: ExportProgress) => void;
|
||||
}
|
||||
|
||||
export class GifExporter {
|
||||
private config: GifExporterConfig;
|
||||
private decoder: VideoFileDecoder | null = null;
|
||||
private renderer: FrameRenderer | null = null;
|
||||
private gif: GIF | null = null;
|
||||
private cancelled = false;
|
||||
|
||||
constructor(config: GifExporterConfig);
|
||||
|
||||
async export(): Promise<ExportResult>;
|
||||
cancel(): void;
|
||||
private cleanup(): void;
|
||||
private getEffectiveDuration(totalDuration: number): number;
|
||||
private mapEffectiveToSourceTime(effectiveTimeMs: number): number;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Export Dialog Component Updates
|
||||
|
||||
```typescript
|
||||
// src/components/video-editor/ExportDialog.tsx
|
||||
|
||||
interface ExportDialogProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onExport: (settings: ExportSettings) => void;
|
||||
progress: ExportProgress | null;
|
||||
isExporting: boolean;
|
||||
error: string | null;
|
||||
onCancel?: () => void;
|
||||
aspectRatio: AspectRatio;
|
||||
sourceWidth: number;
|
||||
sourceHeight: number;
|
||||
}
|
||||
|
||||
// Internal state
|
||||
interface ExportDialogState {
|
||||
format: ExportFormat;
|
||||
quality: ExportQuality;
|
||||
gifFrameRate: GifFrameRate;
|
||||
gifLoop: boolean;
|
||||
gifSizePreset: GifSizePreset;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Format Selector Component
|
||||
|
||||
```typescript
|
||||
// src/components/video-editor/FormatSelector.tsx
|
||||
|
||||
interface FormatSelectorProps {
|
||||
selectedFormat: ExportFormat;
|
||||
onFormatChange: (format: ExportFormat) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. GIF Options Panel Component
|
||||
|
||||
```typescript
|
||||
// src/components/video-editor/GifOptionsPanel.tsx
|
||||
|
||||
interface GifOptionsPanelProps {
|
||||
frameRate: GifFrameRate;
|
||||
onFrameRateChange: (rate: GifFrameRate) => void;
|
||||
loop: boolean;
|
||||
onLoopChange: (loop: boolean) => void;
|
||||
sizePreset: GifSizePreset;
|
||||
onSizePresetChange: (preset: GifSizePreset) => void;
|
||||
outputDimensions: { width: number; height: number };
|
||||
disabled?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Data Models
|
||||
|
||||
### Export Configuration Flow
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> FormatSelection
|
||||
FormatSelection --> MP4Options: Select MP4
|
||||
FormatSelection --> GIFOptions: Select GIF
|
||||
MP4Options --> Exporting: Click Export
|
||||
GIFOptions --> Exporting: Click Export
|
||||
Exporting --> Success: Complete
|
||||
Exporting --> Error: Failed
|
||||
Exporting --> Cancelled: User Cancel
|
||||
Success --> [*]
|
||||
Error --> FormatSelection: Retry
|
||||
Cancelled --> FormatSelection
|
||||
```
|
||||
|
||||
### GIF Encoding Pipeline
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as ExportDialog
|
||||
participant GE as GifExporter
|
||||
participant FR as FrameRenderer
|
||||
participant VD as VideoDecoder
|
||||
participant GIF as gif.js Worker
|
||||
|
||||
UI->>GE: export(config)
|
||||
GE->>VD: loadVideo(url)
|
||||
VD-->>GE: videoInfo
|
||||
GE->>FR: initialize()
|
||||
GE->>GIF: new GIF(options)
|
||||
|
||||
loop For each frame
|
||||
GE->>VD: seek(time)
|
||||
GE->>FR: renderFrame(videoFrame)
|
||||
FR-->>GE: canvas
|
||||
GE->>GIF: addFrame(canvas)
|
||||
GE->>UI: onProgress(%)
|
||||
end
|
||||
|
||||
GE->>GIF: render()
|
||||
GIF-->>GE: blob
|
||||
GE-->>UI: ExportResult
|
||||
```
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
*A property is a characteristic or behavior that should hold true across all valid executions of a system—essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||
|
||||
### Property 1: Valid Frame Rate Acceptance
|
||||
|
||||
*For any* frame rate value, the GIF_Exporter SHALL accept it if and only if it is one of the valid presets (10, 15, 20, 25, 30 FPS). Invalid frame rates should be rejected with an error.
|
||||
|
||||
**Validates: Requirements 2.2**
|
||||
|
||||
### Property 2: Loop Encoding Correctness
|
||||
|
||||
*For any* GIF export configuration, when loop is enabled the output GIF SHALL have a loop count of 0 (infinite), and when loop is disabled the output GIF SHALL have a loop count of 1 (play once).
|
||||
|
||||
**Validates: Requirements 3.2, 3.3**
|
||||
|
||||
### Property 3: Size Preset Resolution Mapping
|
||||
|
||||
*For any* valid size preset and source video dimensions, the GIF_Exporter SHALL produce output with height matching the preset's max height (or source height if smaller), with width calculated to maintain aspect ratio.
|
||||
|
||||
**Validates: Requirements 4.2**
|
||||
|
||||
### Property 4: Aspect Ratio Preservation
|
||||
|
||||
*For any* source video with aspect ratio R and any size preset, the exported GIF SHALL have an aspect ratio within 0.01 of R.
|
||||
|
||||
**Validates: Requirements 4.4**
|
||||
|
||||
### Property 5: Valid GIF Output
|
||||
|
||||
*For any* successful GIF export, the output blob SHALL be a valid GIF file that can be parsed by standard GIF decoders, containing the expected number of frames based on video duration and frame rate.
|
||||
|
||||
**Validates: Requirements 5.3**
|
||||
|
||||
### Property 6: Frame Count Consistency
|
||||
|
||||
*For any* video with effective duration D (excluding trim regions) and frame rate F, the exported GIF SHALL contain approximately D × F frames (within ±1 frame tolerance).
|
||||
|
||||
**Validates: Requirements 5.1**
|
||||
|
||||
### Property 7: MP4 Export Regression
|
||||
|
||||
*For any* valid MP4 export configuration that worked before this feature, the Video_Exporter SHALL continue to produce valid MP4 output with the same quality characteristics.
|
||||
|
||||
**Validates: Requirements 7.2**
|
||||
|
||||
|
||||
|
||||
## Error Handling
|
||||
|
||||
### GIF Export Errors
|
||||
|
||||
| Error Condition | Handling Strategy |
|
||||
|----------------|-------------------|
|
||||
| Video load failure | Display error message, allow retry |
|
||||
| Frame rendering failure | Log error, skip frame, continue if possible |
|
||||
| GIF encoding failure | Display error message with details |
|
||||
| Memory exhaustion | Suggest smaller size preset or lower frame rate |
|
||||
| User cancellation | Clean up resources, reset UI state |
|
||||
| Invalid configuration | Validate before export, show validation errors |
|
||||
|
||||
### Error Messages
|
||||
|
||||
```typescript
|
||||
const GIF_EXPORT_ERRORS = {
|
||||
VIDEO_LOAD_FAILED: 'Failed to load video. Please try again.',
|
||||
ENCODING_FAILED: 'GIF encoding failed. Try reducing size or frame rate.',
|
||||
MEMORY_ERROR: 'Not enough memory. Try a smaller size preset.',
|
||||
CANCELLED: 'Export cancelled.',
|
||||
INVALID_CONFIG: 'Invalid export configuration.',
|
||||
};
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Unit tests will cover:
|
||||
- GIF configuration validation (frame rate, size preset, loop settings)
|
||||
- Dimension calculation for size presets
|
||||
- Aspect ratio calculation and preservation
|
||||
- Export settings serialization/deserialization
|
||||
- UI component state management
|
||||
|
||||
### Property-Based Tests
|
||||
|
||||
Property-based tests will validate the correctness properties using fast-check:
|
||||
|
||||
1. **Frame Rate Validation Property**: Generate random integers, verify only valid frame rates are accepted
|
||||
2. **Loop Encoding Property**: Generate random loop settings, verify GIF metadata matches
|
||||
3. **Size Preset Property**: Generate random video dimensions and presets, verify output dimensions
|
||||
4. **Aspect Ratio Property**: Generate random aspect ratios, verify preservation within tolerance
|
||||
5. **Valid GIF Property**: Generate random export configs, verify output is parseable GIF
|
||||
6. **Frame Count Property**: Generate random durations and frame rates, verify frame count
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- End-to-end export flow for both MP4 and GIF
|
||||
- Format switching in export dialog
|
||||
- Progress reporting during export
|
||||
- Cancellation handling
|
||||
- Error recovery scenarios
|
||||
|
||||
### Test Configuration
|
||||
|
||||
```typescript
|
||||
// Property test configuration
|
||||
const PBT_CONFIG = {
|
||||
numRuns: 100,
|
||||
seed: Date.now(),
|
||||
};
|
||||
```
|
||||
|
||||
### Testing Library
|
||||
|
||||
- **Property-Based Testing**: fast-check (already available in the ecosystem)
|
||||
- **Unit Testing**: Vitest (existing test framework)
|
||||
- **Component Testing**: React Testing Library
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### GIF Library Selection
|
||||
|
||||
Using `gif.js` for client-side GIF encoding:
|
||||
- Web Worker support for non-blocking encoding
|
||||
- Configurable quality and dithering
|
||||
- Frame delay control for variable frame rates
|
||||
- Loop count configuration
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
1. **Memory Management**: Process frames in batches to avoid memory exhaustion
|
||||
2. **Worker Threads**: Use gif.js workers for parallel encoding
|
||||
3. **Progress Reporting**: Report progress after each frame for responsive UI
|
||||
4. **Cancellation**: Check cancelled flag between frames for quick abort
|
||||
|
||||
### File Size Estimation
|
||||
|
||||
Approximate GIF file size formula:
|
||||
```
|
||||
estimatedSize = width × height × frameCount × colorDepthFactor × compressionRatio
|
||||
```
|
||||
|
||||
Where:
|
||||
- colorDepthFactor ≈ 0.5 (for 256 colors with LZW compression)
|
||||
- compressionRatio ≈ 0.3-0.7 (depends on content complexity)
|
||||
@@ -1,106 +0,0 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
This feature extends the existing video export functionality to support GIF export as an alternative output format. The export dialog will be redesigned to present users with a format selection menu, allowing them to choose between MP4 video export (with quality options) or GIF export (with GIF-specific options like frame rate, looping, and size).
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Export_Dialog**: The modal UI component that displays export options and progress
|
||||
- **Format_Selector**: The UI component allowing users to choose between MP4 and GIF export formats
|
||||
- **GIF_Exporter**: The module responsible for encoding video frames into animated GIF format
|
||||
- **Frame_Rate**: The number of frames per second in the output GIF (affects file size and smoothness)
|
||||
- **Loop_Setting**: Configuration determining whether the GIF plays once or loops infinitely
|
||||
- **Size_Preset**: Predefined output dimension options for GIF export (e.g., small, medium, large)
|
||||
- **Quality_Preset**: Predefined quality levels for MP4 export (low, medium, high/source)
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1: Export Format Selection
|
||||
|
||||
**User Story:** As a user, I want to choose between exporting as MP4 or GIF, so that I can create the appropriate format for my use case.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the user clicks the export button, THE Export_Dialog SHALL display a format selection menu with MP4 and GIF options
|
||||
2. WHEN the user selects MP4 format, THE Export_Dialog SHALL display MP4-specific quality options (low, medium, high/source)
|
||||
3. WHEN the user selects GIF format, THE Export_Dialog SHALL display GIF-specific options (frame rate, loop, size)
|
||||
4. THE Export_Dialog SHALL remember the user's last selected format for the current session
|
||||
|
||||
### Requirement 2: GIF Frame Rate Configuration
|
||||
|
||||
**User Story:** As a user, I want to control the frame rate of my GIF export, so that I can balance between smoothness and file size.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN GIF format is selected, THE Export_Dialog SHALL display a frame rate selector with preset options
|
||||
2. THE GIF_Exporter SHALL support frame rates of 10, 15, 20, 25, and 30 FPS
|
||||
3. WHEN a frame rate is selected, THE Export_Dialog SHALL display an estimated file size indicator
|
||||
4. THE GIF_Exporter SHALL default to 15 FPS for optimal balance between quality and file size
|
||||
|
||||
### Requirement 3: GIF Loop Configuration
|
||||
|
||||
**User Story:** As a user, I want to control whether my GIF loops or plays once, so that I can create the appropriate animation behavior.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN GIF format is selected, THE Export_Dialog SHALL display a loop toggle option
|
||||
2. WHEN loop is enabled, THE GIF_Exporter SHALL encode the GIF to loop infinitely
|
||||
3. WHEN loop is disabled, THE GIF_Exporter SHALL encode the GIF to play once and stop
|
||||
4. THE GIF_Exporter SHALL default to loop enabled
|
||||
|
||||
### Requirement 4: GIF Size Configuration
|
||||
|
||||
**User Story:** As a user, I want to control the output size of my GIF, so that I can optimize for different platforms and use cases.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN GIF format is selected, THE Export_Dialog SHALL display size preset options
|
||||
2. THE GIF_Exporter SHALL support size presets: Small (480p), Medium (720p), Large (1080p), and Original
|
||||
3. WHEN a size preset is selected, THE Export_Dialog SHALL display the actual output dimensions
|
||||
4. THE GIF_Exporter SHALL maintain the video's aspect ratio when resizing
|
||||
5. THE GIF_Exporter SHALL default to Medium (720p) size preset
|
||||
|
||||
### Requirement 5: GIF Export Processing
|
||||
|
||||
**User Story:** As a user, I want to export my edited video as a GIF with all applied effects, so that I can share animated content.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the user initiates GIF export, THE GIF_Exporter SHALL process all video frames with applied effects (zoom, crop, annotations, trim)
|
||||
2. WHEN exporting, THE Export_Dialog SHALL display real-time progress with percentage and frame count
|
||||
3. WHEN export completes, THE GIF_Exporter SHALL produce a valid animated GIF file
|
||||
4. IF an error occurs during export, THEN THE Export_Dialog SHALL display a descriptive error message
|
||||
5. WHEN the user cancels export, THE GIF_Exporter SHALL stop processing and clean up resources
|
||||
|
||||
### Requirement 6: GIF Color Optimization
|
||||
|
||||
**User Story:** As a user, I want my GIF to have good color quality despite the 256 color limitation, so that the output looks visually appealing.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE GIF_Exporter SHALL use color quantization to reduce colors to 256 per frame
|
||||
2. THE GIF_Exporter SHALL apply dithering to improve perceived color quality
|
||||
3. THE GIF_Exporter SHALL optimize the color palette for each frame or use a global palette based on content
|
||||
|
||||
### Requirement 7: MP4 Export Preservation
|
||||
|
||||
**User Story:** As a user, I want the existing MP4 export functionality to remain available with its current quality options, so that I can still export high-quality videos.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN MP4 format is selected, THE Export_Dialog SHALL display quality options: Low (720p), Medium (1080p), High/Source (original resolution)
|
||||
2. THE Video_Exporter SHALL maintain all existing MP4 export functionality unchanged
|
||||
3. WHEN MP4 export completes, THE system SHALL save the file with .mp4 extension
|
||||
|
||||
### Requirement 8: Export Dialog UI Design
|
||||
|
||||
**User Story:** As a user, I want a clean and intuitive export interface, so that I can easily configure and initiate exports.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Export_Dialog SHALL display format options as visually distinct selectable cards or tabs
|
||||
2. WHEN a format is selected, THE Export_Dialog SHALL animate the transition to show format-specific options
|
||||
3. THE Export_Dialog SHALL display a prominent "Export" button to initiate the export process
|
||||
4. THE Export_Dialog SHALL maintain the existing dark theme and visual style of the application
|
||||
5. WHEN exporting, THE Export_Dialog SHALL disable format selection and show progress UI
|
||||
@@ -1,113 +0,0 @@
|
||||
# Implementation Plan: GIF Export Feature
|
||||
|
||||
## Overview
|
||||
|
||||
This implementation plan adds GIF export capability to the video editor. The work is organized to build incrementally: first extending types, then implementing the GIF encoder, updating the UI, and finally wiring everything together.
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1. Extend export types and configuration
|
||||
- [x] 1.1 Add GIF export types to src/lib/exporter/types.ts
|
||||
- Add ExportFormat, GifFrameRate, GifSizePreset types
|
||||
- Add GifExportConfig and ExportSettings interfaces
|
||||
- Add GIF_SIZE_PRESETS and GIF_FRAME_RATES constants
|
||||
- _Requirements: 2.2, 4.2_
|
||||
|
||||
- [x] 1.2 Write property test for frame rate validation
|
||||
- **Property 1: Valid Frame Rate Acceptance**
|
||||
- **Validates: Requirements 2.2**
|
||||
|
||||
- [x] 2. Implement GIF exporter module
|
||||
- [x] 2.1 Create src/lib/exporter/gifExporter.ts
|
||||
- Implement GifExporter class with constructor and config
|
||||
- Implement export() method using gif.js library
|
||||
- Implement cancel() and cleanup() methods
|
||||
- Reuse FrameRenderer and VideoFileDecoder from existing pipeline
|
||||
- _Requirements: 5.1, 5.3, 5.5_
|
||||
|
||||
- [x] 2.2 Implement frame extraction and GIF encoding loop
|
||||
- Extract frames at configured frame rate
|
||||
- Apply trim region mapping (reuse from VideoExporter)
|
||||
- Add frames to gif.js encoder with proper delay
|
||||
- Report progress via callback
|
||||
- _Requirements: 5.1, 5.2_
|
||||
|
||||
- [x] 2.3 Implement loop and size configuration
|
||||
- Configure gif.js with loop count (0 for infinite, 1 for once)
|
||||
- Calculate output dimensions based on size preset and aspect ratio
|
||||
- _Requirements: 3.2, 3.3, 4.2, 4.4_
|
||||
|
||||
- [x] 2.4 Write property test for loop encoding
|
||||
- **Property 2: Loop Encoding Correctness**
|
||||
- **Validates: Requirements 3.2, 3.3**
|
||||
|
||||
- [x] 2.5 Write property test for aspect ratio preservation
|
||||
- **Property 4: Aspect Ratio Preservation**
|
||||
- **Validates: Requirements 4.4**
|
||||
|
||||
- [x] 3. Checkpoint - Ensure GIF exporter core works
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 4. Update Export Dialog UI
|
||||
- [x] 4.1 Create FormatSelector component
|
||||
- Create src/components/video-editor/FormatSelector.tsx
|
||||
- Implement MP4/GIF toggle with card-style selection
|
||||
- Style to match existing dark theme
|
||||
- _Requirements: 1.1, 8.1_
|
||||
|
||||
- [x] 4.2 Create GifOptionsPanel component
|
||||
- Create src/components/video-editor/GifOptionsPanel.tsx
|
||||
- Implement frame rate dropdown with preset options
|
||||
- Implement loop toggle switch
|
||||
- Implement size preset selector
|
||||
- Display calculated output dimensions
|
||||
- _Requirements: 2.1, 3.1, 4.1, 4.3_
|
||||
|
||||
- [x] 4.3 Update ExportDialog component
|
||||
- Add format selection state and handlers
|
||||
- Conditionally render MP4 options or GIF options based on format
|
||||
- Update onExport to pass ExportSettings
|
||||
- Disable format selection during export
|
||||
- _Requirements: 1.2, 1.3, 1.4, 8.2, 8.3, 8.5_
|
||||
|
||||
- [x] 5. Integrate GIF export into VideoEditor
|
||||
- [x] 5.1 Update VideoEditor handleExport function
|
||||
- Accept ExportSettings from dialog
|
||||
- Route to VideoExporter or GifExporter based on format
|
||||
- Handle GIF blob saving with .gif extension
|
||||
- _Requirements: 5.3, 7.3_
|
||||
|
||||
- [x] 5.2 Add GIF exporter to index exports
|
||||
- Update src/lib/exporter/index.ts to export GifExporter
|
||||
- Export new types
|
||||
- _Requirements: 5.1_
|
||||
|
||||
- [x] 6. Checkpoint - Ensure full integration works
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 7. Write remaining property tests
|
||||
- [x] 7.1 Write property test for size preset resolution
|
||||
- **Property 3: Size Preset Resolution Mapping**
|
||||
- **Validates: Requirements 4.2**
|
||||
|
||||
- [x] 7.2 Write property test for valid GIF output
|
||||
- **Property 5: Valid GIF Output**
|
||||
- **Validates: Requirements 5.3**
|
||||
|
||||
- [x] 7.3 Write property test for frame count consistency
|
||||
- **Property 6: Frame Count Consistency**
|
||||
- **Validates: Requirements 5.1**
|
||||
|
||||
- [x] 7.4 Write regression test for MP4 export
|
||||
- **Property 7: MP4 Export Regression**
|
||||
- **Validates: Requirements 7.2**
|
||||
|
||||
- [x] 8. Final checkpoint - All tests pass
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
## Notes
|
||||
|
||||
- Each task references specific requirements for traceability
|
||||
- Checkpoints ensure incremental validation
|
||||
- Property tests validate universal correctness properties
|
||||
- The gif.js library needs to be installed: `npm install gif.js @types/gif.js`
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user