Files
arah2/web/src/telegram/theme.ts
T
araharah-executor 0a35bce256 feat(03-02): Telegram platform wrappers — theme slot switch, BackButton total wire, safe-area union
- theme.ts: colorScheme → data-tg-theme on <html>; header_bg_color 0-1 luminance fallback
  (T-03-11 — Telegram hex is a signal, never a consumed CSS value); applyTheme's ONLY
  statement is the attribute set (Pitfall 7 / D-10, greppable invariant)
- backbutton.ts: hide() at init (one shell state), onClick is total — close() at the root,
  no dead-ends (UI-SPEC §BackButton)
- safe-area.ts: env(safe-area-inset-*) probe ∪ WebApp.safeAreaInset, per-edge max,
  version-gated at 8.0, applied as --safe-* custom properties on :root
- webapp.ts + backbutton/safe-area never read node_modules types;
  @types/telegram-web-app stays dropped (web/package.json untouched)
2026-10-22 23:40:03 +08:00

47 lines
2.0 KiB
TypeScript
Raw 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.
/*
* theme.ts — PX-02: the Telegram theme signal → data-tg-theme slot switch.
* ---------------------------------------------------------------------
* Light/dark is a SLOT SWITCH, not a color import: Telegram's signals
* (`colorScheme`, or the luminance of `themeParams.header_bg_color` as
* the old-client fallback) select between two Rams token pairs defined
* in theme.css (`--shell-bg` / `--shell-on-surface`). Telegram's own hex
* values are read ONLY as a 0–1 luminance signal (T-03-11) — no
* Telegram color ever reaches a CSS custom property.
*
* Pitfall 7 (D-10): applyTheme's ONLY statement is the data-attribute
* set. MapLibre is deliberately absent — the map style is never re-set
* on themeChanged; CSS custom properties re-slot and the map stays
* Rams-constant. The invariant is greppable (theme.test.mjs asserts no
* setStyle call exists in this source).
*/
import type { TelegramWebAppLike } from "./webapp.ts";
type ThemeBranch = "light" | "dark";
export function resolveBranch(wa: TelegramWebAppLike): ThemeBranch {
// Primary signal — present on all clients this phase targets.
if (wa.colorScheme === "dark" || wa.colorScheme === "light") return wa.colorScheme;
// Fallback (UI-SPEC §Theme mapping): luminance of header_bg_color ≥ 0.5 → light.
const hex = wa.themeParams?.header_bg_color;
if (typeof hex === "string" && /^#[0-9a-f]{6}$/i.test(hex)) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
const lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
return lum >= 0.5 ? "light" : "dark";
}
return "light";
}
export function applyTheme(wa: TelegramWebAppLike): void {
// The ONLY statement a theme change is allowed to touch. MapLibre is
// deliberately absent from this function (D-10 / RESEARCH Pitfall 7).
document.documentElement.dataset.tgTheme = resolveBranch(wa);
}
export function watchTheme(wa: TelegramWebAppLike): void {
applyTheme(wa);
wa.onEvent?.("themeChanged", () => applyTheme(wa));
}