---
path: /nisaba
surface: customer
domain: nisaba
auth: signed-in
source: apps/oshun/web/src/app/nisaba/page.tsx
status: walked
last_walked:
  '2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data
  (2xx), console/page-errors, expected content, screenshot verified; live
  screen-reader, touch, offline, and telemetry-delivery checks pending a manual
  AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content
  re-verified 2026-06-03 against current source'
---

# Nisaba · the reading desk

## Purpose

The Nisaba domain hub. A reading desk for the long texts: passage with sections
rail, marginalia, cross-references, and links to the deeper Nisaba sub-surfaces
(scholar, lexicon, graph, notebook).

## Entry points

- **Home domain card grid** — see
  [`../02-home-discovery/home.md`](../02-home-discovery/home.md)
- **Domain switcher** — `/switcher` (see
  [`../02-home-discovery/switcher.md`](../02-home-discovery/switcher.md))
- **Domain back-stack from sibling Nisaba routes** — `LSubNav active="desk"`
  keeps the reading desk anchored across `/nisaba/*`
- **Library + Explore** — sky-and-scripture collection and curated routes link
  here
- **Direct URL / bookmark** — yes (auth required via shell middleware)
- **Server fetch** — `getNisaba()` runs on the server before render

## Layout regions

`page.tsx` is a thin server component: `await getNisaba()` →
`<NisabaRoom data={data} />`.

Inside `NisabaRoom` (`rooms.tsx`, line 1114), a Lilith room composition. The
desk passage is a Pali / Buddhist text on the five aggregates (anattā /
not-self) — the Greek (Marcus Aurelius) text belongs to the compare and scholar
views, not the desk:

- **Web shell top** — `LCustomerNav active="library"` plus an `LSubNav` with
  `desk | scholar | lexicon | graph | notebook` tabs (desk active)
- **Header / above the fold** — passage title (italic muted), citation eyebrow,
  language buttons (`data.passage.languages` — Pali / English / Both per
  `activeLanguage`)
- **Sections + Marginalia rail (left, 220 px)** — `data.sections.map` rows with
  section number and title (the `here` section uses ink color and accent
  number), then an `LRule` and a **Marginalia** block (`data.marginalia.map`
  label/count rows)
- **Main reading column (centre)** — passage body (`data.passageBody.map`) with
  a drop-cap first paragraph and an inline "Margin · me" five-aggregates note;
  below it an `LRule` and the prev/next section nav + passage actions (see
  Interactions)
- **Cross-references / concept-graph rail (right, 280 px)** — `Cross-references`
  eyebrow over an `LCard` with the concept-graph SVG (`data.graphNodes`) and a
  `Read alongside` list of related passages (`data.related`)

## Automation anchors

This file covers the compact `/nisaba` reading desk. The search-result
destination is the live domain reader at `/domains/nisaba`, covered by
[`../../journeys/nisaba-scholarly-read.md`](../../journeys/nisaba-scholarly-read.md)
through `data-nisaba-surface`, `data-nisaba-workspace`, and
`data-nisaba-reading-*` anchors.

- Root: `data-nisaba-desk-page` with title/citation/active-language and
  language/section/marginalia/paragraph/graph-node/related counts
- Header: `data-nisaba-desk-header`, `data-nisaba-desk-eyebrow`,
  `data-nisaba-desk-heading`, `data-nisaba-desk-citation-text`, and per
  `data-nisaba-desk-language={language}` button with `aria-pressed` and
  active-state attributes
- Sections + marginalia: `data-nisaba-desk-section-rail` with per
  `data-nisaba-desk-section={number}` row, section title/current attributes, and
  `data-nisaba-desk-marginalia` with per-row label/value cells
- Reading: `data-nisaba-desk-reading`, per `data-nisaba-desk-paragraph={index}`
  paragraph, inline margin note `data-nisaba-desk-inline-margin-note`, and
  footer action wrappers
- Cross-reference rail: `data-nisaba-desk-crossrefs`, `data-nisaba-desk-graph`,
  per graph-node label/color attributes, and `data-nisaba-desk-related` with
  per-row code/title cells

## States

- [x] **Loaded** — `getNisaba()` resolves before render; no client loading
      branch in this view
- [x] **Active language: Pali / English / Both** — `data.passage.activeLanguage`
      seeds the selected language button; clicking a language swaps the reading
      body through `passageBodies`
- [x] **Active section** — exactly one `section.here === true`
- [ ] **Cross-domain bridges** — verify any links to Tara / Veritas / Metis that
      the room data surfaces
- [ ] **Reduced motion** — composes static Lilith primitives; no animations
      observed
- [ ] **Standalone PWA / Offline** — depends on shell + server cache; no
      Nisaba-specific branch

