Files
pipu-music/docs/design.md
T
mrleo1nidandClaude Opus 5.5 9eb1c25147 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>
2026-10-19 23:21:31 +03:00

15 KiB
Raw Permalink Blame History

Дизайн и дизайн-система

Источник — кликабельные прототипы в Разработка дизайна приложения/:

Файл Что внутри
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).

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 не нужен).

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
Главная, поиск, исполнитель, альбом web.md, library-and-storage.md
Коллекция: История (по дням) / Лайки / Дизлайки api.md
Профиль: пароль, язык, тема, кроссфейд, выравнивание громкости (EBU R128), вид волны, вид плеера, выход playback.md
Пользователи: таблица, создание (пароль генерируется автоматически), временный пароль один раз, меню действий auth-and-roles.md
Хранилища: NFS / local, опции, «Принимает новые треки», регэкспы с перетаскиванием, предпросмотр, шаблон раскладки с автоподбором и проверкой library-and-storage.md
Библиотека: состояние, скан, аудиоанализ, качество волны за 7 дней, ошибки recommendations.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.
  • Английская локализация: в прототипе только русские строки; при вёрстке проверять длинные строки на обоих языках.