55 lines
2.9 KiB
Markdown
55 lines
2.9 KiB
Markdown
# Story: Open Documents in a Modal (Almost-Fullscreen)
|
||
|
||
**Phase:** `26_document_modal_viewer.md` · **E2E:** `tests/e2e/test_document_viewer.py`
|
||
|
||
## Narrative
|
||
|
||
As **a user**, when I click a document source chip (in the chat) or a
|
||
document path link (in the Sources table), I want the document to open
|
||
**right where I am** — in an almost-fullscreen modal overlay — instead of
|
||
navigating away to a new page/tab. I want to read it, then close the modal
|
||
and keep going.
|
||
|
||
- **Given** a document cited in a chat answer (or listed in Sources)
|
||
- **When** I click its chip / path link
|
||
- **Then** the document opens in an almost-fullscreen modal on the same
|
||
page, rendered exactly as the `/document.html` viewer renders it
|
||
(markdown in a centered column, other formats in a mono `pre`), with a
|
||
close button, Escape-to-close, and backdrop-to-close; and the dedicated
|
||
`/document.html` page still works for direct links / no-JS fallback.
|
||
|
||
## Acceptance criteria
|
||
1. Clicking a source chip or a Sources-table path link opens the document
|
||
in a modal (`#doc-modal`, `.doc-modal-panel`, ~`96vw × 92vh`) on the
|
||
current page — **no new tab, no navigation**.
|
||
2. The modal renders the same content as `/document.html`: md/markdown via
|
||
the shared escape-first renderer (`.doc-md`), other formats in
|
||
`<pre class="doc-raw">`, source/format/path/indexed/chunks meta.
|
||
3. The modal closes on the close button, on `Escape`, and on backdrop
|
||
click; focus moves into the panel on open and returns on close.
|
||
4. The modal honours the dark theme, `prefers-reduced-motion`, no-CDN, and
|
||
the ≥4.5:1 contrast / focus-visible a11y rules.
|
||
5. The standalone `/document.html` page is unchanged (direct link, back
|
||
button, not-found state, XSS-safe rendering).
|
||
6. No new packages; vanilla HTML/CSS/JS; the modal reuses the existing
|
||
stateless `GET /api/documents/content` endpoint (no backend change).
|
||
7. Story E2E green in isolation, no regressions in the document-navigation
|
||
/ header / smoke suites, one `--no-gpg-sign` commit.
|
||
|
||
## Playwright Mapping Rule
|
||
**Test Scenario → `tests/e2e/test_document_viewer.py`** (mock LLM, seeded
|
||
KB — same harness as the phase-10 suite):
|
||
1. `test_source_chip_opens_modal` — chip → modal opens in-page (no popup),
|
||
title + `.doc-md` content present, page URL unchanged.
|
||
2. `test_sources_row_opens_modal` — Sources path link → modal, yaml in
|
||
`<pre.doc-raw>`, mono font.
|
||
3. `test_modal_closes_on_button_escape_and_backdrop` — close via button,
|
||
backdrop, and `Escape`.
|
||
4. `test_modal_focus_and_a11y` — `role="dialog"` + `aria-modal`, focus
|
||
inside the panel, close button has an `aria-label`.
|
||
5. `test_modal_xss_safe` — hostile md renders escaped, no dialog fires.
|
||
6. `test_standalone_page_still_works` — `/document.html` still renders,
|
||
not-found, dark theme, no-CDN, a11y frame, ≤736px md column.
|
||
7. `test_modal_theme_and_no_cdn` — dark surface; all assets same-origin /
|
||
`data:`.
|