generated from coulomb/repo-seed
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:
115
docs/decisions/ADR-0007-citation-card-format.md
Normal file
115
docs/decisions/ADR-0007-citation-card-format.md
Normal 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.
|
||||
Reference in New Issue
Block a user