# Архитектура клиента (plugin.js) Этот документ — карта для разработчика, открывающего [plugin.js](../plugin.js) впервые. Серверная сторона описана в [README.md](../README.md); ## Почему монолит Весь клиент — один файл, который сервер отдаёт как `/music.js` (с подстановкой `{localhost}`, `{client_debug_enabled}`, `{stats_clear_enabled}` и `{daily_reset_enabled}` при отдаче). Это осознанное решение: файл монтируется в контейнер живьём, правка не требует пересборки, а Lampa загружает плагин одним запросом. Плата — размер (~17 тыс. строк), поэтому навигация построена на двух уровнях маркеров: - `// ===== ИМЯ =====` — 14 крупных секций (список в шапке файла); - `// --- имя ---` — подсекции внутри больших секций. Шапка файла содержит карту секций, точки входа, легенду именований и typedef основных DTO — начинать чтение оттуда. ## Поток данных ### Главный экран ``` /music/home ──> MUSIC_HOME_CACHE (снапшот секций) │ ├── локальные полки из Lampa.Storage: недавние │ поиски/альбомы/артисты, закладки, «Мои плейлисты» └── discovery-полки сервера (Apple Music, VK Top, SoundCloud) карточка = entry (mapTrackCard / mapAlbumCard / mapArtistCard / mapPlaylistCard) «Ещё» ──> /music/section?id=... (полный список, пагинация) ``` Recent-полки живут в `Lampa.Storage` и синхронизируются между экранами событием `MUSIC_RECENT_EVENT` — компоненты подписываются через `bindRecentListener` и обновляют только затронутую секцию, не перерисовывая home целиком. После рестарта сервера discovery-провайдеры могут не успеть в 2-секундный бюджет `/music/home` — сервер тогда ставит `browse_sections_warming: true`, а клиент тихо дозапрашивает home (3s → 5s → 8s) и **доклеивает** недостающие полки в конец без перерисовки и потери фокуса (`homeLoadToken` гейтит устаревшие таймеры). ### Запуск трека ``` тап по треку └─> warmupStandaloneIosGesture() — синхронно В ЖЕСТЕ (только player=ios) └─> /music/play?... — резолв источника на сервере └─> { track, selected_match, sources[] } └─> pickPlaybackSource(sources) — m4a-first для ios-пути └─> buildResolvedPlayback() — playback-объект ├─ player=ios → startStandaloneIosAudioPlayback (свой