docs: add product idea, design prototypes and development documentation
- IDEA.md: product vision from the user's perspective (input for design)
- Architecture, ADRs, data model, REST API overview
- Auth and roles, library storage (NFS, path regexes, scanner, search),
playback, recommendation system ("My Wave"), track upload queue
- Web, Android (offline, self-update), i18n, design system
- Testing strategy, Gitea Actions CI/CD, deployment, conventions, roadmap
- Clickable web/Android design prototypes and wave button variants
- .claude/launch.json to serve the prototypes locally
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
36e99fe960
commit
9eb1c25147
30 files changed
+7538
No files matched your search
+165
@@ -0,0 +1,165 @@
|
||||
# Дизайн и дизайн-система
|
||||
|
||||
Источник — кликабельные прототипы в [`Разработка дизайна приложения/`](Разработка%20дизайна%20приложения/):
|
||||
|
||||
| Файл | Что внутри |
|
||||
|---|---|
|
||||
| `Pipu Web.dc.html` | Веб: вход, setup-экраны, главная, поиск, исполнитель, альбом, коллекция, профиль, вся админка, диалоги, очередь плеера |
|
||||
| `Pipu Android.dc.html` | Android: подключение к серверу, вход, главная, поиск, коллекция (со «Скачанным»), альбом, исполнитель, профиль, полноэкранный плеер, листы (качество, пароль, очередь, обновление), офлайн-режим |
|
||||
| `Wave Options.dc.html` | Три варианта кнопки «Моя волна» |
|
||||
| `wave.js` | Реализация кнопки волны (Canvas 2D, React) — **референс для продакшен-компонента** |
|
||||
| `pipu-data.js` | Демо-данные прототипа |
|
||||
| `support.js`, `android-frame.jsx` | Рантайм прототипа и рамка устройства — в продукт не переносятся |
|
||||
|
||||
Просмотр прототипов: прототипам нужен HTTP-сервер (из `file://` скрипты не работают).
|
||||
В репозитории есть конфиг `.claude/launch.json` (`python -m http.server 8765 --directory docs`),
|
||||
вручную — та же команда, затем `http://localhost:8765/`.
|
||||
|
||||
## 1. Принятые решения по вариантам прототипа
|
||||
|
||||
| Развилка | Решение |
|
||||
|---|---|
|
||||
| Кнопка «Моя волна» | **Сфера с шумом (`orb`)** и **Жидкая форма (`liquid`)** — пользователь выбирает в профиле, по умолчанию `orb`. Вариант «Кольца» не реализуем. |
|
||||
| Каркас веба | **Classic**: сайдбар 240 px, топбар 60 px, разделители-линии, без «парящих» панелей |
|
||||
| Плеер веба | **Оба**: `bar` (полоса 88 px внизу, по умолчанию) и `island` (плавающий остров) — выбор в профиле |
|
||||
| Главная | **Center**: волна по центру, под ней чипы «Занятие» и «Настроение» |
|
||||
| Очередь на добавление (админ) | `grouped` — группировка по альбомам (вариант прототипа по умолчанию) |
|
||||
| Полноэкранный плеер Android | `cover` (крупная обложка) по умолчанию, `wave` (визуализация волны) — переключатель в профиле |
|
||||
| Тема | Тёмная (по умолчанию) и светлая, переключатель в профиле; `system` — следовать ОС |
|
||||
| Ambient-фон | Включён: размытые дрейфующие пятна, третье пятно окрашено в цвет обложки текущего трека; отключается в профиле и при `prefers-reduced-motion` |
|
||||
|
||||
Пользовательские настройки внешнего вида хранятся в `users.ui_prefs` и синхронизируются между устройствами
|
||||
(см. [data-model.md](data-model.md)).
|
||||
|
||||
## 2. Токены
|
||||
|
||||
Источник истины — `packages/design-tokens/tokens.json`; из него генерируются CSS-переменные (веб) и TS-объект (React Native).
|
||||
Значения взяты из прототипа без изменений.
|
||||
|
||||
### Цвета
|
||||
|
||||
| Токен | Тёмная | Светлая | Назначение |
|
||||
|---|---|---|---|
|
||||
| `bg` | `#05070D` | `#F3F5FA` | Фон приложения |
|
||||
| `bg2` | `#080B14` | `#EBEEF5` | Вторичный фон |
|
||||
| `surface` | `#0B0F1A` | `#FFFFFF` | Карточки, панели |
|
||||
| `surface2` | `#121827` | `#F0F3F9` | Поля ввода, вложенные блоки |
|
||||
| `surface3` | `#1A2236` | `#E4E9F3` | Чипы, кнопки второго уровня |
|
||||
| `elev` | `#111726` | `#FFFFFF` | Поповеры, меню |
|
||||
| `hover` | `rgba(120,150,255,.07)` | `rgba(30,60,140,.06)` | Подсветка при наведении |
|
||||
| `line` / `line2` | `rgba(130,160,255,.10)` / `.18` | `rgba(20,35,80,.10)` / `.17` | Разделители / рамки |
|
||||
| `text` / `text2` / `text3` | `#EDF1FF` / `#A0ABC6` / `#737F9C` | `#0A0F1E` / `#4B5675` / `#677290` | Текст: основной / вторичный / подписи |
|
||||
| `accent` / `accent2` | `#2F80FF` / `#5E9BFF` | `#1F6FF2` / `#1F6FF2` | Основной акцент, ссылки |
|
||||
| `accent-soft` | `rgba(47,128,255,.15)` | `rgba(31,111,242,.10)` | Активный пункт меню, фокус |
|
||||
| `violet` / `violet-soft` | `#9B7CFF` / `rgba(124,92,255,.16)` | `#6A47F0` / `.10` | Второй акцент (волна, бейджи) |
|
||||
| `ok` / `ok-soft` | `#2FD39A` | `#0E9465` | Успех |
|
||||
| `warn` / `warn-soft` | `#FFB547` | `#A86200` | Предупреждение |
|
||||
| `danger` / `danger-soft` | `#FF5C70` | `#D32F47` | Ошибка, дизлайк, отклонение |
|
||||
| `overlay` | `rgba(3,5,10,.66)` | `rgba(20,30,60,.32)` | Подложка диалогов |
|
||||
| `glass` | `rgba(12,16,28,.78)` | `rgba(255,255,255,.84)` | Стеклянные карточки (вход, диалоги) + `backdrop-filter: blur(28px) saturate(1.3)` |
|
||||
|
||||
Палитра волны по умолчанию: `#2F80FF`, `#7C5CFF`, `#35D3FF`. Логотип — радиальный градиент `#9CC6FF → #2F80FF → #3A1FA8`.
|
||||
|
||||
### Типографика
|
||||
|
||||
| Роль | Шрифт | Где |
|
||||
|---|---|---|
|
||||
| Заголовки, логотип, надпись на волне | **Unbounded** 500–700, `letter-spacing: -0.01em` | Заголовки страниц 24–28 px, «Моя волна» |
|
||||
| Основной текст | **Onest** 400–700 | Всё остальное; базовый размер 15 px, подписи 12–13 px |
|
||||
| Моноширинный | **JetBrains Mono** 400/600 | Пути файлов, регэкспы, шаблоны, временные пароли, хэши |
|
||||
| Иконки | **Material Symbols Rounded** (вариативный, `FILL 0..1`) | Залитые иконки — активное состояние |
|
||||
|
||||
Все шрифты — OFL / Apache 2.0. **Встраиваем в сборку** (`@fontsource/*`, сабсеты latin + cyrillic) — self-hosted
|
||||
сервис не должен зависеть от Google Fonts. Иконки — сабсет Material Symbols только с используемыми глифами
|
||||
(генерируется скриптом при сборке).
|
||||
|
||||
### Формы и тени
|
||||
|
||||
| Токен | Значение |
|
||||
|---|---|
|
||||
| Радиусы | 8 (мелкие кнопки), 10–12 (поля, кнопки), 16 (карточки), 24 (крупные карточки, диалоги), 999 (чипы, бейджи) |
|
||||
| Высота поля / основной кнопки | 48 px |
|
||||
| `shadow` | `0 24px 64px rgba(0,0,0,.55)` (светлая: `rgba(20,30,60,.18)`) |
|
||||
| `glow` | `0 8px 28px rgba(47,128,255,.38)` — основная кнопка |
|
||||
| Фокус | `border-color: accent` + `box-shadow: 0 0 0 3px accent-soft` |
|
||||
|
||||
### Анимация
|
||||
|
||||
| Что | Как |
|
||||
|---|---|
|
||||
| Появление контента | `pipuIn`: opacity + translateY(6px), 200 мс |
|
||||
| Эквалайзер текущего трека | `pipuEq`: scaleY 0.25 → 1 |
|
||||
| Ambient-фон | 3 пятна 55–70vmax, blur 30 px, дрейф 16 / 21 / 26 с, `hue-rotate` ±35° за 30 с, прозрачность `amb-op` (0.8 тёмная / 0.45 светлая) |
|
||||
| Пружины (волна, нажатия) | Демпфированная пружина как в `wave.js` (`v += (target − x)·0.35; v *= 0.62`) |
|
||||
| `prefers-reduced-motion` | Ambient статичен, волна замедлена ×0.15 без пульса |
|
||||
|
||||
## 3. Кнопка «Моя волна»
|
||||
|
||||
Продакшен-компонент — порт `wave.js` на TypeScript, **Canvas 2D** (прототип показал, что этого достаточно; WebGL не нужен).
|
||||
|
||||
```ts
|
||||
interface WaveButtonProps {
|
||||
variant: 'orb' | 'liquid';
|
||||
playing: boolean;
|
||||
bpm?: number; // из TrackDto.features, по умолчанию 100
|
||||
energy?: number; // 0..1, по умолчанию 0.6
|
||||
colors?: [string, string, string]; // палитра обложки текущего трека, иначе палитра волны
|
||||
theme: 'dark' | 'light';
|
||||
reduced?: boolean; // prefers-reduced-motion
|
||||
interactive?: boolean; // false — декоративный фон экрана входа
|
||||
size: number; // 260–360 px на главной, 620 px на фоне входа
|
||||
onClick?(): void;
|
||||
}
|
||||
```
|
||||
|
||||
Поведение (как в прототипе):
|
||||
- **Пульс в такт**: фаза растёт со скоростью `bpm/60`, импульс `(1 − phase)^4 · (0.35 + 0.65·energy)`; в покое — медленное «дыхание».
|
||||
- **Hover**: скорость анимации ×(1 + 1.4·hover), формы смещаются к курсору; **press**: сжатие до 0.93 с отскоком;
|
||||
клик / Enter / Space — запуск / пауза.
|
||||
- Внутри — иконка `play_arrow` / `pause` (44 px) и надпись «Моя волна» (Unbounded 18 px), подпись контекста («Под ваш вкус», «Бегаю · Энергичное»).
|
||||
- Канвас масштабируется по `devicePixelRatio`; анимация останавливается, когда вкладка скрыта.
|
||||
- Android: тот же алгоритм на `@shopify/react-native-skia` (API Skia Canvas близок к Canvas 2D), общие формулы —
|
||||
в `packages/core/wave-anim`.
|
||||
|
||||
## 4. Каркас и экраны
|
||||
|
||||
### Веб (Classic)
|
||||
- Сетка: `grid-template-rows: 60px 1fr 88px` (плеер `bar`) или `60px 1fr` + остров снизу (`island`, отступ контента 132 px).
|
||||
- Топбар: логотип, поиск по центру (выпадающий список с группами «Исполнители / Альбомы / Треки» и подсказкой
|
||||
исправленной раскладки), аватар с меню профиля (тема, язык, выход).
|
||||
- Сайдбар: Главная · Коллекция (История, Лайки + счётчик, Дизлайки + счётчик) · Админка (Пользователи, Хранилища,
|
||||
Библиотека, Предложить треки, Очередь + бейдж, Настройки) — пункты по ролям.
|
||||
- Плеер: обложка, название / исполнитель, лайк / дизлайк, назад / play (круглая белая кнопка) / вперёд, прогресс со временем,
|
||||
очередь, громкость. Очередь — выезжающая справа панель; в режиме волны внизу пометка «Волна бесконечна…».
|
||||
- Диалоги: стеклянные карточки по центру поверх `overlay`. Тосты — внизу над плеером.
|
||||
|
||||
Экраны, которые есть в прототипе, и документы, где описана логика:
|
||||
|
||||
| Экран | Логика |
|
||||
|---|---|
|
||||
| Вход, смена учётных данных админа, обязательная смена пароля | [auth-and-roles.md](auth-and-roles.md) |
|
||||
| Главная, поиск, исполнитель, альбом | [web.md](web.md), [library-and-storage.md](library-and-storage.md#поиск) |
|
||||
| Коллекция: История (по дням) / Лайки / Дизлайки | [api.md](api.md) |
|
||||
| Профиль: пароль, язык, тема, кроссфейд, выравнивание громкости (EBU R128), вид волны, вид плеера, выход | [playback.md](playback.md) |
|
||||
| Пользователи: таблица, создание (пароль генерируется автоматически), временный пароль один раз, меню действий | [auth-and-roles.md](auth-and-roles.md) |
|
||||
| Хранилища: NFS / local, опции, «Принимает новые треки», регэкспы с перетаскиванием, предпросмотр, шаблон раскладки с автоподбором и проверкой | [library-and-storage.md](library-and-storage.md) |
|
||||
| Библиотека: состояние, скан, аудиоанализ, качество волны за 7 дней, ошибки | [recommendations.md](recommendations.md#1-цели-и-как-измеряем-успех) |
|
||||
| Предложить треки, Очередь на добавление, Настройки очереди | [uploads.md](uploads.md) |
|
||||
|
||||
### Android
|
||||
- Нижняя навигация: Главная · Поиск · Коллекция · Профиль; мини-плеер над ней; полноэкранный плеер (`cover` / `wave`).
|
||||
- Подключение: адрес сервера → проверка (`/server-info`: версия, HTTPS, совместимость) → список сохранённых серверов.
|
||||
- Поиск: недавние запросы (локально), подсказка про опечатки и раскладку.
|
||||
- Коллекция → Скачанное: занято / свободно, «Скачивать новые лайки», «Скачать все лайки», очередь загрузок «по Wi-Fi».
|
||||
- Профиль: пароль, язык, качество звука (лист), только Wi-Fi, кэш недавних треков (очистить), проверка обновлений
|
||||
(точка-индикатор), сервер, «Админка — откроется в браузере» (для персонала), выход.
|
||||
- Лист обновления: «Что нового» → разрешение на установку (один раз) → загрузка → проверка SHA-256 → «Установить».
|
||||
- Офлайн-баннер при отсутствии сети.
|
||||
|
||||
## 5. Пробелы дизайна (дорабатываем при реализации)
|
||||
- **Адаптивность веба**: прототип рассчитан на десктоп (≥ 1200 px). Для < 1024 px сайдбар сворачивается в иконки,
|
||||
для < 768 px — нижняя навигация как в Android. Макетов нет — делаем по образцу Android-версии.
|
||||
- **Пустые состояния и ошибки** есть не везде (например, хранилище недоступно, нет сети в вебе) — по образцу существующих
|
||||
(иконка + короткий текст + действие).
|
||||
- **Онбординг вкуса** (выбор жанров / исполнителей при первом входе) в прототипе отсутствует — макет нужен до M6.
|
||||
- **Английская локализация**: в прототипе только русские строки; при вёрстке проверять длинные строки на обоих языках.
|
||||
Reference in new issue
Block a user