**Phase 138 final verification pass — all green, no defects found.** - Verified: blockquote pass in `frontend/assets/markdown.js` (step 2b, between escape+inline and paragraphs), guard extended to `|blockquote`, docstring updated; `.md-quote` CSS (left border + padding + `--ink-soft`, no fill, palette vars only, contrast comments, user-bubble override); unit pins + node behavior; dedicated E2E (chat / viewer / restore); mock `QUOTE_TRIGGER`/`QUOTE_ANSWER`; fixture `tests/fixtures/quote_docs/`. Thinking block confirmed on the same shared renderer (app.js L2025/L3095). - `uv run pytest tests/e2e/test_markdown_blockquotes.py -v --no-cov` → 3 passed (isolation) - `uv run pytest --cov=app --cov-report=term-missing` → 3268 passed, 9 skipped; TOTAL **99%** (>90%) - `uv run ruff check . && uv run pyright` → All checks passed; 0 errors - Criteria: quotes render in chat/viewer/restore ✅ (E2E) + thinking ✅ (shared renderer + unscoped AA CSS); `>>` collapses, fence/table `>` untouched ✅ (unit+e2e); non-quote byte-identical ✅ (exact fixture; phase-44/renderer suites pass **unedited**, confirmed by empty `git diff --stat`) - No deviations; commit + phase-dir move left to the harness per pipeline rules (working tree clean of stray changes — only phase-138 diffs present) - Next pending phase: **139_read_range_tool_lines**
2.4 KiB
2.4 KiB
Task 01 — The blockquote pass in the shared renderer
Phase: 138_markdown_blockquotes · Source: TODO.md:3 — "Markdown formatted ">" quotes aren't rendering"
Story: n/a
Objective
renderMarkdown turns runs of >-prefixed lines into <blockquote class="md-quote"> elements — after the escape + inline step, before the paragraph pass — with fences and tables keeping their precedence and non-quote input byte-identical.
Work
frontend/assets/markdown.js— new pass between the step-2text = inline(escapeHtml(text))and the step-3 paragraph.map(...): split the text on\n; group the maximal run of consecutive lines matching^\s*>+ ?(the escaped quote marker); strip the>run + one optional space from each member (nested>>/>>>collapses to one level — ASSUMPTION A1); join members with<br>; emit<blockquote class="md-quote">…</blockquote>. A blank line terminates the group.frontend/assets/markdown.js— extend the step-3 block-element guard from/^<(h\d|ul|ol|pre|li)/to/^<(h\d|ul|ol|pre|li|blockquote)/so the paragraph pass leaves the block alone (no<p>wrap).frontend/assets/markdown.js— update the module docstring: pass order is now fences → tables → escape+inline → blockquotes → paragraphs → restore; document the group rule (blank line terminates; nested collapses) and the documented limit (a list/heading line INSIDE a quote renders as plain text — the inline chain ran first; inline code/strong/em work).
- ASSUMPTION: a
>inside a code fence is already protected by the step-1 placeholder (no handling needed); a>in a table cell is escaped inside the<td>markup and restored in step 4 — both are pinned in task 03's behavior checks.
Testing & Quality
- Unit: covered by task 03's file (the source pin + the node behavior listed there) — the frontend house pattern is one unit file per feature.
- Coverage: n/a (frontend) — the suite + the pins are the gate.
Completion Criteria
- node repro:
renderMarkdown("> a\n> b")→<blockquote class="md-quote">a<br>b</blockquote>;renderMarkdown("> quoted")no longer emits a literal> - a non-quote input (plain paragraph, list, table, fence) renders byte-identical to the pre-task renderer
uv run pytestgreen (the renderer-dependent unit suites unedited);uv run ruff check .clean