Files
arah2/web/README.md
araharah-executor 91ad0c9d93 docs: generate + verify project documentation — canonical docs, per-package READMEs, verified fix pass
- New: docs/ARCHITECTURE.md, docs/GETTING-STARTED.md, docs/DEVELOPMENT.md,
  docs/TESTING.md, docs/CONFIGURATION.md (colored mermaid diagrams, quoted labels)
- Regenerated README.md (arah² / arah2bot naming, canonical clone URL)
- New per-package READMEs: theme/, bot/, web/
- Verified 13 docs against the live codebase (2 fix iterations + 2 residual
  corrections); corrected 56 stale claims across AGENTS/DESIGN/IDEAS/GSD_FIX
2026-10-23 20:56:40 +08:00

208 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- generated-by: gsd-doc-writer -->
# @araharah/web
The arah² Telegram Mini App — a MapLibre GL navigation shell built with Vite and TypeScript. It owns the three-source location acquisition chain, an in-memory-only session store, the Telegram WebApp integrations (theme slots, BackButton, safe-area insets), and the Rams chrome (status strip, boot/denied/signal-lost cards, recenter plate).
Part of the [arah²](../README.md) monorepo (workspaces: `theme`, `bot`, `web`). Private package — not published to a registry.
<!-- VERIFY: the deploy step mounts web/dist/ via the bot worker's ASSETS binding (see ../bot wrangler config) -->
---
## Install
Installed through the monorepo root (npm workspaces), not standalone:
```bash
# from the repository root
npm install
```
`@araharah/theme` is a workspace dependency (`"@araharah/theme": "*"`), so the theme package must be built before this one:
```bash
npm run build -w theme # emits theme/dist/rams.css (only)
npm run basemap -w theme # separately emits theme/basemap/liberty-rams.json
```
---
## Quick start
```bash
npm run web:dev # vite dev server on :5173 (strictPort)
```
With no Telegram client present, `src/mock/telegram-mock.ts` installs a fake `window.Telegram.WebApp` automatically in dev (`import.meta.env.DEV`), so the shell boots to `LIVE` in a plain browser with the KL default fix. Without it, the app renders its denied card — still no blank screen.
Production build (what the Worker serves):
```bash
npm run web:build # theme build + vite build -> web/dist
```
---
## Scripts
Run from the package directory, or from the root with `npm run <script> -w web`.
| Command | Description |
|---|---|
| `npm run dev` | Vite dev server on port 5173 (`strictPort: true`, `vite.config.ts`). |
| `npm run build` | Production build to `dist/` — the harness mock and fetch stubs are dead-code-eliminated. |
| `npm run build:harness` | `vite build --mode harness` to `dist-harness/` — `__ARAHARAH_HARNESS__` is `true`, so the mock installer and runner-page stubs are compiled in. Never deployed. |
| `npm test` | Zero-dependency runner (`node test/runner.mjs`) that auto-discovers `test/*.test.mjs`. |
| `npm run typecheck` | `tsc --noEmit` over `src` (strict, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). |
| `npm run capture` | Playwright harness (`scripts/capture.mjs`): serves `dist-harness/`, drives every boot state, writes `screenshots/`, and runs the runtime storage probe. |
`dist/` and `dist-harness/` must never share an outDir — the two builds would clobber each other and void the production-artifact gate.
---
## Module map
```
src/
├── main.ts boot choreography — order is load-bearing
├── shell.css the Rams chrome; imports @araharah/theme/dist/rams.css
├── vite-env.d.ts ambient __ARAHARAH_HARNESS__ declaration
├── telegram/
│ ├── webapp.ts the one typed accessor for window.Telegram.WebApp (TelegramWebAppLike)
│ ├── theme.ts Telegram theme signal -> [data-tg-theme] slot switch
│ ├── safe-area.ts env(safe-area-inset-*) ∪ WebApp.safeAreaInset -> CSS custom properties
│ └── backbutton.ts BackButton total wire (root press -> WebApp.close())
├── session/
│ ├── client.ts POST /api/session with the verbatim initData string
│ └── store.ts memory-only token holder, 900s TTL — no storage API anywhere
├── location/
│ ├── chain.ts acquireFix(): the locked telegram -> webview -> chat-pin chain
│ ├── telegram.ts LocationManager wrapper + Android stale-fix nudge
│ ├── webview.ts navigator.geolocation (maximumAge: 0)
│ ├── chat-pin.ts bounded poll of /api/location + ASK VIA CHAT trigger
│ └── types.ts Fix, FixSource, LocationState, KL_CENTER
├── map/
│ ├── map.ts MapLibre v6 bootstrap; returns null when WebGL2 is absent
│ ├── follow.ts following/free camera controller
│ └── marker.ts position marker + accuracy circle + heading cone
├── ui/
│ ├── strip.ts status strip (validating/acquiring/live/lost/denied/awaiting)
│ ├── cards.ts boot / denied / signal-lost cards + gantry blink
│ └── recenter.ts follow/recenter plate
├── i18n/strings.ts every user-facing string, en + ms (the copy gate)
└── mock/telegram-mock.ts Mock-Telegram harness — excluded from dist by BA5
```
```mermaid
graph TD
MAIN["src/main.ts — boot choreography"]
WA["src/telegram/webapp.ts"]
TH["src/telegram/theme.ts"]
SA["src/telegram/safe-area.ts"]
BB["src/telegram/backbutton.ts"]
SESS["src/session/client.ts"]
STORE["src/session/store.ts"]
CHAIN["src/location/chain.ts"]
TG["src/location/telegram.ts"]
WV["src/location/webview.ts"]
PIN["src/location/chat-pin.ts"]
TYPES["src/location/types.ts"]
MAP["src/map/map.ts"]
FOLLOW["src/map/follow.ts"]
MARK["src/map/marker.ts"]
STRIP["src/ui/strip.ts"]
CARDS["src/ui/cards.ts"]
REC["src/ui/recenter.ts"]
STR["src/i18n/strings.ts"]
MOCK["src/mock/telegram-mock.ts"]
MAIN --> WA
MAIN --> TH
MAIN --> SA
MAIN --> BB
MAIN --> SESS
MAIN --> CHAIN
MAIN --> MAP
MAIN --> STRIP
MAIN --> CARDS
MAIN --> REC
MAIN --> MOCK
SESS --> STORE
CHAIN --> TG
CHAIN --> WV
CHAIN --> PIN
CHAIN --> TYPES
TG --> TYPES
WV --> TYPES
PIN --> TYPES
MAP --> FOLLOW
MAP --> MARK
FOLLOW --> TYPES
MARK --> TYPES
STRIP --> TYPES
STRIP --> STR
CARDS --> STR
REC --> STR
```
---
## Usage examples
Acquire a fix through the chain (this is exactly what `main.ts` does):
```ts
import { acquireFix, isFixStale, fixAgeSeconds } from "./location/chain.ts";
import { createSession, } from "./session/client.ts";
import { getWebApp } from "./telegram/webapp.ts";
import { getSession } from "./session/store.ts";
const wa = getWebApp(); // null off-Telegram — the chain handles null
const session = await createSession(wa?.initData ?? "");
if (!session.ok) throw new Error(session.reason); // "malformed" | "tampered" | "stale" | "network"
const { fix, state } = await acquireFix({ wa, getToken: getSession }, null);
if (fix !== null && !isFixStale(fix, Date.now())) {
console.log(fix.source, fixAgeSeconds(fix, Date.now()), "s");
} else {
console.log(state.kind); // "denied" | "awaiting-pin" | ...
}
```
Boot the map on its own (returns `null` rather than throwing on a WebGL2-less device):
```ts
import { bootMap } from "./map/map.ts";
import { createPositionMarker } from "./map/marker.ts";
const map = bootMap(document.getElementById("map")!);
if (map !== null) {
const marker = createPositionMarker();
marker.marker.addTo(map);
marker.update(map, fix);
}
```
---
## Testing
```bash
npm run web:test # or: npm test (inside web/)
npm run web:typecheck
```
`test/runner.mjs` auto-discovers every `test/*.test.mjs` suite and drains the shared registry from `test/harness.mjs`. Suites cover the location chain (`location-chain.test.mjs`), the memory-only session store (`session.test.mjs`), theme slots (`theme.test.mjs`), the en/ms copy gates (`copy-gate.test.mjs`), the no-storage invariant (`no-storage.test.mjs`), and the production build artifact (`build-artifact.test.mjs` — CDN references, self-hosted fonts, hashed MapLibre worker, inlined Rams style, and BA5/BA6 asserting no mock installer or fetch stub ships in `dist/`).
The Playwright capture harness is a separate command (`npm run web:capture`): it serves `dist-harness/`, waits on the `RAMS-BASEMAP-READY` and `ARAHARAH-LIVE` console signals, and writes the state gallery into `screenshots/`.
---
## Related docs
- Root [README.md](../README.md) — monorepo overview and system diagram
- [docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md) — component responsibilities across `theme` / `bot` / `web`
- [docs/DEVELOPMENT.md](../docs/DEVELOPMENT.md) — local setup, build, and PR workflow
- [docs/TESTING.md](../docs/TESTING.md) — the cross-package test strategy
- [DESIGN.md](../DESIGN.md) and `theme/` — the Telemetry Rams design system this shell consumes