## Interactions

### Top sub-nav

- [x] **The desk** (tab) — active; route `/nisaba`
- [x] **Scholar mode** (tab) — routes to `/nisaba/scholar` (verify wiring in
      `LSubNav`)
- [x] **Lexicon** (tab) — routes to `/nisaba/lexicon`
- [x] **Concept graph** (tab) — routes to `/nisaba/graph`
- [x] **Notebook** (tab) — routes to `/nisaba/notebook`

### Header language buttons

- [x] **Language button** — `Pali`, `English`, and `Both, side-by-side` are real
      buttons when the live desk provides all three bodies; clicking updates the
      active-language attribute and reading body. The honest unavailable
      fallback remains English-only.

### Sections rail

- [x] **Section row** — navigates to that section anchor; current-section row
      styled with accent number + ink title

### Reading column

- [ ] **Footnote / cross-reference link** — opens the corresponding lexicon /
      graph / scholar entry
- [ ] **Highlighted token** — opens lexicon for that word (verify)

### Passage footer (below the reading column rule)

- [ ] **‹ I. The setting** (`LBtn kind="ghost" href="/nisaba#section-I"`) —
      jumps to the previous section anchor
- [ ] **III. On feeling ›** (`LBtn kind="primary" href="/nisaba#section-III"`) —
      jumps to the next section anchor
- [ ] **`NisabaPassageActions`** (`rooms.tsx` ~1311-1315 →
      `components/lilith/NisabaPassageActions.tsx`) — a `'use client'` row:
  - **Save passage / ✓ Saved** — toggles the passage in the local web library
    (`toggleOshunWebLibraryItem`, `domain: 'nisaba'`)
  - **Read aloud / Pause / Resume** — speaks the passage via
    `window.speechSynthesis`; degrades to a "Read-aloud not supported" eyebrow
    when the API is absent
  - **`TrustSafetyFlagButton`** — flags the passage (`targetKind="content"`)

## Data & contracts

- **Reads**: `getNisaba()` from `@/lib/lilith-data/nisaba` — fetches the live
  BFF route `GET /v1/nisaba/room` via `bffGet` (`nisaba.ts` ~14-19) and returns
  `NisabaData` (passage, sections, languages, activeLanguage, marginalia,
  graphNodes, related). On an unreachable feed it returns an honest
  `nisabaUnavailable()` room (no embedded fixture).
- **Writes**: _None from the server view; `NisabaPassageActions` (client) writes
  to the local web library and to the trust/safety flag flow._
- **Realtime**: _None._
- **Caching**: server fetch; SW caches the rendered HTML for offline reload
- **Auth/role check**: shell middleware
- **Route metadata**: title "Nisaba · the reading desk", canonical `/nisaba`

## Cross-references

- Shell: [`shell/01-app-shell.md`](../../shell/01-app-shell.md)
- Sibling Nisaba routes:
  - [`nisaba-compare.md`](./nisaba-compare.md) — text, three ways
  - [`nisaba-daily.md`](./nisaba-daily.md) — daily passage (phone shell)
  - [`nisaba-graph.md`](./nisaba-graph.md) — concept graph
  - [`nisaba-lexicon.md`](./nisaba-lexicon.md) — lexicon
  - [`nisaba-manuscript.md`](./nisaba-manuscript.md) — Vatic. gr. 1950, f. 24v
  - [`nisaba-notebook.md`](./nisaba-notebook.md) — long-form notebook
  - [`nisaba-plan.md`](./nisaba-plan.md) — seven-week study plan
  - [`nisaba-scholar.md`](./nisaba-scholar.md) — full apparatus
- Component sources:
  - `apps/oshun/web/src/components/lilith/rooms.tsx` (`NisabaRoom` line 1114)
  - `apps/oshun/web/src/components/lilith/NisabaPassageActions.tsx`
  - `apps/oshun/web/src/lib/lilith-data/nisaba.ts`
- Feature spec: [`V1/features.md#nisaba`](../../../V1/features.md#nisaba)

## Open questions / known gaps

- [ ] `NisabaData` is served live from `GET /v1/nisaba/room`; confirm the BFF
      contract stays in sync with the `NisabaData` shape
- [x] Confirm whether language and section controls are interactive in V1 —
      language buttons swap Pali / English / Both bodies on the live desk;
      section rows are links to `/nisaba#section-*`
- [ ] Document the cross-domain bridges Nisaba carries (Tara ritual moments,
      Veritas grounded evidence, Metis study)
- [x] Confirm shell sub-nav routing — `LSubNav` items carry explicit hrefs for
      desk, scholar, lexicon, graph, and notebook
