-
Notifications
You must be signed in to change notification settings - Fork 0
Rendering Pipeline
English | 中文 | 日本語 | 한국어 | Español | Português | Русский
The Markdown → trusted-HTML pipeline for assistant bubbles lives in lib/sidepanel/render.js. The order of operations is load-bearing:
-
fixCjkEmphasisSpacing(the CJK-adjacency**patch — its character classes deliberately mirror marked's flanking rules: punctuation =\p{P}∪\p{S}, neighbors = any letter/digit) - Extract
<think>blocks → placeholders - Extract block math
$$…$$/\[…\] - Extract inline math
$…$/\(…\)— two guards:- the markdown escapes
\\and\$are consumed verbatim first (\$8Kis a literal dollar — 2026-09-30 MRR-table report); - inline
$follows Pandoc's delimiter rules: tight opening/closing, and a closer followed by a digit is rejected (从**$8K**、**$20K**once paired its bare$s into KaTeX, which rendered the*s as∗— 2026-10-01 report). Rejected candidates are returned verbatim to marked.
- the markdown escapes
-
marked.parse→ 6. DOMPurify → 7. KaTeX worker batch-render + restore (AFTER sanitization, so MathML attributes never meet DOMPurify) → 8. restore think foldables.
-
renderStreamingSafe(sync) handles only the tail each frame; completed markdown blocks commit once (findStreamingBlockBoundary: blank-line boundary, outside fences, next line not a list item) and only the open tail re-parses — O(n²) becomes O(n). Open fences take a fast path (<pre><code>via textContent, zero marked calls). - The reveal pacer is vendored
markstream-core(makeStreamRendererreturns a closure;.destroy()before reassigning it). DONE re-renders the full text viarenderSafe(KaTeX + final think blocks), fixing any streaming approximation. - History renders lazily: an IntersectionObserver upgrades bubbles as they near the scrollport;
content-visibility:autoonly under.cv-settled(double-rAF) — never during initial layout/scroll. - KaTeX offload:
katex-threshold.jsdecides worker vs sync; the worker batches per message, caches, and falls back to sync on failure.
| Fence | Renderer | Vendor | Notes |
|---|---|---|---|
| ```mermaid | renderMermaid |
mermaid |
securityLevel:'loose' (KaTeX in foreignObject) + sanitizeMermaidSvg (load-bearing security); AI repair card on parse failure (REPAIR_MERMAID) |
| ```echarts | renderEcharts |
echarts | built-in 'dark' theme + transparent canvas unless the model names its own |
| ```markmap | renderMarkmap |
markmap lib+view |
pre.replaceWith(wrapper) BEFORE Markmap.create() (layout measures text); mm.rescale() is a RELATIVE multiplier — zoom goes through _markmapZoomBy's absolute conversion |
| ```smiles | renderSmiles |
RDKit MinimalLib WASM |
get_mol/get_rxn; RDKit doubles as the validator (chemically impossible → explicit error note, raw block kept); descriptor line (MW/logP/TPSA…); dark mode re-inks strokes but KEEPS CPK heteroatom colors (chemistry semantics — never CSS-invert) |
| ```pdb | renderPdb |
Mol* (RAW-copied IIFE) |
parsePdbBlock classifies: RCSB ID / AlphaFold AF-…-F1 (current model resolved via API) / inline coordinates; the representation preset must ride representationPreset; live viewers tracked and disposed (browsers cap ~16 WebGL contexts) |
| ```dot | renderDot |
@viz-js/viz (base64-inlined WASM) | default-black ink remap + contrast rescue; invalid DOT → error note + raw code kept (no AI repair); model contract: ID [label="…"] only |
The render contract (what the model is taught to emit) has ONE text source: CAPABILITY_HINTS_ENTRIES in lib/prompt-assembly.js — CAPABILITY_HINTS (LLM/Hermes system channel) and AGENT_RENDER_HINT (agent first turn) are both assemblies of it. No hand-mirrors may reappear (the old ```nn mirror missed an update for a day).
- Diagram vendors lazy-load and pre-warm only when a fence is actually seen (
wantsChartVendorssniff behind a one-shot gate) — a diagram-free session never pays the ~7MB parse. -
Mol's dist never goes through esbuild* (RAW-copied prebuilt IIFE); Mol*/RDKit embind glue gets CSP patches applied at copy time in build.mjs (hand-written invokers replace
new Function; eager h264 init made lazy) — patch regexes anchor on stable strings and THROW THE BUILD when a molstar upgrade stops matching. - The verification gate after any vendor swap is a REAL extension load (
launchPersistentContext --load-extension, xvfb): dev-preview is a plain http page that allows eval and cannot catch CSP violations (2026-09-30 lesson).
_chartObservers (ResizeObserver registry) / _renderDisposers (per-element) / disposeMolstarViewers() run before every bubble removal, history re-render, and history wipe. The classic leak symptom is a detached WebGL context rendering forever (~16-context browser cap).
Source of truth: AGENTS.md "sidepanel.js module split" (render.js / mermaid-utils / chem / dot), "Renderer-vendor audit", 批B/批C invariants; ADR-0008 / 0010 / 0011. Synced 2026-10-01.
English
- Home
- Architecture
- Rendering Pipeline
- Storage Model
- Providers and Agents
- ASR and Video Analysis
- Security Model
- Design Decisions
- Contributing
中文
相关 / Related
日本語
한국어
Español
- Inicio
- Arquitectura
- Pipeline de renderizado
- Modelo de almacenamiento
- Proveedores y agentes
- ASR y análisis de vídeo
- Modelo de seguridad
- Decisiones de diseño
- Contribuir
Português
- Início
- Arquitetura
- Pipeline de renderização
- Modelo de armazenamento
- Provedores e agentes
- ASR e análise de vídeo
- Modelo de segurança
- Decisões de design
- Contribuindo
Русский