Rich Feed
Rich Feed 2d — Diagrams & Widgets
Mermaid flow/sequence diagrams, read-only widget cards, deep-link launchers, and bar/line charts — one lazy-loaded, sanitised extension to the 2a frame.
Situation
2a's parseChips model — a trailing fence Mallory attaches, toSpeech already strips — extends to a full family of structured visual blocks: mermaid diagrams (flowchart/sequence only), read-only widget cards, deep-link launchers, and (added mid-build) bar/line charts. Widgets, launchers, and charts are JSON rendered by typed Svelte components with text interpolation only — no {@html}; mermaid is the one exception, rendered through strict-mode SVG passed through an extended DOMPurify profile.
The entire wave is client-side. The Worker's only change is a persona-prompt string; blocks ride inside the existing content string with no stream/DB/route change, preserving the edge-local invariant.
Key decisions
One parser, extended
parseBlocks peels all recognised structured fences (mermaid/widget/launch/chart) in document order after parseChips runs; blocks render appended after the prose, not spliced in-place.
Mermaid is lazy and sandboxed
await import('mermaid') only on first diagram render (never in the main bundle); securityLevel:'strict' disables HTML labels/click directives, and the rendered SVG is passed through a DOMPurify SVG profile forbidding script/foreignObject/event handlers before insertion.
Widgets/launchers are data, not HTML
Both are JSON parsed into typed Svelte components using text interpolation only, so the injection surface is just escaped text plus a validated launcher href.
Launcher targets are allow-listed
A launcher renders only for an internal route, obsidian:, tel:, or mailto: target (isLaunchTarget); anything else — including external http(s) — is filtered out rather than rendered as a dead button.
Invalid-but-recognised blocks are dropped, unrecognised fences pass through
A malformed widget/launch/mermaid/chart fence is dropped silently (never dumps raw JSON into prose); an unrelated fence like ``python`` is left untouched and renders as an ordinary code block.
Chart added mid-build (Amendment)
The original draft deferred bar/line charts to 2e; Alex requested they ship in 2d instead — added as a hand-rolled inline-SVG ChartBlock with no new dependency.
Architecture
A pure multi-fence parser feeds five purpose-built Svelte renderers; only mermaid needs a sandbox because only mermaid renders untrusted markup as SVG.
FeedBlock union, in document order, after parseChips.sanitizeSvg; quiet placeholder while loading, fenced-source fallback on failure.{@html}.<button>/<a> per allow-listed target with discernible labels and visible focus states.{type,title,series} payload — no new dependency.Risks & guardrails
Diagram source as an injection vector
Mermaid source is model-authored but treated as untrusted by policy — strict mode plus a DOMPurify SVG pass defends against a compromised or replayed reply becoming a script-execution path.
Bundle cost
Mermaid (~500KB+) is dynamically imported only on first diagram render, and the resulting async chunk is auto-precached by the existing service worker, so most replies and offline cold-starts are unaffected.
Dead or booby-trapped launcher buttons
Failing entries are filtered out rather than rendered; a launch block with no valid targets left is dropped entirely.
Diagram accessibility
Rendered SVGs get role=img and an aria-label from an optional %% alt: comment or a generic fallback — flagged as an open question for richer alt text in a future pass.
Delivery
Pure block parsing
parseBlocks + FeedBlock union + isLaunchTarget, unit-tested for extraction, validation, ordering, and chip composition.
SVG sanitiser + lazy mermaid dependency
sanitizeSvg added to markdown.ts; mermaid added as a lazy dynamic-import dependency only.
Block components
MermaidBlock, WidgetCard, LaunchButtons (and ChartBlock per the amendment) built and wired.
Feed + persona wiring
Blocks rendered in document order after the prose in +page.svelte; one persona bullet permitting sparing use.
Out of scope
docs/superpowers/specs/2026-06-13-rich-feed-2d-diagrams-widgets-design.mdplan ·
docs/superpowers/plans/2026-06-13-rich-feed-2d-diagrams-widgets.md