Implement CE-WP-0004 T01-T05: citation card export (Markdown + HTML)

Per-evidence-item export: click Export → Copy as Markdown / Copy as HTML
writes a portable citation card to the clipboard. Cmd/Ctrl+Shift+C
exports the active evidence as Markdown.

- ADR-0007 locks the Markdown + HTML output formats.
- New shared types: CitationCard, openContextUrl(), resolveSourceLabel().
- Engine renderers under src/engine/rendering/: renderCitationCardMarkdown,
  renderCitationCardHtml — snapshot-tested, escape-safe, BEM classes for HTML.
- src/work/useExportEvidence.ts wires engine + renderers + clipboard.
- EvidenceSidebar gains an Export popover per row + auto-dismissing toast.
- E2E test (tests/integration/citation-card-export-e2e.dom.test.tsx)
  walks PRD scenario steps 10-11 and asserts the clipboard payload.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 14:43:17 +02:00
parent 8607c252c4
commit 8632f7b04a
19 changed files with 1617 additions and 35 deletions

View File

@@ -0,0 +1,115 @@
# ADR-0007 — Citation card output format (Markdown and HTML)
- Status: accepted
- Date: 2026-05-25
- Workplan: CE-WP-0004-T02 (Markdown renderer) and CE-WP-0004-T03 (HTML renderer)
- Spec refs: `wiki/ArchitectureOverview.md` §4.7, §14.1, §14.2, §14.3
## Context
The MVP scenario ends with a user exporting an evidence item as a portable
citation card. Two formats ship in CE-WP-0004:
- **Markdown** — copied to the clipboard for pasting into notes, emails,
GitHub issues, and so on. Renders well as plain text and as rendered
Markdown.
- **HTML** — copied for pasting into rich-text editors and web pages.
A third format, the `<citation-card>` Web Component from
`ArchitectureOverview.md` §14.2, is out of scope here and lands in a later
workplan. Its visual presentation should be *equivalent* to the HTML form
but is not constrained to be byte-identical.
The two formats need a written contract so that:
1. UI components (T04 sidebar export, future web embeds) can rely on the
exact output structure.
2. Snapshot tests fail loudly if the format drifts.
3. Consumers that style the HTML form know which elements and classes are
stable.
## Decision
### Markdown format (CE-WP-0004-T02)
```markdown
> {quote}
*{sourceLabel}* · [Open source]({openContextUrl})
{commentary}
```
Rules:
- Each `{quote}` line is rendered with the leading `> ` blockquote marker,
preserving line breaks in the source quote. A single-line quote is one
blockquote line; a multi-line quote becomes multiple `> `-prefixed
lines.
- A blank line follows the blockquote.
- The attribution line uses an em dash (`—`, U+2014) followed by a single
space, the italicised source label, a middle dot (`·`, U+00B7) with
surrounding spaces, and the `[Open source]({openContextUrl})` link.
- The middle dot + link segment is **omitted entirely** when no
`openContextUrl` is provided (which is unusual but possible for
evidence items without an annotation).
- A blank line follows the attribution.
- The optional `{commentary}` paragraph is rendered as-is. When absent
the trailing blank line and commentary paragraph are both omitted.
- The output ends with a single trailing newline.
Reserved Markdown characters inside the quote are not escaped — the
intent is to reproduce the source text verbatim. The blockquote prefix
already neutralises the most dangerous reflow problems. The
`{sourceLabel}` is escaped to defuse `*`/`_` only; the link target is
URL-encoded by `openContextUrl()`.
### HTML format (CE-WP-0004-T003)
A single `<aside class="citation-card">` root element with this stable
structure:
```html
<aside class="citation-card">
<blockquote class="citation-card__quote">{escaped quote}</blockquote>
<p class="citation-card__attribution">
<cite class="citation-card__source">{escaped source label}</cite>
<a class="citation-card__link" href="{open context url}">Open source</a>
</p>
<div class="citation-card__commentary">{escaped commentary}</div>
</aside>
```
Rules:
- All user-supplied text is HTML-escaped (`&`, `<`, `>`, `"`, `'`).
- Inline styles are **not** emitted. Host pages provide the CSS.
- The `<a>` and the attribution `·` separator are omitted when no
`openContextUrl` is provided.
- The `<div class="citation-card__commentary">` is omitted when no
commentary is provided.
- Commentary is treated as plain text — no Markdown or raw HTML
passthrough. A future workplan can introduce a sanitiser if rich
commentary is required.
- The output ends with a single trailing newline.
### Class-name contract
The four BEM-style class names — `citation-card`, `citation-card__quote`,
`citation-card__attribution`, `citation-card__source`,
`citation-card__link`, `citation-card__commentary` — are part of the
public contract. They must not be renamed without an ADR superseding
this one.
## Consequences
- Snapshot tests in `src/engine/rendering/*.test.ts` lock these
formats. Intentional changes require updating both the snapshots and
this ADR.
- The Web Component planned for §14.2 will reuse the HTML structure
inside its shadow DOM, so the class names also become the
customisation surface for downstream stylesheets.
- The `openContextUrl` shape from
`wiki/ArchitectureOverview.md` §14.3 is now consumed by two
renderers; changing the URL scheme requires regenerating snapshots
and announcing via a new ADR.