Skip to main content

ADR 004: Generic SSE with marked and Shiki

Status: Accepted
Date: 2026-05-23

Context

The MVP includes AiStreamContainer, which renders incremental markdown from streaming AI responses. We need to decide:

  1. How the UI layer receives streamed content (protocol)
  2. Which markdown parser to use
  3. Which syntax highlighter to use
  4. Whether to bundle a specific LLM provider SDK

The library targets open-source consumers who may use any backend (self-hosted, Azure OpenAI, Anthropic, etc.).

Decision

  • Streaming protocol: Generic Server-Sent Events (SSE). The library provides parsing utilities in @synapse-ui/core but does not manage the HTTP connection — consumers connect to their own backend.
  • Markdown parser: marked — lightweight, extensible, GFM support.
  • Syntax highlighting: Shiki — accurate TextMate grammar-based highlighting with theme support.
  • No LLM SDK in the UI layer.

Consequences

Positive

  • Backend-agnostic — works with any SSE-compliant server.
  • marked is small and well-maintained with a simple API.
  • Shiki produces high-quality highlighting that matches VS Code themes.
  • Clear separation of concerns: app handles auth/connection, library handles rendering.

Negative

  • Consumers must implement their own SSE connection and error handling.
  • Shiki has a larger bundle size than highlight.js (mitigated by lazy-loading grammars).
  • marked output must be sanitized before DOM insertion (XSS risk).
  • Token counting in AiPromptBar is heuristic-only — no server-side validation.

Alternatives Considered

AlternativeWhy rejected
WebSocket streamingMore complex; SSE is sufficient for unidirectional AI text streams
ngx-markdownAdds Angular-specific wrapper dependency; marked is simpler to integrate
highlight.jsLower quality highlighting; harder to match theme tokens
Prism.jsSimilar to highlight.js; Shiki preferred for accuracy
Azure OpenAI SDK in coreCouples library to one provider; violates open-source neutrality
Custom markdown parserUnnecessary reinventing; marked is battle-tested

Security Note

All markdown rendered by AiStreamContainer must pass through Angular's sanitizer or DOMPurify before DOM insertion. Raw HTML in markdown input is stripped.