11 KiB
DEV TASK: Тёмная тема карты (F-18)
Статус: Ready for dev
Проект: enduro-trails
Фаза: 5.1
BRD: нет (простая фича)
Цель
Карта MapLibre переключается между светлым и тёмным стилем синхронно с темой UI (auto/light/dark + SunCalc).
Архитектура
Сейчас style.json — это светлый стиль (фон #f0ede6, OSM raster desaturated). Нужно создать style-dark.json с тёмной палитрой и исправить логику в switchMapStyle(), которая сейчас инвертирована (считает style.json тёмным).
Подход: тёмный стиль = тот же OSM raster, но с brightness-max: 0.3, contrast: -0.2, тёмный background. Цвета треков и POI адаптированы для тёмного фона.
Стек / Зависимости
- MapLibre GL JS (уже подключён)
- Никаких новых зависимостей
Инфраструктура
| Параметр | Значение |
|---|---|
| Сервер | slin@82.22.50.71 (пароль: motoZ@yaz2010) |
| Рабочая директория | /home/slin/enduro-trails/prototype/static/ |
| Workspace | /home/node/.openclaw/workspace/tasks/enduro-trails/prototype/static/ |
| Деплой | SFTP + docker cp после рестарта |
| Контейнер | prototype-enduro-trails-1 |
| URL | https://openclaw.mva154.duckdns.org/enduro/ |
Файловая карта
| Действие | Файл | Ответственность |
|---|---|---|
| Создать | static/style-dark.json |
Тёмный стиль карты |
| Переименовать | static/style.json → оставить как есть |
Светлый стиль (уже есть) |
| Изменить | static/app.js (строки 86-105) |
Исправить логику switchMapStyle |
Задачи
Task 1: Создать style-dark.json
Файлы:
- Создать:
static/style-dark.json
Шаги:
- 1.1 Создать
style-dark.jsonна основеstyle.jsonсо следующими изменениями:
{
"name": "Enduro Trails Dark",
"layers": [
{
"id": "background",
"paint": { "background-color": "#1a1a2e" }
},
{
"id": "osm-base",
"paint": {
"raster-opacity": 1.0,
"raster-saturation": -0.6,
"raster-contrast": -0.1,
"raster-brightness-min": 0,
"raster-brightness-max": 0.35
}
},
{
"id": "trails-track",
"paint": {
"line-color": "...",
"line-opacity": 0.95
}
// Цвета треков: grade1/2 → #FFE066, grade3/4/5 → #FF6633, default → #FF6633
},
{
"id": "trails-path-bridleway",
"paint": {
"line-color": "#ff4444",
"line-opacity": 0.9
}
},
{
"id": "poi-circles",
"paint": {
"circle-stroke-color": "#333333"
// Остальные цвета POI — ярче на 10-15% для контраста на тёмном фоне
}
},
{
"id": "poi-labels",
"paint": {
"text-color": "#e0e0e0",
"text-halo-color": "#1a1a2e",
"text-halo-width": 2
}
}
]
}
Полная спецификация изменений относительно style.json:
| Слой | Свойство | Светлый | Тёмный |
|---|---|---|---|
| background | background-color | #f0ede6 |
#1a1a2e |
| osm-base | raster-saturation | -0.4 |
-0.6 |
| osm-base | raster-contrast | 0.25 |
-0.1 |
| osm-base | raster-brightness-max | 0.9 |
0.35 |
| trails-track | line-color grade1/2 | #FFD700 |
#FFE066 |
| trails-track | line-color grade3/4/5/default | #FF4400 |
#FF6633 |
| trails-path-bridleway | line-color | #cc0000 |
#ff4444 |
| poi-circles | circle-stroke-color | #ffffff |
#333333 |
| poi-labels | text-color | #333333 |
#e0e0e0 |
| poi-labels | text-halo-color | #ffffff |
#1a1a2e |
| poi-labels | text-halo-width | 1.5 |
2 |
Все остальные свойства (sources, glyphs, filters, zoom levels, line-width) — копировать из style.json без изменений.
- 1.2 Проверить валидность JSON
cat static/style-dark.json | python3 -m json.tool > /dev/null && echo "OK"
# Ожидаемый результат: OK
Критерий готовности: Файл style-dark.json существует, валидный JSON, содержит все слои из style.json с тёмной палитрой.
Task 2: Исправить switchMapStyle() в app.js
Файлы:
- Изменить:
static/app.js(строки 86-105)
Шаги:
- 2.1 Заменить функцию
switchMapStyle()(строки 86-105):
Было:
function switchMapStyle() {
const map = window._map;
if (!map) return;
const dark = isDarkTheme();
const basePath = window.location.pathname.replace(/\/[^/]*$/, '') || '';
const styleUrl = dark ? basePath + '/style.json' : basePath + '/style-light.json';
// Check if style-light.json exists - if not, keep current
fetch(styleUrl, { method: 'HEAD' }).then(r => {
if (r.ok) {
map.setStyle(styleUrl);
} else {
// No light style available, keep dark
if (!dark) {
console.log('Light map style not available, keeping dark');
}
}
}).catch(() => {
// Network error, don't switch
});
}
Стало:
function switchMapStyle() {
const map = window._map;
if (!map) return;
const dark = isDarkTheme();
const basePath = window.location.pathname.replace(/\/[^/]*$/, '') || '';
const styleUrl = dark ? basePath + '/style-dark.json' : basePath + '/style.json';
fetch(styleUrl, { method: 'HEAD' }).then(r => {
if (r.ok) {
map.setStyle(styleUrl);
} else {
console.log('Map style not available:', styleUrl);
}
}).catch(() => {
// Network error, don't switch
});
}
Ключевое изменение: dark → style-dark.json, light → style.json (было инвертировано).
- 2.2 Проверить что
switchMapStyle()вызывается вapplyTheme()(строка 26) — уже есть, не трогать.
Критерий готовности: При isDarkTheme() === true загружается style-dark.json, при false — style.json.
Task 3: Деплой и проверка
Шаги:
- 3.1 Загрузить файлы на сервер
# Через Node.js ssh2 (deploy_static.js) или напрямую:
scp static/style-dark.json slin@82.22.50.71:/home/slin/enduro-trails/prototype/static/
scp static/app.js slin@82.22.50.71:/home/slin/enduro-trails/prototype/static/
- 3.2 Docker cp в контейнер
ssh slin@82.22.50.71 << 'EOF'
docker cp /home/slin/enduro-trails/prototype/static/style-dark.json prototype-enduro-trails-1:/app/static/style-dark.json
docker cp /home/slin/enduro-trails/prototype/static/app.js prototype-enduro-trails-1:/app/static/app.js
EOF
⚠️ НЕ рестартовать контейнер! Просто cp — иначе придётся копировать ВСЕ файлы заново.
- 3.3 Проверить доступность стилей
curl -s -o /dev/null -w "%{http_code}" https://openclaw.mva154.duckdns.org/enduro/style.json
# Ожидаемый результат: 200
curl -s -o /dev/null -w "%{http_code}" https://openclaw.mva154.duckdns.org/enduro/style-dark.json
# Ожидаемый результат: 200
- 3.4 Проверить содержимое тёмного стиля
curl -s https://openclaw.mva154.duckdns.org/enduro/style-dark.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['name'], d['layers'][0]['paint'])"
# Ожидаемый результат: Enduro Trails Dark {'background-color': '#1a1a2e'}
Критерий готовности: Оба стиля доступны по URL, тёмный стиль содержит правильную палитру.
Проверка (Acceptance)
| # | Проверка | Действие | Ожидаемый результат |
|---|---|---|---|
| 1 | Светлая тема | Открыть сайт, выбрать ☀️ | Карта светлая (бежевый фон) |
| 2 | Тёмная тема | Переключить на 🌙 | Карта тёмная (тёмно-синий фон, приглушённый OSM) |
| 3 | Auto (день) | Режим auto днём | Карта светлая |
| 4 | Auto (ночь) | Режим auto ночью | Карта тёмная |
| 5 | Overlay сохраняется | Включить terrain → переключить тему | Terrain слой остаётся видимым |
| 6 | Маршрут сохраняется | Построить маршрут → переключить тему | Маршрут остаётся на карте |
| 7 | style-dark.json 200 | curl |
HTTP 200 |
Ограничения и контекст
- ⚠️
docker cpБЕЗ рестарта контейнера — рестарт перезапишет статику из образа - ⚠️ SSH через Node.js ssh2 модуль (бинарный ssh в контейнере OpenClaw не работает — glibc mismatch). Альтернатива:
deploy_static.js - ⚠️ После
map.setStyle()все кастомные слои (terrain, routes, markers) слетают —onMapStyleLoad()+rebuildMapOverlays()уже обрабатывают это через событиеstyle.load - 🚫 Не трогать
style.json— это рабочий светлый стиль - 🚫 Не менять логику SunCalc / applyAutoTheme — она работает корректно
Деплой-чеклист
style-dark.jsonсоздан и валиденapp.js— логикаswitchMapStyleисправлена- Файлы загружены на сервер (
/home/slin/enduro-trails/prototype/static/) docker cpвыполнен (без рестарта!)style-dark.jsonдоступен по URL (HTTP 200)- Переключение темы меняет стиль карты
- Overlays (terrain, routes) сохраняются при смене стиля
Создано: 2026-05-12 | Автор ТЗ: Стрим | Исполнитель: Dev-агент