ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m1s
ci / db / db (push) Successful in 1m16s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m12s
ci / api / api (push) Successful in 45s
ci / recommender / recommender (push) Successful in 1m50s
ci / ts / ts (packages + web) (push) Failing after 6m53s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m13s
- Introduced a new warning for users uploading non-FLAC or WAV formats, indicating that these formats should only be used as a last resort. - Added functionality to highlight files needing format attention in the upload queue. - Updated the UI to provide clearer guidance on acceptable audio formats during uploads. - Improved documentation to reflect changes in upload requirements and user interface updates.
11 KiB
11 KiB
Веб-клиент (apps/web)
Стек
| Задача | Выбор |
|---|---|
| Сборка | Vite, TypeScript (strict) |
| UI | React 19 |
| Роутинг | TanStack Router (типобезопасные пути и search-параметры) |
| Серверное состояние | TanStack Query (кэш, инвалидация, бесконечные списки) |
| Клиентское состояние | Zustand (плеер, очередь, UI) |
| Стили | CSS Modules + CSS-переменные из packages/design-tokens; тёмная и светлая темы (design.md) |
| Шрифты и иконки | @fontsource Onest / Unbounded / JetBrains Mono; иконки — SVG Material Symbols Rounded (@material-symbols/svg-400), импортируются поштучно в shared/ui/icons.ts и рисуются CSS-маской (currentColor), поэтому в сборку попадают только используемые — всё встроено в сборку |
| Компоненты-примитивы | Свои Menu, Dialog, поля и сегменты в shared/ui |
| Анимация | Canvas 2D для кнопки волны |
| Формы | Контролируемые поля; проверка на сервере, ошибки приходят по полям |
| i18n | i18next + react-i18next, словари из packages/i18n |
| API | packages/api-client (сгенерирован из OpenAPI, openapi-typescript + openapi-fetch) |
Структура (feature-sliced)
apps/web/src/
├── app/ # провайдеры, роутер, глобальные стили, гарды
├── pages/ # login, setup-admin, setup-password, home, search, artist, album,
│ # collection/{history,likes,dislikes}, settings,
│ # admin/{users,sources,library,upload,upload-queue,settings}
├── widgets/ # app-shell (топбар, сайдбар), player-bar, wave-button, search-box, track-list
├── features/ # like-track, change-password, mood-picker, create-user, pattern-editor, ...
├── entities/ # track, album, artist, user — модели и маленькие UI-карточки
└── shared/ # ui-kit, lib, hooks, config
Слой импортирует только из нижележащих: pages → widgets → features → entities → shared.
Ключевые компоненты
App shell
Каркас Classic (design.md).
- Сетка: топбар 60 px (логотип, поиск, меню профиля) / сайдбар 240 px (Главная, Коллекция → История / Лайки / Дизлайки, Админка по роли) / контент / плеер.
- Плеер в двух видах, выбор в профиле:
bar— полоса 88 px внизу (по умолчанию),island— плавающий остров. - Ambient-фон за всем интерфейсом: три размытых дрейфующих пятна, одно — в цвете обложки текущего трека.
- Адаптивность: < 1024 px — сайдбар из иконок, < 768 px — нижняя навигация, как в мобильном.
Поиск
SearchBox— combobox по WAI-ARIA (стрелки, Enter, Esc).- Debounce 150 мс, отмена предыдущего запроса,
GET /search/suggest. - Enter без выбранного элемента →
/search?q=…; Enter / клик по элементу → исполнитель / альбом / запуск трека. - Горячая клавиша
/илиCtrl+K— фокус в поиск.
Плеер
- Движок:
HTMLAudioElement→ Web Audio граф (MediaElementSource → Gain (нормализация) → Analyser → destination).Analyserдаёт спектр для анимации кнопки волны (тот же origin — CORS не мешает). - Два элемента
<audio>для предзагрузки следующего трека и бесшовного перехода (опционально — кроссфейд 0–6 с в настройках). - Media Session API: метаданные, обложка, кнопки ОС / наушников.
- Горячие клавиши: пробел — пауза, ←/→ — перемотка 10 с, Shift+←/→ — трек, L — лайк.
- Логика очереди, событий, сохранения позиции — из
packages/core(общая с мобильным); веб реализует только адаптерAudioEngine.
Кнопка «Моя волна»
- Большой круг в центре главной (раскладка Center). Два вида на выбор пользователя: сфера с шумом (
orb, по умолчанию) и жидкая форма (liquid). Реализация — портwave.jsна TS, Canvas 2D; API и поведение — design.md. - В покое — медленное «дыхание». При воспроизведении — пульс с частотой BPM трека и силой от energy,
дополнительно амплитуда от RMS спектра (
AnalyserNode); цвета — палитра обложки текущего трека. - Hover — волны ускоряются и «тянутся» к курсору; нажатие — сжатие / отскок (spring).
- Та же кнопка, увеличенная и неинтерактивная, — декоративный фон экрана входа.
- Под кнопкой —
MoodPicker: чипы занятий и настроений (одно занятие + одно настроение, повторный клик снимает выбор). Смена во время игры волны — без остановки музыки. prefers-reduced-motion— замедленная версия без пульса, ambient-фон статичен.
Профиль и настройки
Пароль, язык, тема (тёмная / светлая / системная), вид волны, вид плеера, ambient-фон, кроссфейд (0–6 с),
выравнивание громкости (EBU R128), выход. Настройки внешнего вида сохраняются в ui_prefs (PATCH /me).
Админка
- Пользователи: таблица с поиском и фильтром по ролям, создание (диалог: логин, имя, роль; пароль генерируется сервером и показывается один раз), сброс пароля (тот же диалог «Временный пароль» с кнопкой «копировать»), блокировка, смена роли (только админу видно).
- Хранилища: список с статусом, форма NFS / local, кнопка «Проверить»; редактор шаблонов — список регэкспов с drag-and-drop порядка, подсветкой синтаксиса и ошибок компиляции, таблица предпросмотра с подсветкой групп.
- Библиотека: счётчики, прогресс скана и анализа (live), список ошибок.
- Хранилища → «Шаблон раскладки»: поле
write_templateс кнопками «Подобрать автоматически» и «Проверить» (таблица расхождений реальный путь ↔ сгенерированный). - Предложить треки (moderator, admin): зона drag-and-drop для файлов и папок, индикатор очереди «X из Y ГБ».
Перед загрузкой — Web Worker считает SHA-256 и читает теги (
music-metadata),POST /uploads/precheck→ дубли сразу показываются как «уже есть: <трек>» и не загружаются. Загрузка —tus-js-client, до 4 параллельно, пауза / возобновление. Ниже — «Мои предложения» со статусами и причинами отклонения. - Очередь на добавление (admin): группы по пачкам, прослушивание из staging, правка метаданных метаданных (поле на весь альбом), live-превью итогового пути, бейджи «возможный дубль» / «лучшее качество», массовые одобрение / отклонение с причиной. Бейдж с числом ожидающих — в сайдбаре.
- Настройки (admin): лимит очереди (ГБ), лимит на модератора, макс. размер файла, хранилище по умолчанию.
Производительность
- Длинные списки (история, лайки, треки альбома) — виртуализация (TanStack Virtual).
- Код-сплиттинг по страницам; админка — отдельный чанк, грузится только для ролей admin / moderator.
- Обложки —
srcset64 / 300 / 1200,loading="lazy". - Бюджет: первый JS-чанк < 200 КБ gzip, LCP < 1.5 с в локальной сети.
Доступность
- Вся навигация с клавиатуры, видимый фокус, ARIA-роли у плеера и поиска, контраст AA.
Доступность
Цель — WCAG 2.1 AA. Проверено axe-core на всех экранах в тёмной и светлой темах (с играющим треком и открытой очередью) — нарушений нет. Правила, которые держим:
- текст на заливке акцента — только
--accent-fill(белый текст ≥ 4.5:1), вторичный текст--text3— ≥ 4.5:1 на фоне и поверхностях; - у каждого экрана есть
h1; у кнопок-иконок —aria-label, у переключателей —aria-pressed; - списки треков —
role="list"/listitem(строка фокусируется, Enter запускает трек, лайк и ссылки — отдельные элементы); прогресс-бары подписаны черезaria-labelledby; - выезжающие панели (очередь) забирают фокус при открытии, закрываются по Escape и возвращают фокус на кнопку;
:focus-visible— обводка акцентом;prefers-reduced-motion— анимации и Ambient выключены, волна замедлена.