From 9de1a05903e894039c3b091fa3a4f190fb979b36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:26:51 +0000 Subject: [PATCH 1/3] Initial plan From 1b9683752fc911e803fcceda9c8708316d3363c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:31:40 +0000 Subject: [PATCH 2/3] Fit map to route bounds when opening a route in the planner Agent-Logs-Url: https://github.com/trails-cool/trails/sessions/558ccff8-ed5d-432a-a5d3-aca49c0e531e Co-authored-by: stigi <13815+stigi@users.noreply.github.com> --- apps/planner/app/components/PlannerMap.tsx | 22 ++++++++++++++++++++ e2e/planner.test.ts | 24 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx index 1597cf5..3c81524 100644 --- a/apps/planner/app/components/PlannerMap.tsx +++ b/apps/planner/app/components/PlannerMap.tsx @@ -54,6 +54,27 @@ function MapExposer() { return null; } +function RouteFitter({ coordinates }: { coordinates: [number, number, number][] | null }) { + const map = useMap(); + const hasFitted = useRef(false); + + useEffect(() => { + if (hasFitted.current || !coordinates || coordinates.length < 2) return; + + // Coordinates are in [lon, lat, elevation] GeoJSON format + const bounds = L.latLngBounds( + coordinates.filter((c) => c.length >= 2).map((c) => [c[1]!, c[0]!] as [number, number]), + ); + + if (bounds.isValid()) { + map.fitBounds(bounds, { padding: [50, 50], maxZoom: 16 }); + hasFitted.current = true; + } + }, [coordinates, map]); + + return null; +} + function MapClickHandler({ onAdd, suppressRef }: { onAdd: (lat: number, lng: number) => void; suppressRef: React.RefObject }) { useMapEvents({ click(e) { @@ -314,6 +335,7 @@ export function PlannerMap({ yjs, onRouteRequest, highlightPosition }: PlannerMa + {} : addWaypoint} suppressRef={suppressMapClickRef} /> diff --git a/e2e/planner.test.ts b/e2e/planner.test.ts index 2ace23e..759c9ec 100644 --- a/e2e/planner.test.ts +++ b/e2e/planner.test.ts @@ -151,6 +151,30 @@ test.describe("Planner", () => { await expect(page.getByTitle("Draw no-go area")).toBeVisible(); }); + test("map zooms to fit the route when opened with initial waypoints", async ({ page, request }) => { + const sessionResp = await request.post("/api/sessions", { data: {} }); + const { url } = await sessionResp.json(); + + const waypoints = [ + { lat: 52.520, lon: 13.405 }, + { lat: 52.515, lon: 13.351 }, + ]; + await page.goto(`${url}?waypoints=${encodeURIComponent(JSON.stringify(waypoints))}`); + + await expect(page.locator(".leaflet-container")).toBeVisible({ timeout: 10000 }); + await expect(page.getByText("Connected")).toBeVisible({ timeout: 15000 }); + // Wait for route to compute and fit + await expect(page.getByText(/\d+\.\d+ km/)).toBeVisible({ timeout: 20000 }); + + // The map should have zoomed in to the route bounds (zoom > default 6) + const zoom = await page.evaluate(() => { + const map = (window as any).__leafletMap; + return map ? map.getZoom() : null; + }); + expect(zoom).not.toBeNull(); + expect(zoom).toBeGreaterThan(6); + }); + test("can create session with initial waypoints", async ({ request }) => { const response = await request.post("/api/sessions", { data: { From 99a4d7af97792caea43704652fb7b90d2bed3ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 29 Mar 2026 15:20:06 +0200 Subject: [PATCH 3/3] Fix fitBounds running before elevation chart resizes map invalidateSize() + requestAnimationFrame ensures Leaflet knows the map's actual dimensions after the elevation chart renders. Also removes unnecessary filter on already-typed coordinates. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/planner/app/components/PlannerMap.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx index 3c81524..d8d3499 100644 --- a/apps/planner/app/components/PlannerMap.tsx +++ b/apps/planner/app/components/PlannerMap.tsx @@ -63,13 +63,18 @@ function RouteFitter({ coordinates }: { coordinates: [number, number, number][] // Coordinates are in [lon, lat, elevation] GeoJSON format const bounds = L.latLngBounds( - coordinates.filter((c) => c.length >= 2).map((c) => [c[1]!, c[0]!] as [number, number]), + coordinates.map((c) => [c[1]!, c[0]!] as [number, number]), ); - if (bounds.isValid()) { + if (!bounds.isValid()) return; + + // Delay fitBounds so the layout has settled (elevation chart may resize the map) + const raf = requestAnimationFrame(() => { + map.invalidateSize(); map.fitBounds(bounds, { padding: [50, 50], maxZoom: 16 }); hasFitted.current = true; - } + }); + return () => cancelAnimationFrame(raf); }, [coordinates, map]); return null;