From f4453996ac61e635edf45fc704c18426bbcb9047 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Fri, 23 Oct 2026 01:24:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(03-04):=20on-screen=20diagnostic=20probe=20?= =?UTF-8?q?=E2=80=94=20WA=20version/platform,=20initData=20char-count,=20s?= =?UTF-8?q?ession=20+=20location=20categories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/main.ts | 8 ++++++++ web/src/shell.css | 5 +++++ web/src/ui/probe.ts | 47 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 web/src/ui/probe.ts diff --git a/web/src/main.ts b/web/src/main.ts index bf46e8e..1bfa6e3 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -28,6 +28,7 @@ import { createPositionMarker } from "./map/marker.ts"; import { createFollowController } from "./map/follow.ts"; import { createStatusStrip } from "./ui/strip.ts"; import { buildBootCard, buildDeniedCard, buildSignalLostCard, startGantry, stopGantry } from "./ui/cards.ts"; +import { createProbe } from "./ui/probe.ts"; import { createRecenterControl } from "./ui/recenter.ts"; import { getWebApp } from "./telegram/webapp.ts"; import { watchTheme } from "./telegram/theme.ts"; @@ -97,6 +98,11 @@ async function boot(): Promise { if (lastFix !== null && map !== null) follow?.follow(lastFix); }); + // DEBUG-ROUND probe: category-only on-screen telemetry for the device + // matrix (webview has no dev-tools); removed at the exit. + const probe = createProbe(mainEl); + probe.set({ wa: wa ? `${wa.version ?? "?"}/${wa.platform ?? "?"}` : "none", initDataChars: wa ? wa.initData.length : null }); + // Cards are built once at boot (boot-time locale) and reused across re-runs. const bootCard1 = buildBootCard(locale, 1); const bootCard2 = buildBootCard(locale, 2); @@ -182,6 +188,7 @@ async function boot(): Promise { } function renderState(state: LocationState): void { + probe.set({ location: state.kind }); const ageSec = lastFix !== null ? fixAgeSeconds(lastFix, Date.now()) : null; const accuracyM = lastFix !== null ? lastFix.accuracyM : null; strip.render(state, ageSec, accuracyM); @@ -233,6 +240,7 @@ async function boot(): Promise { async function runFromSession(): Promise { renderState({ kind: "validating" }); const session = await createSession(wa?.initData ?? ""); + probe.set({ session: session.ok ? "ok" : session.reason, initDataChars: wa ? wa.initData.length : null }); if (!session.ok) { staleHandled = false; // session-first failure is boot-level, not a stale-fix recovery renderDenied(); diff --git a/web/src/shell.css b/web/src/shell.css index e3bf24b..e74f66c 100644 --- a/web/src/shell.css +++ b/web/src/shell.css @@ -91,3 +91,8 @@ main { position: fixed; inset: 0; } .pos-group[data-stale="true"] { opacity: 0.4; } /* No CSS transition or animation anywhere — state changes are instant hard swaps. */ + +/* DEBUG-ROUND probe — on-screen category-only telemetry for the device-matrix + loop (no dev-tools in a phone webview). Removed at the exit. Layout-only + positioning; the label typography is the existing label-sm token. */ +.diag-probe { position: absolute; left: var(--spacing-margin); bottom: calc(var(--safe-bottom, 0px) + var(--spacing-space-sm)); font: var(--type-label-sm); letter-spacing: var(--type-label-sm-tracking); color: var(--color-soft-beige); z-index: 10; pointer-events: none; } diff --git a/web/src/ui/probe.ts b/web/src/ui/probe.ts new file mode 100644 index 0000000..2ce5ca3 --- /dev/null +++ b/web/src/ui/probe.ts @@ -0,0 +1,47 @@ +/* + * probe.ts — DEBUG-ROUND diagnostic line (phase 3 device-matrix troubleshooting). + * ---------------------------------------------------------------------------- + * One category-only instrument line, fixed bottom-left: which WebApp the app + * sees (version/platform), the character-count of the raw initData (a COUNT, + * never its content), the last session-gate category, and the location-chain + * category. No coordinates, no payload, no ids — same law as the Worker's + * category-only logs (D-14), only surfaced on-screen because a phone + * webview has no dev-tools access during the device-matrix pass. + * + * Rendering uses only machine tokens (letters, digits, separators) — no + * human copy, so no strings-module entry and no COPY5 concern. The probe is + * expected to be REMOVED or reduced at the device-matrix exit. + */ +export interface ProbeParts { + wa: string; + initDataChars: number | null; + session: string; + location: string; +} + +export interface Probe { + el: HTMLElement; + set(parts: Partial): void; +} + +export function createProbe(parent: HTMLElement): Probe { + const el = document.createElement("div"); + el.className = "diag-probe"; + el.setAttribute("aria-hidden", "true"); + const parts: ProbeParts = { wa: "-", initDataChars: null, session: "-", location: "-" }; + const state = { parts }; + function render(): void { + const p = state.parts; + const q = p.initDataChars === null ? "-" : `${p.initDataChars}ch`; + el.textContent = `WA ${p.wa} · q ${q} · s ${p.session} · loc ${p.location}`; + } + parent.appendChild(el); + render(); + return { + el, + set(delta: Partial): void { + Object.assign(state.parts, delta); + render(); + }, + }; +}