docs initial
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
# Subtitle Preset Grid Redesign - Design Document
|
||||
|
||||
**Date:** 2026-04-06
|
||||
**Scope:** Redesign preset preview cards in Caption Settings step to match uploaded video aspect ratio with modern visual refresh
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Redesign the subtitle preset selection grid to:
|
||||
1. Display preset previews with the **same aspect ratio as the uploaded video**
|
||||
2. Apply a **modern visual refresh** consistent with the app's design language
|
||||
3. Show **style characteristics** (font, colors) as subtle hints
|
||||
4. Maintain **responsive layout** across screen sizes
|
||||
|
||||
---
|
||||
|
||||
## Core Functionality
|
||||
|
||||
### Dynamic Aspect Ratio
|
||||
|
||||
**Data Flow:**
|
||||
1. Fetch video metadata via `GET /api/media/mediafiles/{media_file_id}/` using `primaryFileId` from WizardContext
|
||||
2. Extract `width` and `height` from `MediaFileRead` response
|
||||
3. Calculate aspect ratio: `width / height`
|
||||
4. Apply as CSS `aspect-ratio` to preset cards via inline style or CSS variable
|
||||
5. Handle loading state while fetching metadata
|
||||
6. Fallback to 16:9 if no video is uploaded or API error occurs
|
||||
|
||||
**Implementation Notes:**
|
||||
- Store aspect ratio in WizardContext alongside other video metadata
|
||||
- Update ratio when `primaryFileId` changes
|
||||
- Cards use container queries for responsive sizing
|
||||
|
||||
---
|
||||
|
||||
## Visual Design (5 Pillars Applied)
|
||||
|
||||
### 1. Typography with Character
|
||||
- Keep existing font system (consistent with app)
|
||||
- Style name: `font-weight: 500`, `font-size: 14px`
|
||||
- Characteristic labels: `font-size: 12px`, muted color (`--gray-10`)
|
||||
|
||||
### 2. Committed Color & Theme
|
||||
- Uses **Catppuccin Mocha** palette matching the project:
|
||||
- Canvas: `--bg-canvas: #11111b`
|
||||
- Cards: `--bg-default: #1e1e2e`
|
||||
- Surfaces: `--bg-surface: #313244`
|
||||
- Borders: `--border-default: #45475a`, `--border-subtle: #313244`
|
||||
- Text: `--text-primary: #cdd6f4`, `--text-secondary: #bac2de`, `--text-tertiary: #9399b2`
|
||||
- Selected state: purple accent (`--purple-400: #cba6f7`) with glow shadow
|
||||
- Card hover: border transitions to purple accent
|
||||
- System badge: purple-100 background with purple-400 text
|
||||
- Checkmark indicator on selected card (top-right corner)
|
||||
|
||||
### 3. Purposeful Motion
|
||||
- Cards fade in with staggered animation (50ms delay per card)
|
||||
- Smooth border-color transition on hover (150ms ease)
|
||||
- Selection change: immediate border color change
|
||||
- Loading skeleton: shimmer animation
|
||||
|
||||
### 4. Brave Spatial Composition
|
||||
- CSS Grid with `auto-fill` and `minmax(200px, 1fr)`
|
||||
- Consistent 16px gap between cards
|
||||
- Cards maintain video aspect ratio without stretching
|
||||
- Responsive: more columns on wide screens, fewer on narrow
|
||||
|
||||
### 5. Atmosphere & Depth
|
||||
- Card background: subtle gradient overlay for depth
|
||||
- Selected card: elevated with `box-shadow` + accent glow
|
||||
- Dark preview background (`#0c0a1a`) preserved from existing StylePreview
|
||||
- Rounded corners: `border-radius: 12px`
|
||||
|
||||
---
|
||||
|
||||
## Component Structure
|
||||
|
||||
### PresetCard
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ │
|
||||
│ [StylePreview Component] │ ← Dynamic aspect-ratio
|
||||
│ "Пример субтитров" │ based on video
|
||||
│ │
|
||||
├──────────────────────────────────────┤
|
||||
│ Style Name [Системный] │ ← Footer
|
||||
│ Lobster · Yellow accent │ ← Characteristics (subtle)
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Props:**
|
||||
- `preset: CaptionPresetRead`
|
||||
- `isSelected: boolean`
|
||||
- `aspectRatio: number` (width/height, e.g., 1.777 for 16:9)
|
||||
- `onSelect: () => void`
|
||||
- `onEdit: () => void`
|
||||
- `onDelete: () => void`
|
||||
|
||||
### StylePreview Updates
|
||||
|
||||
**New Props:**
|
||||
- `aspectRatio?: number` - overrides default 9/16
|
||||
|
||||
**Behavior:**
|
||||
- Uses passed `aspectRatio` for container sizing
|
||||
- Falls back to 9/16 if not provided
|
||||
- Maintains all existing text styling logic
|
||||
|
||||
### PresetGrid Updates
|
||||
|
||||
**New Behavior:**
|
||||
- Fetches video metadata via `useVideoMetadata()` hook
|
||||
- Passes `aspectRatio` to all PresetCard children
|
||||
- Shows skeleton loading state while fetching
|
||||
- Responsive grid layout
|
||||
|
||||
---
|
||||
|
||||
## Style Characteristics Display
|
||||
|
||||
Each card footer shows:
|
||||
- **Font family** (e.g., "Lobster", "Inter") - extracted from `preset.style_config.text.font_family`
|
||||
- **Accent color** - small color dot + name if distinct from default
|
||||
- Hidden on cards narrower than 180px (responsive)
|
||||
|
||||
**Format:**
|
||||
```
|
||||
{font_family} · {accent_color_name}
|
||||
```
|
||||
|
||||
Example: `Lobster · Желтый` or `Inter · Неоновый`
|
||||
|
||||
---
|
||||
|
||||
## Loading State
|
||||
|
||||
**Skeleton Card:**
|
||||
- Same aspect ratio as target (default 16:9 while loading)
|
||||
- Shimmer animation on preview area
|
||||
- Gray placeholder for text
|
||||
- 4-6 skeleton cards shown while loading
|
||||
|
||||
---
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
| Screen Width | Grid Columns | Card Min Width |
|
||||
|--------------|--------------|----------------|
|
||||
| < 480px | 2 | 140px |
|
||||
| 480-768px | 3 | 160px |
|
||||
| 768-1200px | 4 | 180px |
|
||||
| > 1200px | 5-6 | 200px |
|
||||
|
||||
---
|
||||
|
||||
## API Integration
|
||||
|
||||
### New Hook: `useVideoMetadata`
|
||||
|
||||
```typescript
|
||||
function useVideoMetadata(fileId: string | null) {
|
||||
return api.useQuery(
|
||||
"get",
|
||||
"/api/media/mediafiles/{media_file_id}/",
|
||||
{ params: { path: { media_file_id: fileId ?? "" } } },
|
||||
{ enabled: !!fileId }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Aspect Ratio Calculation
|
||||
|
||||
```typescript
|
||||
const aspectRatio = useMemo(() => {
|
||||
if (!mediaFile?.width || !mediaFile?.height) return 16 / 9
|
||||
return mediaFile.width / mediaFile.height
|
||||
}, [mediaFile])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Edge Cases
|
||||
|
||||
1. **No video uploaded:** Fall back to 16:9 aspect ratio
|
||||
2. **Video metadata unavailable:** Show error toast, fall back to 16:9
|
||||
3. **Very wide video (>21:9):** Cap max card width to prevent overflow
|
||||
4. **Very tall video (9:16+):** Limit max height, allow scrolling if needed
|
||||
5. **No presets:** Show empty state with "Создать пресет" card only
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. `src/features/project/CaptionSettingsStep/PresetGrid.tsx` - Grid logic, aspect ratio distribution
|
||||
2. `src/features/project/CaptionSettingsStep/PresetGrid.module.scss` - Grid styles, responsive layout
|
||||
3. `src/features/project/CaptionSettingsStep/StylePreview.tsx` - Accept aspect ratio prop
|
||||
4. `src/features/project/CaptionSettingsStep/StylePreview.module.scss` - Dynamic sizing
|
||||
5. `src/features/project/CaptionSettingsStep/useVideoMetadata.ts` - New hook (or inline in PresetGrid)
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Preset cards display with uploaded video's aspect ratio
|
||||
- [ ] Grid is responsive and works on mobile/desktop
|
||||
- [ ] Loading state shows skeleton cards
|
||||
- [ ] Style characteristics (font, color) visible on cards
|
||||
- [ ] Selected state clearly visible with accent border
|
||||
- [ ] Hover effects smooth and purposeful
|
||||
- [ ] Fallback to 16:9 when no video available
|
||||
- [ ] All existing functionality preserved (select, edit, delete, create)
|
||||
Reference in New Issue
Block a user