myYouTube/UI_UX_spec.md

1019 lines
28 KiB
Markdown
Raw Normal View History

# UI/UX-спецификация: персональный YouTube-клиент
**Назначение:** передать coding agent вместе с основным техническим заданием.
**Формат:** single-user self-hosted web app, в будущем PWA/mobile client.
## 1. Концепция
Приложение должно ощущаться как персональная лента YouTube-подписок, но с нормальной категоризацией каналов и локальным хранением выбранных видео.
Пользовательский сценарий:
```text
Открыть приложение
→ выбрать категорию
→ увидеть последние видео каналов этой категории
→ смотреть через YouTube
→ при желании нажать "Скачать"
→ после загрузки видеть "На сервере"
→ дальше смотреть локальную копию через MeTube
```
Интерфейс не должен выглядеть как admin panel. Это consumer media UI.
---
## 2. Общий layout
Desktop:
```text
┌──────────────────────────────────────────────────────────────────────┐
│ Header │
├──────────────────────┬───────────────────────────────────────────────┤
│ Sidebar │ Main content │
│ │ │
│ Все видео │ Video feed │
│ Без категории │ │
│ На сервере │ │
│ │ │
│ КАТЕГОРИИ │ │
│ Linux │ │
│ Шахматы │ │
│ Научпоп │ │
│ │ │
│ Каналы │ │
│ Настройки │ │
└──────────────────────┴───────────────────────────────────────────────┘
```
Sidebar: 220–260 px.
Header: фиксированный сверху.
Main content: responsive grid.
Mobile: sidebar превращается в drawer/hamburger.
---
## 3. Визуальный стиль
Тёмная тема по умолчанию.
Рекомендуемые design tokens:
```text
Background main #0F0F0F
Surface #181818
Surface hover #272727
Border #303030
Primary text #F1F1F1
Secondary text #AAAAAA
Accent #3EA6FF
Success/local #2BA640
Warning/downloading #F5A623
Error #E53935
```
Использовать CSS variables, а не раскидывать цвета по компонентам.
Типографика:
```text
Inter / Roboto / system-ui
Page title 24px / 600
Section title 18px / 600
Video title 15–16px / 500
Metadata 12–14px
Buttons 14px / 500
```
---
## 4. Header
Пример:
```text
┌──────────────────────────────────────────────────────────────────────┐
│ ▶ MyTube 🔍 Поиск... ✓ 12 мин ⚙ │
└──────────────────────────────────────────────────────────────────────┘
```
Слева:
- логотип;
- рабочее название `MyTube` или configurable app name.
Центр:
- поиск по локальной БД: video title + channel title.
Справа:
- sync indicator;
- settings.
Sync states:
```text
✓ Обновлено 12 мин назад
↻ Обновление...
⚠ Ошибка синхронизации
```
По клику на sync indicator:
```text
Подписки: обновлены 20:11
Видео: обновлены 20:12
MeTube: доступен
```
---
## 5. Sidebar
Пример:
```text
● Все видео
Без категории
На сервере
КАТЕГОРИИ
🐧 Linux
♟ Шахматы
🔬 Научпоп
🎮 Игры
+ Новая категория
────────────
Каналы
Настройки
```
Активная категория должна иметь заметный, но спокойный selected state.
`Все видео`, `Без категории`, `На сервере` — виртуальные категории.
---
## 6. Feed
Route:
```text
/
```
или:
```text
/feed
```
Header выбранной категории:
```text
Linux
24 канала
Обновлено 12 минут назад [↻ Обновить]
```
Опциональные chips:
```text
[Все] [На сервере] [Не скачаны]
```
Сортировка видео:
```text
published_at DESC
```
---
## 7. Video grid
Responsive:
```text
>= 1800 px → 5 columns
>= 1400 px → 4 columns
>= 1000 px → 3 columns
>= 700 px → 2 columns
mobile → 1 column
```
Рекомендуемый CSS:
```css
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
```
---
## 8. Video card
Пример:
```text
┌────────────────────────────────────┐
│ │
│ THUMBNAIL │
│ 18:42 │
│ │
├────────────────────────────────────┤
│ Linux Kernel 7.0 — What's New │
│ The Linux Experiment │
│ 2 часа назад │
│ │
│ [▶ Смотреть] [⬇ Скачать] │
└────────────────────────────────────┘
```
Thumbnail:
- aspect ratio 16:9;
- duration badge справа снизу.
Карточка содержит:
- thumbnail;
- title;
- channel name;
- publish time;
- duration;
- download/local state;
- Watch;
- Download.
---
## 9. Состояния скачивания
### Не скачано
```text
[▶ Смотреть] [⬇ Скачать]
```
### В очереди
```text
[▶ Смотреть] [В очереди...]
```
### Скачивается
```text
[▶ Смотреть] [64%]
```
Дополнительно progress-line внизу thumbnail.
### Post-processing
```text
[▶ Смотреть] [Обработка...]
```
### На сервере
```text
[▶ Смотреть] [✓ На сервере]
```
### Ошибка
```text
[▶ Смотреть] [⚠ Повторить]
```
Не показывать traceback.
---
## 10. Video page
Route:
```text
/video/:youtubeVideoId
```
Desktop:
```text
┌──────────────────────────────────────────────────────────────────┐
│ │
│ VIDEO PLAYER │
│ │
└──────────────────────────────────────────────────────────────────┘
Linux Kernel 7.0 — What's New
The Linux Experiment
2 часа назад [⬇ Скачать]
Источник: YouTube
[Linux] [IT]
────────────────────────────────────────────────────────────────────
Описание видео...
```
Если local copy существует:
```text
Источник: ● Локальная копия · mediaVM
```
и вместо YouTube iframe используется HTML5 `<video>`.
Пользователь не выбирает источник вручную по умолчанию.
Логика:
```text
local available?
yes → local player
no → YouTube player
```
Если local player вернул ошибку/404:
```text
Локальная копия недоступна.
Переключено на YouTube.
```
---
## 11. Channels page
Route:
```text
/channels
```
Главная задача — быстро распределять подписки по категориям.
Пример:
```text
Каналы
[Поиск каналов...] [Только без категории]
┌────────────────────────────────────────────────────┐
│ ○ The Linux Experiment │
│ [Linux ×] [IT ×] [+ Категория] │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ ○ GothamChess │
│ [Шахматы ×] [+ Категория] │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ ○ Veritasium │
│ [Научпоп ×] [+ Категория] │
└────────────────────────────────────────────────────┘
```
Не делать отдельную страницу редактирования для каждого канала.
---
## 12. Category picker
По кнопке:
```text
+ Категория
```
показывать popover:
```text
Категории
☑ Linux
☑ IT
☐ Научпоп
☐ Игры
──────────────
+ Создать новую
```
Checkbox сохраняется сразу.
Отдельная кнопка `Сохранить` не нужна.
---
## 13. Массовая категоризация
Желательная функция.
Пользователь отмечает несколько каналов:
```text
☑ Channel A
☑ Channel B
☑ Channel C
```
Появляется action bar:
```text
Выбрано: 3
[Добавить в категорию] [Убрать из категории] [Отмена]
```
Если MVP затягивается, допускается реализовать после базовой версии.
---
## 14. Category management
Route:
```text
/settings/categories
```
Пример:
```text
Категории
≡ Linux 24 канала ✎ ⋮
≡ Шахматы 12 каналов ✎ ⋮
≡ Научпоп 8 каналов ✎ ⋮
[+ Новая категория]
```
Поддержать:
- create;
- rename;
- delete;
- reorder.
При удалении:
```text
Удалить категорию "Linux"?
Каналы удалены не будут.
Они просто перестанут относиться к этой категории.
[Отмена] [Удалить]
```
---
## 15. Virtual category "Без категории"
Показывает каналы/видео каналов, которые ещё не распределены.
Header:
```text
Без категории
17 каналов пока не распределены.
[Перейти к каналам]
```
---
## 16. Virtual category "На сервере"
Показывает видео:
```text
download_status = completed
```
Это простая локальная библиотека без отдельного сложного media-manager.
---
## 17. Empty states
### Нет категорий
```text
Категорий пока нет
Раздели подписки по темам:
Linux, Шахматы, Научпоп и т.д.
[Создать первую категорию]
```
### В категории нет видео
```text
Здесь пока нет видео
Добавь каналы в эту категорию
или обнови подписки.
[Добавить каналы]
```
### YouTube ещё не подключён
```text
Подключи свой YouTube
Мы импортируем список подписок и последние видео.
Доступ к YouTube только read-only.
[Подключить YouTube]
```
---
## 18. Loading states
Не использовать fullscreen spinner.
Использовать skeleton video cards.
Background sync не должен блокировать интерфейс.
---
## 19. Error UX
Global toast:
```text
Не удалось связаться с MeTube
```
или:
```text
YouTube временно недоступен
```
Critical banner:
```text
⚠ Требуется повторная авторизация YouTube
[Подключить заново]
```
Background sync не должен постоянно спамить toast notifications.
---
## 20. Settings
Route:
```text
/settings
```
Sections:
```text
YouTube
MeTube
Синхронизация
Интерфейс
О приложении
```
### YouTube
```text
Google account:
user@example.com
Статус: Подключено
Последняя синхронизация:
Сегодня, 20:15
[Обновить сейчас]
[Переподключить]
```
### MeTube
```text
MeTube
Статус: ● Доступен
Сервер: mediaVM
Адрес: 192.168.8.177:8081
[Проверить соединение]
```
Не показывать на основном UI filesystem path или internal job IDs.
### Sync
```text
Подписки: каждые 6 часов
Видео: каждые 60 минут
Последний запуск:
20:12
[Синхронизировать сейчас]
```
---
## 21. First-run onboarding
Flow:
```text
Open app
→ Подключить YouTube
→ Google OAuth
→ initial sync
→ channels imported
→ предложить категоризацию
```
После initial sync:
```text
Готово!
Импортировано:
143 канала
Теперь можно распределить их по категориям.
[Настроить категории]
[Сделать позже]
```
---
## 22. Mobile UX
Header:
```text
☰ MyTube ↻
```
Категории можно показывать horizontal chips:
```text
[Все] [Linux] [Шахматы] [Научпоп] →
```
Video card:
```text
┌───────────────────────────────┐
│ │
│ Thumbnail │
│ │
└───────────────────────────────┘
Video title
Channel · 2 часа назад
[▶ Смотреть] [⬇ Скачать]
```
Touch targets минимум ~44×44 px.
---
## 23. Navigation routes
Рекомендуемые routes:
```text
/ all feed
/category/:categoryId category feed
/channels channels
/video/:youtubeVideoId video detail/player
/local local videos
/uncategorized uncategorized
/settings settings
/settings/categories category management
```
Browser Back после просмотра видео должен возвращать:
- в ту же категорию;
- примерно на ту же scroll position.
---
## 24. Components
Frontend должен быть разделён на reusable components:
```text
AppShell
Header
Sidebar
CategoryNav
VideoGrid
VideoCard
VideoThumbnail
DownloadButton
DownloadStatus
Player
YouTubePlayer
LocalPlayer
ChannelRow
CategoryPicker
CategoryChip
SyncIndicator
EmptyState
ErrorBanner
Toast
ConfirmDialog
SkeletonVideoCard
```
Не делать весь UI одним React component.
---
## 25. Feed API object
Желательно, чтобы backend сразу отдавал frontend всё необходимое для одной карточки:
```json
{
"youtube_video_id": "abc123",
"title": "Linux Kernel 7.0",
"channel": {
"id": 12,
"youtube_channel_id": "UC...",
"title": "The Linux Experiment",
"thumbnail_url": "..."
},
"thumbnail_url": "...",
"published_at": "2026-09-16T18:00:00Z",
"duration_seconds": 1042,
"categories": [
{
"id": 2,
"name": "Linux"
}
],
"local": {
"available": true,
"status": "completed",
"progress_percent": 100,
"media_url": "http://..."
}
}
```
Запрещён frontend pattern:
```text
GET feed
→ отдельный request channel для каждой карточки
→ отдельный request download status для каждой карточки
```
Не должно быть N+1 API requests.
---
## 26. Desktop wireframe
```text
┌────────────────────────────────────────────────────────────────────────────┐
│ ▶ MyTube 🔍 Поиск ✓ 12 мин ⚙ │
├──────────────────────┬─────────────────────────────────────────────────────┤
│ │ │
│ ● Все видео │ Linux │
│ Без категории │ 24 канала [↻ Обновить] │
│ На сервере │ │
│ │ [Все] [На сервере] [Не скачаны] │
│ КАТЕГОРИИ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ 🐧 Linux │ │ thumbnail │ │ thumbnail │ │ thumbnail │ │
│ ♟ Шахматы │ │ 18:42│ │ 12:03│ │ 8:41│ │
│ 🔬 Научпоп │ └─────────────┘ └─────────────┘ └─────────────┘ │
│ 🎮 Игры │ Kernel 7.0... NixOS update... New GNOME... │
│ │ Linux Exp. Brodie R. DistroTube │
│ + Категория │ 2 часа назад 4 часа назад вчера │
│ │ [▶] [⬇] [▶] [64%] [▶] [✓ Local] │
│ ───────────── │ │
│ Каналы │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ Настройки │ │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │
└──────────────────────┴─────────────────────────────────────────────────────┘
```
---
## 27. Channels wireframe
```text
┌──────────────────────────────────────────────────────────────────────┐
│ Каналы │
│ │
│ 🔍 Поиск каналов... [Только без категории] │
│ │
├──────────────────────────────────────────────────────────────────────┤
│ ○ The Linux Experiment │
│ [Linux ×] [IT ×] [+ Категория] │
├──────────────────────────────────────────────────────────────────────┤
│ ○ GothamChess │
│ [Шахматы ×] [+ Категория] │
├──────────────────────────────────────────────────────────────────────┤
│ ○ Veritasium │
│ [Научпоп ×] [+ Категория] │
├──────────────────────────────────────────────────────────────────────┤
│ ○ Some Channel │
│ [+ Категория] │
└──────────────────────────────────────────────────────────────────────┘
```
---
## 28. Video page wireframe
```text
┌──────────────────────────────────────────────────────────────────────┐
│ │
│ VIDEO PLAYER │
│ │
└──────────────────────────────────────────────────────────────────────┘
Linux Kernel 7.0 — What's New
The Linux Experiment
2 часа назад
● Локальная копия · mediaVM [✓ На сервере]
[Linux] [IT]
────────────────────────────────────────────────────────────────────────
Описание видео...
```
Когда local отсутствует:
```text
Источник: YouTube [⬇ Скачать]
```
---
## 29. UX-антипаттерны
Не делать:
- Bootstrap-like admin tables как основной UI;
- raw JSON;
- container names/job IDs на главном экране;
- filesystem paths;
- огромные формы;
- отдельный Save после каждого checkbox;
- модальное окно на каждое простое действие;
- fullscreen spinner при background sync;
- N+1 API requests;
- слишком много badges на thumbnail;
- сильные hover zoom animations.
---
## 30. Accessibility
Минимум:
- semantic HTML;
- keyboard navigation;
- visible focus ring;
- aria-label для icon buttons;
- alt у изображений;
- нормальный contrast;
- `<button>` для кнопок;
- состояние не должно кодироваться только цветом.
---
## 31. Icons
Использовать один icon set, например Lucide React.
Основные icons:
```text
Home
Folder
Download
Play
Check
RefreshCw
Settings
Search
MoreVertical
AlertTriangle
Server
Youtube
Menu
```
---
## 32. Анимации
Минимально:
- hover 150–200 ms;
- popover fade;
- drawer slide;
- progress;
- skeleton pulse.
Не делать сложные page transitions.
---
## 33. Рекомендуемый порядок реализации UI
Сначала сделать интерфейс на mock data:
```text
App shell
Sidebar
Video grid
Video card
Channels page
Category picker
Video page
Mobile responsive
```
Только после визуальной проверки подключать реальные backend API.
---
## 34. Definition of Done UI
UI MVP готов, если:
1. Пользователь может подключить YouTube.
2. После sync видит подписанные каналы.
3. Может создать категорию.
4. Может назначить каналу одну или несколько категорий.
5. Может выбрать категорию и увидеть соответствующую video feed.
6. Карточка показывает thumbnail/title/channel/time/duration.
7. Можно открыть video page.
8. Нескачанный ролик играет через YouTube.
9. Работает Download.
10. Отображаются queued/downloading/completed/failed.
11. Completed ролик имеет статус `На сервере`.
12. Completed ролик играет через MeTube local URL.
13. Есть loading/error/empty states.
14. UI usable на desktop и mobile.
15. Browser refresh сохраняет route.
16. UI не показывает secrets/internal paths/job IDs.
17. На feed нет N+1 requests.
18. Основной экран воспринимается как видеоприложение, а не административная панель.
---
## 35. Главное UX-правило
Пользователь должен воспринимать сервис так:
> Это моя лента YouTube-подписок, только каналы нормально разложены по темам, а понравившееся видео я могу одним кликом оставить у себя.
Технические детали:
```text
OAuth
YouTube Data API
PostgreSQL
MeTube
Socket.IO
mediaVM
filenames
```
должны быть скрыты за простым интерфейсом.