feat: youtube preview
This commit is contained in:
278
specs/002-youtube-design-preview/contracts/types.ts
Normal file
278
specs/002-youtube-design-preview/contracts/types.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* YouTube Design Preview - TypeScript Contracts
|
||||
*
|
||||
* These interfaces define the data structures used in the preview tool.
|
||||
* This file serves as the contract between components.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Enums
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Preview display modes matching YouTube's layout variations
|
||||
*/
|
||||
export type PreviewMode = 'desktop' | 'sidebar' | 'mobile';
|
||||
|
||||
/**
|
||||
* Theme modes matching YouTube's color schemes
|
||||
*/
|
||||
export type ThemeMode = 'light' | 'dark';
|
||||
|
||||
/**
|
||||
* Supported image formats for thumbnail upload
|
||||
*/
|
||||
export type SupportedImageFormat = 'image/jpeg' | 'image/png' | 'image/webp';
|
||||
|
||||
// ============================================================================
|
||||
// Core Entities
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Video metadata displayed alongside the thumbnail
|
||||
*/
|
||||
export interface VideoMetadata {
|
||||
/** Video title (1-100 characters) */
|
||||
title: string;
|
||||
|
||||
/** Channel name (1-50 characters) */
|
||||
channelName: string;
|
||||
|
||||
/** Optional channel avatar URL or base64 data URL */
|
||||
channelAvatarUrl?: string;
|
||||
|
||||
/** Duration in format "MM:SS" or "H:MM:SS" */
|
||||
duration: string;
|
||||
|
||||
/** View count as raw number (formatted for display) */
|
||||
viewCount: number;
|
||||
|
||||
/** Publication date (used for relative time display) */
|
||||
publishedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploaded thumbnail image with metadata
|
||||
*/
|
||||
export interface UploadedThumbnail {
|
||||
/** Unique identifier (UUID) */
|
||||
id: string;
|
||||
|
||||
/** Base64 data URL of the image */
|
||||
dataUrl: string;
|
||||
|
||||
/** Original filename */
|
||||
originalName: string;
|
||||
|
||||
/** File size in bytes (max 5MB = 5,242,880) */
|
||||
fileSize: number;
|
||||
|
||||
/** MIME type */
|
||||
mimeType: SupportedImageFormat;
|
||||
|
||||
/** Original image dimensions */
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
/** Upload timestamp */
|
||||
uploadedAt: Date;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// State Management
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Complete preview state persisted to localStorage
|
||||
*/
|
||||
export interface PreviewState {
|
||||
/** Currently active thumbnail for preview */
|
||||
currentThumbnail: UploadedThumbnail | null;
|
||||
|
||||
/** Active preview layout mode */
|
||||
previewMode: PreviewMode;
|
||||
|
||||
/** Active color theme */
|
||||
themeMode: ThemeMode;
|
||||
|
||||
/** Video metadata for display */
|
||||
metadata: VideoMetadata;
|
||||
|
||||
/** Recently used thumbnails (max 10) */
|
||||
recentThumbnails: UploadedThumbnail[];
|
||||
|
||||
/** UI state: metadata editor visibility */
|
||||
showMetadataEditor: boolean;
|
||||
|
||||
/** Last persistence timestamp */
|
||||
lastSaved: Date;
|
||||
|
||||
/** Schema version for migrations */
|
||||
version: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* localStorage serialization wrapper
|
||||
*/
|
||||
export interface LocalStorageSchema {
|
||||
version: 1;
|
||||
state: PreviewState;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Visual Testing
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Result of a visual comparison test
|
||||
*/
|
||||
export interface VisualTestResult {
|
||||
/** Unique test run identifier */
|
||||
testId: string;
|
||||
|
||||
/** Preview mode being tested */
|
||||
previewMode: PreviewMode;
|
||||
|
||||
/** Theme mode being tested */
|
||||
themeMode: ThemeMode;
|
||||
|
||||
/** Test execution timestamp */
|
||||
capturedAt: Date;
|
||||
|
||||
/** Similarity percentage (0-100) */
|
||||
matchPercentage: number;
|
||||
|
||||
/** Pass/fail based on 98% threshold */
|
||||
passed: boolean;
|
||||
|
||||
/** Path to captured screenshot */
|
||||
actualScreenshotPath: string;
|
||||
|
||||
/** Path to reference screenshot */
|
||||
expectedScreenshotPath: string;
|
||||
|
||||
/** Path to diff image (only if failed) */
|
||||
diffImagePath?: string;
|
||||
|
||||
/** Error message if test failed */
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* YouTube reference screenshot metadata
|
||||
*/
|
||||
export interface ReferenceScreenshot {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
|
||||
/** Preview mode this reference is for */
|
||||
previewMode: PreviewMode;
|
||||
|
||||
/** Theme mode this reference is for */
|
||||
themeMode: ThemeMode;
|
||||
|
||||
/** When captured from YouTube */
|
||||
capturedAt: Date;
|
||||
|
||||
/** YouTube URL captured from */
|
||||
capturedFrom: string;
|
||||
|
||||
/** File system path */
|
||||
filePath: string;
|
||||
|
||||
/** Viewport dimensions */
|
||||
viewportWidth: number;
|
||||
viewportHeight: number;
|
||||
|
||||
/** Browser used for capture */
|
||||
browserType: 'chromium' | 'firefox' | 'webkit';
|
||||
|
||||
/** Whether this is the current baseline */
|
||||
isActive: boolean;
|
||||
|
||||
/** ID of newer version if replaced */
|
||||
replacedBy?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Component Props
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Props for YouTubeVideoCard component
|
||||
*/
|
||||
export interface YouTubeVideoCardProps {
|
||||
/** Thumbnail image URL or data URL */
|
||||
thumbnailUrl: string;
|
||||
|
||||
/** Video title */
|
||||
title: string;
|
||||
|
||||
/** Channel name */
|
||||
channelTitle: string;
|
||||
|
||||
/** Formatted view count (e.g., "1.2M views") */
|
||||
viewCount?: string;
|
||||
|
||||
/** Relative time string (e.g., "3 days ago") */
|
||||
publishedAt?: string;
|
||||
|
||||
/** Whether this is user's uploaded thumbnail (for highlighting) */
|
||||
isUserThumbnail?: boolean;
|
||||
|
||||
/** Layout variant */
|
||||
variant?: PreviewMode;
|
||||
|
||||
/** Video duration string */
|
||||
duration?: string;
|
||||
|
||||
/** Optional channel avatar URL */
|
||||
channelAvatarUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for ThemeToggle component
|
||||
*/
|
||||
export interface ThemeToggleProps {
|
||||
/** Current theme mode */
|
||||
theme: ThemeMode;
|
||||
|
||||
/** Callback when theme changes */
|
||||
onThemeChange: (theme: ThemeMode) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for PreviewModeSelector component
|
||||
*/
|
||||
export interface PreviewModeSelectorProps {
|
||||
/** Current preview mode */
|
||||
mode: PreviewMode;
|
||||
|
||||
/** Callback when mode changes */
|
||||
onModeChange: (mode: PreviewMode) => void;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Validation
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Thumbnail upload validation result
|
||||
*/
|
||||
export interface ThumbnailValidationResult {
|
||||
valid: boolean;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation constants
|
||||
*/
|
||||
export const VALIDATION_CONSTANTS = {
|
||||
MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB
|
||||
MAX_TITLE_LENGTH: 100,
|
||||
MAX_CHANNEL_NAME_LENGTH: 50,
|
||||
MAX_RECENT_THUMBNAILS: 10,
|
||||
VISUAL_MATCH_THRESHOLD: 98, // percentage
|
||||
SUPPORTED_FORMATS: ['image/jpeg', 'image/png', 'image/webp'] as const,
|
||||
DURATION_REGEX: /^\d{1,2}:\d{2}(:\d{2})?$/,
|
||||
} as const;
|
||||
Reference in New Issue
Block a user