Context. surface telegram-miniapp · domain discovery · route /[surface] · auth none · source apps/oshun/telegram-miniapp/src/app/[surface]/page.tsx
Last walked. —
Purpose#
The dynamic per-surface view renders one of seven curated Oshun surfaces inside
Telegram. The route param surface is validated against SURFACE_SLUGS; an
unknown slug falls back to 'today' (the Tara ritual). All page content comes
from the module-level surfaceContent map — the page itself does no fetch and
reads no session.
Source: apps/oshun/telegram-miniapp/src/app/[surface]/page.tsx (server
component) which renders the client component
apps/oshun/telegram-miniapp/src/app/[surface]/SurfaceWorkspace.tsx. The seven
slugs are statically generated via generateStaticParams().
Entry points#
- From
/(home) via asurfaceLinkanchor — href/${slug}/ - Direct URL —
/today/,/sophia/,/veritas/,/nyx/,/arete/,/nisaba/,/illustration/(all pre-rendered bygenerateStaticParams) - Any other
/[surface]/value renders the Tara ritual ('today') fallback rather than a 404 (isSurfaceSlugguard inpage.tsx)
Layout regions#
<main className="miniShell surfaceShell"> with --surface-accent set from
surface.accent:
- Header —
<a href="/" className="backLink">Back</a>(above the header block), then<header className="surfaceHeader">: eyebrow (surface.eyebrow), h1 (surface.title), deck paragraph (surface.deck), and a<div className="metaStrip">showing three spans:surface.domain,Telegram Mini App,Curated only. - Main —
<div className="surfaceGrid">containing<section className="workspacePanel" aria-label="${surface.title} workspace">, whose body is dispatched byrenderSurfacePayload(surface, setStatus)per-slug (see Interactions). - Aside / secondary panel —
<aside className="inspectorPanel" aria-label="Evidence and provenance">: theEvidenceInspector(Evidence tablist + citation drill-down), aprovenanceBlock(h2 "Provenance" + list ofsurface.provenance), and adisclosureparagraph (surface.disclosure). - Footer / sticky bottom —
<div className="actionBar" aria-label="Available actions">with three buttons (primary action, secondary action, View provenance), followed by<p className="statusLine" role="status">echoing the currentstatus(initial valueReady).
States#
- Loading — N/A: content is read synchronously from
surfaceContent; no async fetch, no loading state. - Empty — N/A: every slug maps to a fully populated
surfaceContententry; evidence/provenance arrays are non-empty in data. - Populated (short) — the only state: the per-slug payload, evidence tabs (2–3 items), and provenance list (3 items) all render from data.
- Populated (long) — N/A: per-surface content is fixed-size in
surface-data.ts; no list grows unbounded. - Error (recoverable) — N/A: no query to fail.
- Error (unrecoverable) — N/A: unknown slugs fall back to
'today'rather than erroring; no server data dependency to 500. - Offline — page renders from bundled data once cached; action buttons
only mutate local
statustext (no network), so they work offline; no SW configured (see open questions). - Gated — N/A: no auth/role/flag gate; reads no session or
initData. - Standalone PWA — N/A: hosted inside the Telegram WebApp container; no manifest/SW in the app (see open questions).
- Slug fallback — any non-
SURFACE_SLUGSparam renders the Tara ritual ('today') surface (verify via e.g./unknown/).
Interactions#
Shared chrome (present on every surface):
-
Back (link)
- Function: navigates to
/(home surface list). - Keyboard: native anchor; Tab-reachable; Enter activates.
- Screen reader: announces "Back, link".
- Touch target: per
.backLinkCSS; not asserted in code. - Mobile (≤ 640 px): no code-level delta.
- Disabled when: never.
- Offline behavior: standard navigation.
- Telemetry: None.
- Function: navigates to
-
Evidence tab (button,
role="tab", one persurface.evidenceitem)- Function: sets
activeEvidenceId; the citation drill-down below updates to that item'ssource,excerpt, andOpen citationlink. - Keyboard: button in a
role="tablist"; Tab/Enter/Space activate. - Screen reader: announces the evidence
label; selected tab carriesaria-selected. - Disabled when: never.
- Offline behavior: local state only; no network.
- Telemetry: None.
- Function: sets
-
Open citation (link, inside the active citation drill-down)
- Function: anchor to
activeEvidence.href(e.g./nisaba/passages/attention-context) — an Oshun-web deep path, not a Mini App route. - Disabled when: never.
- Telemetry: None.
- Function: anchor to
-
Primary action (button,
actionBar, label =surface.primaryAction)- Function: sets
statusto${surface.primaryAction} queued. No network write; only therole="status"line changes. - Telemetry: None.
- Function: sets
-
Secondary action (button,
actionBar, label =surface.secondaryAction)- Function: sets
statusto${surface.secondaryAction} ready. - Telemetry: None.
- Function: sets
-
View provenance (button,
actionBar)- Function: sets
statustoProvenance inspector open(the provenance list is already always visible in the aside; this only updates status text). - Telemetry: None.
- Function: sets
Per-surface workspace body (dispatched by renderSurfacePayload):
-
today— Tara ritual (TaraRitual)- Next breath cue (button): advances
cueIndexcyclically throughpayload.breathCues; sets statusBreath cue advanced. - Play / Pause narration (button): toggles
narrationPlaying; statusNarration playing/Narration paused. No audio element is mounted (state only). - Complete ritual (button): sets
complete = true; statustara.ritual.completed queued; reveals "Reflection capture is ready." - Renders the timer face (
payload.durationLabel,payload.templateId) and thepayload.stepsordered list.
- Next breath cue (button): advances
-
sophia— Sophia Q&A (SophiaQa)- Save to notebook (button): status
${payload.notebookTarget} saved. - Renders question, answer blockquote, and a fact row (
confidenceBand,evidencePackId).
- Save to notebook (button): status
-
veritas— Veritas claim (VeritasClaim)- Regret flag (button): sets
flagged = true; statusRegret flag queued for review; label flips toRegret flag queuedand class todangerButton. - Renders claim, confidence badge, summary, and split Sources / Counterclaims lists.
- Regret flag (button): sets
-
nyx— Nyx sky (NyxSky)- Layer button (button, one per
payload.sceneLayers,segmentedControl): setslayerIndex; status${label} layer selected; detail text below updates. - Add to calendar (button): status
${payload.calendarEntry} added. - Renders the decorative
skyScene(role="img") and event fact row (observationWindow,qualityBand).
- Layer button (button, one per
-
arete— Arete check-in (AreteCheckIn)- Tick habit / Habit ticked (button,
aria-pressed): toggleshabitDone; statusHabit tick saved/Habit tick cleared. - Mood button (button, one per
payload.moods,segmentedControl): setsmood; status${mood} mood selected. - Renders the humane
recoveryStateblock (payload.streak,payload.recoveryState).
- Tick habit / Habit ticked (button,
-
nisaba— Nisaba reader (NisabaReader)- Edition button (button, one per
payload.editions,segmentedControl): setseditionIndex; status${label} edition selected; passage + note update. - Lexicon term button (button, one per
payload.lexicon,segmentedControl): setstermIndex; status${term} lexicon entry selected; gloss line updates. - Annotation (textarea):
defaultValue=payload.annotationPrompt; uncontrolled, no onChange handler in code. - Save annotation (button): status
Annotation saved to notebook.
- Edition button (button, one per
-
illustration— Illustration card (IllustrationCard)- Request generation / Generation queued (button): sets
requested = true; statusGeneration request queued with lineage; label flips. - Renders decorative
illustrationPreview(role="img"), workflow class, request title, prompt,policyState, and thepayload.lineageordered list.
- Request generation / Generation queued (button): sets
Data & contracts#
- Reads:
surfaceContent[slug]from../surface-data(module constant) — one of sevenSurfaceContentunion members. No network read. - Writes: None over the network. Every button updates only local React
state (
status, and per-surface toggles); no BFF//apicall exists in this app. - Realtime: None.
- Caching: statically generated per slug via
generateStaticParams()(Next.js App Router); no SWR/ISR/SW-cache configured. - Auth/role check: None. No middleware; the page reads no session and no
Telegram
initData.
Cross-references#
- Sibling route:
home.md(/— the surface list landing) - Server page:
apps/oshun/telegram-miniapp/src/app/[surface]/page.tsx - Client workspace:
apps/oshun/telegram-miniapp/src/app/[surface]/SurfaceWorkspace.tsx - Surface data + per-slug payload types:
apps/oshun/telegram-miniapp/src/app/surface-data.ts - E2E coverage:
apps/oshun/telegram-miniapp/e2e/miniapp.spec.ts(per-surface tests for sophia, today, veritas, nyx, arete, plus the landing list) - Unused Telegram bridge helper:
apps/oshun/telegram-miniapp/src/lib/telegram-webapp.ts
Open questions / known gaps#
- All action buttons are status-text-only stubs at the app level — they
mutate local React state but issue no BFF write (no
tara.ritual.completedemit, no notebook save, no calendar add, no regret-flag submission, no generation request). The data layer the labels imply (notebook/calendar/ review/generation) is not reachable from this Mini App in code. Confirm whether real BFF wiring is planned and which contracts/routes back each action. - The Telegram WebApp runtime is NOT wired here either: no
telegram-web-app.js, nowindow.Telegram, and no use ofsrc/lib/telegram-webapp.ts(theme params,MainButton/BackButton,HapticFeedback,initDataheader). TheactionBarbuttons could be promoted to the TelegramMainButton, and theBacklink toBackButton, but neither is done in code. Confirm intended wiring. - The Nisaba Annotation textarea is uncontrolled (
defaultValue, noonChange), so edits are never read by Save annotation (which emits a fixed status string). Confirm whether annotation capture is intended. - No telemetry is emitted on any interaction; confirm event names if surface-open / action analytics are required.
- No PWA manifest/SW, so Offline / Standalone-PWA states cannot be produced here; confirm this is intentional for a Telegram-hosted Mini App.