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:
mrleo1nidandClaude Opus 5.5 committed 2026-10-19 23:21:31 +03:00
1 parent 36e99fe960
commit 9eb1c25147
30 files changed
+7538

No files matched your search

+165
View File
@@ -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.
- **Английская локализация**: в прототипе только русские строки; при вёрстке проверять длинные строки на обоих языках.