diff --git a/.roo/memory-bank/activeContext.md b/.roo/memory-bank/activeContext.md
index 96ed86a..ea5d6ab 100644
--- a/.roo/memory-bank/activeContext.md
+++ b/.roo/memory-bank/activeContext.md
@@ -2,11 +2,20 @@
## Task State
-- task_id: TASK-LINK1
+- task_id: TASK-ROUTE-FIX
- status: success
- parent_task: —
-- summary: **FIX: секция «Ссылки на описание» в components/WorkoutItem.vue — кнопка «Добавить» видна на публичной странице (isPrivate=false), но не работает (модалка скрыта v-if="isPrivate"). Правило: нет ссылки + нет прав → секция полностью скрыта.**
-- next task: after Coder returns — review and close.
+- summary: **FIX + FEATURE: Route.vue — багфикс GPX-генератора (for...in, дубликаты, фейковый ele) + snap to road через GraphHopper Cloud API.**
+- next task: —
+
+## ✅ CODER SUCCESS REPORT — TASK-ROUTE-FIX
+
+- Изменены файлы:
+ - `src/pages/routes/Route.vue`: `toGPX()` (var→const, for...in→index loop, ele fix), `handleMapClick` (async, 5m filter + snapToRoad), template onClick
+ - `src/services/roadSnap.ts` (НОВЫЙ): `snapToRoad(lon, lat)` → GraphHopper Cloud API, fallback к исходным координатам
+- Поведение: клик рядом с дорогой → точка прилипает (snap); двойной клик < 5м → игнорируется; GraphHopper down → работает как раньше
+- `yarn lint`: exit 0
+- `yarn build`: exit 0 (vue-tsc --noEmit + vite build)
## TASK-LINK1 — TODO (delegate to Coder)
diff --git a/src/pages/routes/Route.vue b/src/pages/routes/Route.vue
index 7b533c2..6aa4867 100644
--- a/src/pages/routes/Route.vue
+++ b/src/pages/routes/Route.vue
@@ -16,8 +16,7 @@
(null);
const clickCoordinates = ref([]);
+const SNAP_THRESHOLD_M = 5;
+
+async function handleMapClick(coords: LngLat): Promise {
+ if (clickCoordinates.value.length > 0) {
+ const last = clickCoordinates.value[clickCoordinates.value.length - 1];
+ if (
+ calculateHaversineDistance(coords[0], coords[1], last[0], last[1]) *
+ 1000 <
+ SNAP_THRESHOLD_M
+ ) {
+ return;
+ }
+ }
+
+ const [snappedLon, snappedLat] = await snapToRoad(coords[0], coords[1]);
+ clickCoordinates.value = [
+ ...clickCoordinates.value,
+ [snappedLon, snappedLat],
+ ];
+}
+
const toGPX = () => {
let content = "";
- var date = new Date().toISOString();
- for (let i in clickCoordinates.value) {
- content += `${i}`;
+ const date = new Date().toISOString();
+ for (let i = 0; i < clickCoordinates.value.length; i++) {
+ content += `0`;
}
return `cycle-rider.rucycle-rider.ru${content}`;
};