# BRD: Enduro Trails — Поиск точек маршрута **Версия:** 1.1 **Дата:** 2026-05-05 **Автор:** Стрим 🌊 **Статус:** 🔄 В разработке --- ## 1. Контекст и проблема Сейчас добавить точку маршрута можно только тапом на карту. Если нужно попасть в конкретное место (деревня, перекрёсток, POI) — нужно сначала найти его на карте, потом тапнуть. Это неудобно, особенно на мобиле. Верхний search bar решал задачу навигации по карте, но не интегрирован с маршрутом — найденное место просто центрировало карту, не добавляя точку. Плюс bar занимал место и мешал карте. --- ## 2. Цель Убрать верхний search bar. Добавить поиск прямо в список точек маршрута — inline, для каждой точки отдельно. При первом открытии режима маршрута — онбординг с полями ввода старта и финиша. --- ## 3. Что меняется ### 3.1 Убрать верхний search bar - Удалить `#search-bar` и `#search-results` из HTML - Удалить все CSS стили (`#search-bar`, `#search-input`, `#search-results`, `.search-result-item`, `.sri-*`) - Удалить вызов `initSearch()` из JS - Кнопку темы (`#btn-theme`) перенести в `#map-controls-r` (рядом с компасом и геолокацией) ### 3.2 Онбординг при активации режима маршрута Когда пользователь тапает «Маршрут» в toolbar и точек ещё нет — показывать вместо пустого списка: ``` ┌─────────────────────────────────┐ │ 🔍 Откуда? │ ← поле поиска старта │ или тапни на карте │ ← подсказка └─────────────────────────────────┘ ``` После выбора старта автоматически появляется: ``` ┌─────────────────────────────────┐ │ S Название старта │ ← добавленная точка ├─────────────────────────────────┤ │ 🔍 Куда? │ ← поле поиска финиша │ или тапни на карте │ └─────────────────────────────────┘ ``` После выбора финиша — поля исчезают, маршрут строится, показывается обычный `wl-list`. **Детали:** - Поля поиска используют тот же Nominatim что и inline поиск в wl-item - Подсказка «или тапни на карте» — мелкий текст `var(--text3)` под полем - Тап на карте тоже работает — добавляет точку и переходит к следующему полю - Если точки уже есть — онбординг не показывается, сразу wl-list ### 3.3 Inline поиск в каждой точке маршрута В каждом `wl-item` добавить кнопку-лупу. Тап → разворачивается inline панель поиска прямо под точкой. **UX flow:** 1. Пользователь видит список точек маршрута (S → 1 → F) 2. Тапает иконку 🔍 рядом с нужной точкой 3. Под точкой появляется поле ввода с автофокусом 4. Вводит название места → через 400ms debounce → Nominatim поиск 5. Выбирает результат → точка обновляется, карта летит к ней, маршрут перестраивается 6. Панель закрывается **Детали:** - Одновременно открыта только одна панель поиска (остальные закрываются) - Поиск через Nominatim (`countrycodes=ru`, `accept-language=ru`, limit=5) - Результат: название + подзаголовок (область/район) - После выбора: `routeWaypoints[idx]` обновляется, `rebuildWaypointMarkers()` + `renderWaypointsList()` + `debounceBuildRoute()` - `map.flyTo()` к выбранному месту (zoom 13, duration 600ms) --- ## 4. UI компоненты ### Кнопка лупы в wl-item - Иконка: Lucide `search` SVG (15×15) - Цвет: `var(--text3)` → hover `var(--accent)` - Позиция: между `.wl-info` и `.wl-drag-handle` ### Inline панель поиска - Фон: `var(--surface)`, бордер сверху `var(--border)` - Input: `var(--surface2)` фон, `var(--border)` бордер, focus → `var(--accent)` бордер - Результаты: max-height 180px, scroll, hover `var(--surface2)` - Каждый результат: название (bold) + подзаголовок (мелкий, `var(--text2)`) ### Кнопка темы (перенос) - Переносится в `#map-controls-r` как `.map-btn` - `updateThemeButtonIcon()` продолжает работать — находит `#btn-theme` по id --- ## 5. Технические детали | Аспект | Решение | |--------|---------| | Поиск API | Nominatim `/search` (уже используется в проекте) | | Debounce | 400ms | | Состояние | `wpSearchTimeout` — глобальная переменная для debounce | | Закрытие панели | Тап на лупу повторно — закрывает; открытие другой — закрывает текущую | | После выбора | `routeWaypoints[idx]` = `{lat, lon}`, rebuild + route | | Кнопка «Добавить точку» | `wl-add` остаётся без изменений | --- ## 6. Баги после редизайна (требуют исправления) | Баг | Описание | Вероятная причина | |-----|---------|------------------| | Метки в верхнем левом углу | Попап метки отображается не на позиции маркера, а в углу экрана | CSS `position` маркера сбивает позиционирование MapLibre | | Метки линейки в верхнем углу | Попап точки линейки отображается не на треке, а в углу | Та же причина — CSS позиционирование маркера | | Подсказка «Тапни на карте» пропала | `#route-status` не виден когда точек нет | Скрыт или перекрыт после редизайна | **Вероятная причина багов с маркерами:** в редизайне изменились глобальные CSS стили для `position`, `transform` или `z-index` — это сбивает позиционирование MapLibre маркеров. Маркер MapLibre должен иметь `position: absolute` без переопределения в глобальных стилях. **Фикс:** проверить CSS для `.maplibregl-marker`, `.marker-flag`, `.ruler-marker` — убрать любой `position: fixed/relative/static` который может переопределять поведение маркера. --- ## 7. Definition of Done | Критерий | Статус | |----------|--------| | Верхний search bar отсутствует | ⬜ | | Кнопка темы работает в map-controls-r | ⬜ | | При активации маршрута без точек — поле поиска старта | ⬜ | | После старта — поле поиска финиша | ⬜ | | Подсказка «или тапни на карте» видна | ⬜ | | Иконка лупы в каждом wl-item | ⬜ | | Тап на лупу → inline поиск открывается | ⬜ | | Поиск находит места через Nominatim | ⬜ | | Выбор результата → waypoint обновляется | ⬜ | | Маршрут перестраивается после выбора | ⬜ | | Карта летит к выбранному месту | ⬜ | | Одновременно открыта только одна панель | ⬜ | | Метки отображаются на позиции маркера (не в углу) | ⬜ | | Метки линейки отображаются на треке | ⬜ | | Деплой + health check OK | ⬜ | --- ## 8. Файлы | Файл | Изменения | |------|-----------| | `prototype/static/index.html` | Удалить `#search-bar`, `#search-results`; перенести `#btn-theme` в `#map-controls-r` | | `prototype/static/app.css` | Удалить стили search bar; добавить `.wl-search-*`; исправить позиционирование маркеров | | `prototype/static/app.js` | Удалить `initSearch()`; добавить онбординг, `openWaypointSearch()`, `doWaypointSearch()`, `selectWaypointSearchResult()` | --- --- ## 9. Дополнительные замечания (05.05.2026 — пакет 2) | # | Замечание | Детали | |---|-----------|--------| | 1 | Иконка колеса | Заменить на нормальное мотокросс колесо | | 2 | Флажок финиша | Чёрно-белая шахматная раскраска как финишный флаг | | 3 | Спиннер в основном листе | Вращающееся колесо пока строится маршрут (как в мини-баре) | | 4 | Тёмная карта | При тёмной теме — тёмный стиль карты, при светлой — светлый | | 5 | Автозум на маршрут | Плавный `fitBounds` после построения маршрута, маршрут по центру без выхода за границы | | 6 | Мини-бар перекрывает кнопки справа | Исправить z-index/позиционирование `#sheet-route-mini` | ### Детали по пунктам **П.1 Иконка колеса:** Заменить текущую SVG на нормальное мотокросс колесо — спицы, кноблинг, центральная втулка. Использовать Lucide `bike` или нарисовать SVG колеса с спицами. **П.2 Флажок финиша:** В `waypointPinSvg('F', ...)` для финишной точки использовать шахматный узор (SVG `pattern` чёрные/белые клетки) вместо красного цвета. **П.3 Спиннер в основном листе:** В `#route-cards` показывать skeleton/спиннер пока идёт запрос к OSRM. Тот же спиннер что в мини-баре. **П.4 Тёмная карта:** `map.setStyle()` при смене темы. Тёмный стиль: `/style-dark.json` (или текущий тёмный), светлый: `/style-light.json` (или текущий светлый). После `setStyle` пересоздать слои маршрутов через `map.on('style.load', onMapStyleLoad)`. **П.5 Автозум:** После `drawRouteResults()` вызывать: ```js const coords = routeResults[0].geometry.coordinates; const bounds = coords.reduce((b, c) => b.extend(c), new maplibregl.LngLatBounds(coords[0], coords[0])); map.fitBounds(bounds, { padding: 60, duration: 1000, maxZoom: 14 }); ``` **П.6 Мини-бар:** `#sheet-route-mini` перекрывает `#map-controls-r`. Исправить `right` или `width` мини-бара чтобы не залезал на кнопки справа.