# Data Model: YouTube Design Preview Replica **Date**: 2026-01-29 ## Entities ### PreviewMode (Enum) ```typescript type PreviewMode = 'desktop' | 'sidebar' | 'mobile'; ``` | Value | Description | Thumbnail Size | |-------|-------------|----------------| | `desktop` | YouTube homepage/search grid layout | 360x202px | | `sidebar` | YouTube related videos sidebar | 168x94px | | `mobile` | YouTube mobile app full-width layout | 100% width | ### ThemeMode (Enum) ```typescript type ThemeMode = 'light' | 'dark'; ``` ### VideoMetadata User-customizable metadata for preview display. ```typescript interface VideoMetadata { title: string; // Video title (max 100 chars for display) channelName: string; // Channel name channelAvatarUrl?: string; // Optional channel avatar image URL duration: string; // Format: "MM:SS" or "H:MM:SS" viewCount: number; // Raw number, formatted for display publishedAt: Date; // Used for "X days ago" calculation } ``` **Validation Rules:** - `title`: Required, 1-100 characters - `channelName`: Required, 1-50 characters - `duration`: Required, format `/^\d{1,2}:\d{2}(:\d{2})?$/` - `viewCount`: Required, non-negative integer - `publishedAt`: Required, valid Date, not in future ### UploadedThumbnail Represents a user-uploaded thumbnail image. ```typescript interface UploadedThumbnail { id: string; // UUID dataUrl: string; // Base64 data URL of the image originalName: string; // Original filename fileSize: number; // Size in bytes (max 5MB) mimeType: string; // 'image/jpeg' | 'image/png' | 'image/webp' width: number; // Original image width height: number; // Original image height uploadedAt: Date; // Timestamp } ``` **Validation Rules:** - `fileSize`: Max 5,242,880 bytes (5MB) - `mimeType`: Only 'image/jpeg', 'image/png', 'image/webp' - `width`, `height`: Positive integers ### PreviewState Complete state for the preview tool, persisted to localStorage. ```typescript interface PreviewState { // Active preview configuration currentThumbnail: UploadedThumbnail | null; previewMode: PreviewMode; themeMode: ThemeMode; metadata: VideoMetadata; // History for quick switching recentThumbnails: UploadedThumbnail[]; // Max 10 items // UI state showMetadataEditor: boolean; // Persistence metadata lastSaved: Date; version: number; // Schema version for migrations } ``` ### VisualTestResult Result of Playwright visual comparison test. ```typescript interface VisualTestResult { testId: string; previewMode: PreviewMode; themeMode: ThemeMode; capturedAt: Date; // Comparison metrics matchPercentage: number; // 0-100 passed: boolean; // matchPercentage >= threshold (98%) // File references actualScreenshotPath: string; expectedScreenshotPath: string; diffImagePath?: string; // Only if failed // Error info errorMessage?: string; } ``` ### ReferenceScreenshot Stored YouTube reference for visual comparison. ```typescript interface ReferenceScreenshot { id: string; previewMode: PreviewMode; themeMode: ThemeMode; capturedAt: Date; capturedFrom: string; // YouTube URL filePath: string; // Metadata viewportWidth: number; viewportHeight: number; browserType: string; // 'chromium' | 'firefox' | 'webkit' // Version tracking isActive: boolean; // Current baseline replacedBy?: string; // ID of newer version } ``` ## Local Storage Schema Key: `thumbpreview_state` ```typescript interface LocalStorageSchema { version: 1; state: PreviewState; } ``` **Storage Limits:** - Total localStorage budget: ~5MB - Each thumbnail (base64): ~1-2MB typical - Keep max 10 recent thumbnails - Automatic cleanup of oldest when limit reached ## State Transitions ### Thumbnail Upload Flow ``` [No Thumbnail] | v (user uploads file) [Validating] | +-- (invalid) --> [Error: show message] --> [No Thumbnail] | v (valid) [Processing] | v (resize/optimize if needed) [Preview Ready] | v (auto-save to localStorage) [Persisted] ``` ### Preview Mode Switch ``` [Current Mode] | v (user selects new mode) [Transitioning] | v (re-render with new layout) [New Mode Active] | v (persist preference) [Saved] ``` ### Theme Mode Switch ``` [Current Theme] | v (user clicks toggle) [Apply Theme Class] | v (CSS variables update) [New Theme Active] | v (persist preference) [Saved] ``` ## Relationships ``` PreviewState | +-- 1:1 --> currentThumbnail (UploadedThumbnail) | +-- 1:N --> recentThumbnails (UploadedThumbnail[]) | +-- 1:1 --> metadata (VideoMetadata) | +-- enum --> previewMode (PreviewMode) | +-- enum --> themeMode (ThemeMode) VisualTestResult | +-- ref --> ReferenceScreenshot (expectedScreenshotPath) | +-- enum --> previewMode | +-- enum --> themeMode ``` ## Default Values ```typescript const DEFAULT_METADATA: VideoMetadata = { title: 'Your Video Title Here', channelName: 'Your Channel', channelAvatarUrl: undefined, duration: '10:30', viewCount: 125000, publishedAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // 1 week ago }; const DEFAULT_STATE: PreviewState = { currentThumbnail: null, previewMode: 'desktop', themeMode: 'light', metadata: DEFAULT_METADATA, recentThumbnails: [], showMetadataEditor: false, lastSaved: new Date(), version: 1 }; ```