diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx
index f1986d6..b6759bf 100644
--- a/apps/planner/app/components/PlannerMap.tsx
+++ b/apps/planner/app/components/PlannerMap.tsx
@@ -3,25 +3,43 @@ import { MapContainer, TileLayer, LayersControl, Marker, CircleMarker, useMapEve
import L from "leaflet";
import * as Y from "yjs";
import { useTranslation } from "react-i18next";
+import type { DayStage } from "@trails-cool/gpx";
import type { YjsState } from "~/lib/use-yjs";
import { baseLayers } from "@trails-cool/map";
import { parseGpxAsync, extractWaypoints } from "@trails-cool/gpx";
+import { isOvernight } from "~/lib/overnight";
+import { setOvernight } from "~/lib/overnight";
import { NoGoAreaLayer } from "./NoGoAreaLayer";
import { ColoredRoute, findSegmentForPoint, type ColorMode } from "./ColoredRoute";
import { RouteInteraction } from "./RouteInteraction";
import "leaflet/dist/leaflet.css";
-function waypointIcon(index: number): L.DivIcon {
+function waypointIcon(index: number, overnight?: boolean): L.DivIcon {
+ const bg = overnight ? "#8B6D3A" : "#2563eb";
return L.divIcon({
className: "",
html: `
${index + 1}
`,
+ ">${overnight ? "☾" : index + 1}`,
+ iconSize: [0, 0],
+ });
+}
+
+function dayLabelIcon(dayNumber: number, distanceKm: string): L.DivIcon {
+ return L.divIcon({
+ className: "",
+ html: `Day ${dayNumber} · ${distanceKm} km
`,
iconSize: [0, 0],
});
}
@@ -30,6 +48,7 @@ interface WaypointData {
lat: number;
lon: number;
name?: string;
+ overnight: boolean;
}
function getWaypointsFromYjs(waypoints: Y.Array>): WaypointData[] {
@@ -37,6 +56,7 @@ function getWaypointsFromYjs(waypoints: Y.Array>): WaypointData[]
lat: yMap.get("lat") as number,
lon: yMap.get("lon") as number,
name: yMap.get("name") as string | undefined,
+ overnight: isOvernight(yMap),
}));
}
@@ -45,6 +65,7 @@ interface PlannerMapProps {
onRouteRequest?: (waypoints: WaypointData[]) => void;
onImportError?: (message: string) => void;
highlightPosition?: [number, number] | null;
+ days?: DayStage[];
}
function MapExposer() {
@@ -206,7 +227,7 @@ function NoGoAreaButton({ active, onClick }: { active: boolean; onClick: () => v
);
}
-export function PlannerMap({ yjs, onRouteRequest, highlightPosition, onImportError }: PlannerMapProps) {
+export function PlannerMap({ yjs, onRouteRequest, highlightPosition, onImportError, days }: PlannerMapProps) {
const { t } = useTranslation("planner");
const [waypoints, setWaypoints] = useState([]);
const [draggingOver, setDraggingOver] = useState(false);
@@ -442,7 +463,7 @@ export function PlannerMap({ yjs, onRouteRequest, highlightPosition, onImportErr
key={i}
position={[wp.lat, wp.lon]}
draggable
- icon={waypointIcon(i)}
+ icon={waypointIcon(i, wp.overnight)}
eventHandlers={{
mouseover: () => {
routeInteractionSuspendedRef.current = true;
@@ -465,12 +486,31 @@ export function PlannerMap({ yjs, onRouteRequest, highlightPosition, onImportErr
},
contextmenu: (e) => {
L.DomEvent.preventDefault(e as unknown as Event);
- deleteWaypoint(i);
+ // Middle waypoints: toggle overnight. First/last: delete.
+ if (i > 0 && i < waypoints.length - 1) {
+ setOvernight(yjs, i, !wp.overnight);
+ } else {
+ deleteWaypoint(i);
+ }
},
}}
/>
))}
+ {/* Day boundary labels on map */}
+ {days && days.length > 1 && days.map((day) => {
+ const wp = waypoints[day.endWaypointIndex];
+ if (!wp || day.dayNumber === days.length) return null;
+ return (
+
+ );
+ })}
+
{routeCoordinates && routeCoordinates.length >= 2 && (
<>
}
>
- addToast(msg, "error")} />
+ addToast(msg, "error")} days={days} />
diff --git a/openspec/changes/multi-day-routes/tasks.md b/openspec/changes/multi-day-routes/tasks.md
index 5a16b03..13830f8 100644
--- a/openspec/changes/multi-day-routes/tasks.md
+++ b/openspec/changes/multi-day-routes/tasks.md
@@ -13,9 +13,9 @@
## 3. Map Integration
-- [ ] 3.1 Add overnight marker variant to `PlannerMap`: amber-brown circle (`--stop` token) for overnight waypoints, replacing default olive marker
-- [ ] 3.2 Add day label DivIcon markers on route at day boundaries: white pill with "Day N . X km" text, positioned at overnight waypoint coordinates
-- [ ] 3.3 Add right-click context menu on waypoint markers with "Mark as overnight stop" / "Remove overnight stop" option
+- [x] 3.1 Add overnight marker variant to `PlannerMap`: amber-brown circle (`--stop` token) for overnight waypoints, replacing default olive marker
+- [x] 3.2 Add day label DivIcon markers on route at day boundaries: white pill with "Day N . X km" text, positioned at overnight waypoint coordinates
+- [x] 3.3 Add right-click context menu on waypoint markers with "Mark as overnight stop" / "Remove overnight stop" option
## 4. Elevation Chart