feat(07-01): wire fullscreen + safe-area watch onto the guidance lifecycle edges

- route.ts: requestGuidanceFullscreen at BOTH guidance-start edges (activateGuidance + resume) directly after engageCloseProtection — synchronous, best-effort, return ignored
- route.ts: exitGuidanceFullscreen at BOTH teardown edges (teardownGuidance + stopGuidanceArtifacts) directly after releaseCloseProtection
- route.ts: watchFullscreenEvents registered exactly ONCE at the guidance-start edge; the callback is a deliberate no-op (observability only, D-14)
- main.ts: watchSafeAreaChanges(getWebApp()) called at the boot site directly after applySafeArea(resolveSafeInsets(getWebApp())) — insets re-resolve at runtime, not only once at boot (T-07-06b)
- no new logging; the route module's state-token console law unchanged
This commit is contained in:
araharah-executor
2026-10-25 03:46:35 +08:00
parent aec8b07530
commit 5b124e0b75
2 changed files with 17 additions and 1 deletions
+6 -1
View File
@@ -33,7 +33,7 @@ import { createRecenterControl } from "./ui/recenter.ts";
import { getWebApp } from "./telegram/webapp.ts";
import { watchTheme } from "./telegram/theme.ts";
import { wireBackButton } from "./telegram/backbutton.ts";
import { resolveSafeInsets, applySafeArea } from "./telegram/safe-area.ts";
import { resolveSafeInsets, applySafeArea, watchSafeAreaChanges } from "./telegram/safe-area.ts";
import { createSession } from "./session/client.ts";
import { getSession, isSessionStale } from "./session/store.ts";
import { acquireFix, isFixStale, fixAgeSeconds } from "./location/chain.ts";
@@ -72,6 +72,11 @@ void boot();
async function boot(): Promise<void> {
// 1 — safe-area vars are set before any layout reads them (PX-03 groundwork).
applySafeArea(resolveSafeInsets(getWebApp()));
// PX-04/D-03: the insets re-resolve at RUNTIME too — rotation and fullscreen
// transitions re-apply through the same union authority on the client's
// inset events. Same one-shot getWebApp() value as the apply line above;
// internally guarded (null WebApp / missing onEvent → returns immediately).
watchSafeAreaChanges(getWebApp());
// 3 (declared ahead of their first use inside the closures below) — the
// single-shell state the BackButton resolver and the stale branch read:
+11
View File
@@ -60,6 +60,7 @@ import { clearRouteLayers, setRouteDrawn } from "./route-layers.ts";
import type { buildRouteCard, buildRoutePendingCard } from "./route-card.ts";
import { createGuidanceCamera } from "../guidance/camera.ts";
import { engageCloseProtection, releaseCloseProtection } from "../guidance/close-protection.ts";
import { requestGuidanceFullscreen, exitGuidanceFullscreen, watchFullscreenEvents } from "../telegram/fullscreen.ts";
import { fireHaptic } from "../guidance/haptics.ts";
import {
buildConfirmCard,
@@ -339,6 +340,10 @@ export function createRouteController(deps: RouteControllerDeps): {
if (g === null || g.active) return;
g.active = true;
engageCloseProtection(deps.wa ?? null);
// PX-04/D-03: the native fullscreen request is BEST EFFORT — a refusal is a supported fallback, never a guidance failure.
requestGuidanceFullscreen(deps.wa ?? null);
// PX-04/D-03: the fullscreen events are observability only — no state, no logging (D-14 category-only law).
watchFullscreenEvents(deps.wa ?? null, () => { /* observability only — no state, no logging */ });
if (deps.shellRoot !== undefined) deps.shellRoot.dataset.guidance = "active";
foldEta(g);
camera.easeToSnapped(g.lastSnapped); // the locked-follow pose over the SNAPPED line
@@ -479,6 +484,8 @@ export function createRouteController(deps: RouteControllerDeps): {
stopWatch();
deleteEnvelope();
releaseCloseProtection(deps.wa ?? null);
// PX-04/D-03: the trip end releases fullscreen alongside the close protection — best effort, same law.
exitGuidanceFullscreen(deps.wa ?? null);
if (deps.shellRoot !== undefined) delete deps.shellRoot.dataset.guidance;
stopBlink();
if (deps.bannerHost !== undefined) deps.bannerHost.replaceChildren();
@@ -614,6 +621,8 @@ export function createRouteController(deps: RouteControllerDeps): {
};
envelopeId = trip.envelopeId;
engageCloseProtection(deps.wa ?? null);
// PX-04/D-03: a resumed trip is a live guidance session — the same best-effort fullscreen request as a fresh start.
requestGuidanceFullscreen(deps.wa ?? null);
if (deps.shellRoot !== undefined) deps.shellRoot.dataset.guidance = "active";
setRouteDrawn(deps.map, { points: trip.model.points }, []);
const first = points[0];
@@ -690,6 +699,8 @@ export function createRouteController(deps: RouteControllerDeps): {
stopWatch();
if (guidance === null) return;
releaseCloseProtection(deps.wa ?? null);
// PX-04/D-03: the walk-back teardown releases fullscreen alongside the close protection.
exitGuidanceFullscreen(deps.wa ?? null);
if (deps.shellRoot !== undefined) delete deps.shellRoot.dataset.guidance;
stopBlink();
if (deps.bannerHost !== undefined) deps.bannerHost.replaceChildren();