feat(06-06): the guidance pump — start gate, effects dispatch, cockpit chrome, silent reroute (GREEN)
- route.ts grows the pump beside the Phase 5 graph: the injected watch arms with the route draw; the first fix opens the 06-02 session; the start gate (>= GUIDANCE_START_ALONG_M along) activates the cockpit — PX-03 close protection, [data-guidance=active], banner + card + list, camera eased to the SNAPPED line, state=on_route token - effects dispatch: haptics via fireHaptic, reroute-requested through the injected RoutingSource (fail-honest catch; success = setData snap-in with NO camera jump + list re-render; failure = calm NO REROUTE + card state line); arrival -> the D-14 teardown (watch stop, protection release, chrome clear, trip-end card center) - END TRIP/BackButton -> the ONE confirm card; YES ABANDON = same teardown; CONTINUE dismisses with guidance alive; guidancePhase exposes none/driving/confirm/ended for the PX-01 resolver; walkBack refuses while the confirm owns the exit and vacates the route stack on the walk - guidanceView (guidance.ts): banner/card/list view data composed beside the machine's own maneuver marks — route/ imports and calls it, never re-derives (the RS6 re-pin's boundary; zero banned tokens in route/) - G1-G8 green; RS6/RS8 re-pins green; 137 passed, typecheck clean
This commit is contained in:
@@ -46,6 +46,7 @@ import type { RoutePoint, RouteStep } from "@araharah/routing/src/types.ts";
|
||||
import { haversineM } from "@araharah/routing/src/geo.ts";
|
||||
import { decodePolyline6 } from "@araharah/routing/src/polyline.ts";
|
||||
import type { Fix } from "../location/types.ts";
|
||||
import type { GlyphKind } from "./glyphs.ts";
|
||||
|
||||
/* ————— THE constants table (06-RESEARCH Pattern 3; replay-tuned, never inline) ————— */
|
||||
|
||||
@@ -436,3 +437,118 @@ function processEdges(next: GuidanceSnapshot, fix: Fix, effects: GuidanceEffect[
|
||||
next.approachArmed = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ————— the chrome's view data (06-06) ————— */
|
||||
/**
|
||||
* guidanceView composes EVERYTHING the cockpit chrome renders from a
|
||||
* snapshot — banner slots, card view fields, and the maneuver list rows —
|
||||
* so the route controller never re-derives maneuver/progress data (the
|
||||
* RS6 re-pin's law: guidance IMPLEMENTATION lives in web/src/guidance/;
|
||||
* route/ imports and calls, never re-computes). Pure: snapshot in, plain
|
||||
* data out — structurally the view types banner.ts's builders consume.
|
||||
*
|
||||
* The banner's distance slot is the model-derived distance TO THE NEXT
|
||||
* MANEUVER MARK (D-07: never a raw fix-to-destination distance) — null
|
||||
* (the honest dash) while REROUTING or in the NO REROUTE degradation; the
|
||||
* RESUMING register legally shows the projection numbers. The street slot
|
||||
* is the next maneuver's own step name (the road the maneuver puts you
|
||||
* on); the banner builder swaps in the calm-state copy for the wait
|
||||
* states regardless.
|
||||
*/
|
||||
export interface GuidanceListRow {
|
||||
kind: GlyphKind;
|
||||
street: string;
|
||||
distanceM: number;
|
||||
rowState: "next" | "future" | "past";
|
||||
}
|
||||
|
||||
export interface GuidanceBannerData {
|
||||
state: GuidanceState;
|
||||
glyph: GlyphKind;
|
||||
distanceM: number | null;
|
||||
street: string | null;
|
||||
listOpen: boolean;
|
||||
}
|
||||
|
||||
export interface GuidanceCardData {
|
||||
destinationName: string;
|
||||
progressPct: number;
|
||||
showNoReroute: boolean;
|
||||
}
|
||||
|
||||
export interface GuidanceViewData {
|
||||
banner: GuidanceBannerData;
|
||||
card: GuidanceCardData;
|
||||
rows: GuidanceListRow[];
|
||||
}
|
||||
|
||||
export function guidanceView(snap: GuidanceSnapshot, listOpen: boolean): GuidanceViewData {
|
||||
const { model } = snap;
|
||||
const marks = maneuverMarksOf(model);
|
||||
const next = marks[snap.nextManeuver] ?? null;
|
||||
|
||||
// The road a mark names: a TURN mark names the road the turn puts you on
|
||||
// (its owning step); the ARRIVE mark names the road being arrived on —
|
||||
// the last NAMED step of the route (an arrive step itself carries no
|
||||
// name; a blank slot would render the unnamed fallback and lie).
|
||||
const arrivalRoad = (() => {
|
||||
let named: string | null = null;
|
||||
for (const step of model.steps) {
|
||||
if (step.name !== undefined && step.name.length > 0) named = step.name;
|
||||
}
|
||||
return named;
|
||||
})();
|
||||
|
||||
// the next maneuver's owning step: the step whose turn-kind AND prefix
|
||||
// match the mark (a step runs FROM its maneuver TO the next boundary)
|
||||
let street: string | null = null;
|
||||
let along = 0;
|
||||
if (next !== null) {
|
||||
for (const step of model.steps) {
|
||||
if (step.kind === next.kind && along === next.alongM) {
|
||||
street = step.kind === "arrive" ? arrivalRoad : (step.name ?? null);
|
||||
break;
|
||||
}
|
||||
along += step.distanceM ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
const toManeuverM = next === null ? null : Math.max(0, Math.round(next.alongM - snap.alongM));
|
||||
const calm = snap.state === "rerouting" || snap.state === "no_reroute";
|
||||
const totalM = model.cumulative[model.cumulative.length - 1] ?? 0;
|
||||
const progressPct = totalM > 0 ? Math.min(100, Math.max(0, (snap.alongM / totalM) * 100)) : 0;
|
||||
|
||||
const rows: GuidanceListRow[] = marks.map((mark, i) => {
|
||||
let rowStreet = "";
|
||||
let cursor = 0;
|
||||
for (const step of model.steps) {
|
||||
if (step.kind === mark.kind && cursor === mark.alongM) {
|
||||
rowStreet = mark.kind === "arrive" ? (arrivalRoad ?? "") : (step.name ?? "");
|
||||
break;
|
||||
}
|
||||
cursor += step.distanceM ?? 0;
|
||||
}
|
||||
return {
|
||||
kind: mark.kind ?? "straight",
|
||||
street: rowStreet,
|
||||
distanceM: Math.max(0, Math.round(mark.alongM - snap.alongM)),
|
||||
rowState: i < snap.nextManeuver ? "past" : i === snap.nextManeuver ? "next" : "future",
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
banner: {
|
||||
state: snap.state,
|
||||
glyph: (next?.kind ?? "straight") as GlyphKind,
|
||||
distanceM: calm ? null : toManeuverM,
|
||||
street,
|
||||
listOpen,
|
||||
},
|
||||
card: {
|
||||
destinationName: model.destinationName,
|
||||
progressPct,
|
||||
showNoReroute: snap.state === "no_reroute",
|
||||
},
|
||||
rows,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user