diff --git a/DECISIONS.md b/DECISIONS.md index 5798e51..4870990 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1219,6 +1219,54 @@ stops scraping; the app is unaffected. No schema change. --- +## ADR-031 — SPA-like reader: inline-expand summary + Q&A in the list (HTMX, no framework) + +**Status:** Proposed (2026-06-12). Issue #16. **Draft for review — no code yet.** + +**Context / requirements.** The reader is multi-page: a list of compact cards (`/`), then a +navigation to a separate detail page (`/v/{id}`) for the full summary + the docked chat (ADR-027). +It feels less fluid than a single integrated view. We want the full summary AND the per-video +Q&A to open **in place in the list**, no page hop. Requirements: +- R1: clicking a summarized card expands it in place to the full summary (summary/highlights/ + takeaways) + the chat dock; a collapse returns it to the compact card. +- R2: **no SPA framework** — stay HTMX + Templ (ADR-003); reuse existing fragments, not a rewrite. +- R3: **progressive enhancement** — with JS off, the card link still navigates to `/v/{id}` + (the detail page stays as the no-JS + deep-link surface). Nothing becomes JS-only. +- R4: only **summarized** cards expand; pending/rate-limited/no-caption cards keep their current + footer behaviour (Summarize button, waiting/none states). +- R5: chat inside an expanded card works exactly as on the detail page (reuse `chatReveal`/ + `chatSection` + the existing `/v/{id}/chat` endpoints, unchanged). + +**Decision / architecture.** +1. **Reuse the existing fragments.** `summaryBody(r)` and `chatReveal(videoID)` already exist and + render the detail page; a new `expandedCard(r, chatEnabled)` composes the compact header + a + collapse control + `summaryBody` + `chatReveal`. `DetailPage` is refactored to also compose + `summaryBody` so the two never drift (DRY). +2. **Two fragment endpoints** (mirroring the existing list/status HTMX fragment pattern): + `GET /v/{videoId}/expand` → `expandedCard`; collapse reuses the existing compact `VideoCard` + via `GET /v/{videoId}/card`. Both are list-card `
  • ` fragments with the SAME `id` + (`video-{id}`), swapped `outerHTML` — same mechanism as `processingCard`/`VideoCard` today. +3. **The compact card's title/"Read" affordance** becomes `hx-get=/v/{id}/expand`, + `hx-target=#video-{id}`, `hx-swap=outerHTML`, with `href=/v/{id}` as the no-JS fallback (R3). + The expanded card's collapse control is the inverse (`hx-get=/v/{id}/card`). +4. **Only when `r.Summarized`** does the expand affordance render (R4); the other states are + unchanged. +5. **v1 does NOT push the URL** (`hx-push-url`) — expand/collapse is ephemeral list UI state; the + detail page remains the deep-link/shareable URL. Deep-linking the open state via `hx-push-url` + is noted as a later option (needs list-state restore on back). + +**Out of scope / later.** URL push / deep-linkable open state; the visual refresh (#17) — though +the expanded-card markup is where #17's TUI/charm styling will land, so they pair. + +**Reversibility.** Additive: two fragment endpoints + one templ + an affordance swap on the +compact card. Removing the affordance reverts to plain list→detail navigation; the detail page is +untouched. No schema change. + +**Next steps (gated):** on approval → BDD (`docs/use-cases/inline_expand.feature` + coverage +map) → TDD → implement → SemVer + docs. + +--- + ## Rejected alternatives Approaches considered during the 2026-06-02 planning + grill session and **deliberately not