Skip to content

Rendering Pipeline ru

Hermes Agent edited this page Oct 1, 2026 · 1 revision

Конвейер рендеринга

English | 中文 | 日本語 | 한국어 | Español | Português | Русский

Конвейер Markdown → доверенный HTML для пузырей ассистента живёт в lib/sidepanel/render.js. Порядок операций критичен:

  1. fixCjkEmphasisSpacing (патч ** при CJK-соседстве — его символьные классы намеренно зеркалят правила фланкинга marked: пунктуация = \p{P}∪\p{S}, соседи = любая буква/цифра)
  2. Извлечь блоки <think> → плейсхолдеры
  3. Извлечь блочную математику $$…$$ / \[…\]
  4. Извлечь инлайн-математику $…$ / \(…\) — две защиты:
    • markdown-эскейпы \\ и \$ поглощаются дословно первыми (\$8K — буквальный доллар — отчёт 2026-09-30 про таблицу MRR);
    • инлайн $ следует правилам делимитеров Pandoc: плотное открытие/закрытие, а закрывающий, за которым идёт цифра, отвергается (从**$8K**、**$20K** однажды спарил свои голые $ в KaTeX, который отрисовал * как ∗ — отчёт 2026-10-01). Отклонённые кандидаты возвращаются в marked дословно.
  5. marked.parse → 6. DOMPurify → 7. пакетный рендер в KaTeX-воркере + восстановление (ПОСЛЕ санитизации, чтобы атрибуты MathML никогда не встречались с DOMPurify) → 8. восстановление сворачиваемых think-блоков.

Стриминг: фиксация блоков (ADR-0011)

  • renderStreamingSafe (синхронный) обрабатывает в каждом кадре только хвост; завершённые markdown-блоки фиксируются один раз (findStreamingBlockBoundary: граница по пустой строке, вне фенсов, следующая строка не элемент списка) и повторно парсится только открытый хвост — O(n²) становится O(n). Открытые фенсы идут по быстрому пути (<pre><code> через textContent, ноль вызовов marked).
  • Reveal-pacer — вендорский markstream-core (makeStreamRenderer возвращает замыкание; .destroy() перед переприсваиванием). DONE перерендеривает весь текст через renderSafe (KaTeX + финальные think-блоки), исправляя любые стриминговые приближения.
  • История рендерится лениво: IntersectionObserver обновляет пузыри по мере приближения к scrollport; content-visibility:auto только под .cv-settled (double-rAF) — никогда во время первичной раскладки/скролла.
  • Оффлоад KaTeX: katex-threshold.js решает, воркер или синхронно; воркер батчит по сообщению, кэширует и при сбое откатывается к синхронному рендеру.

Шесть fenced-рендереров (```lang → подмена живым рендером, без кнопок)

Fence Рендерер Вендор Примечания
```mermaid renderMermaid mermaid securityLevel:'loose' (KaTeX в foreignObject) + sanitizeMermaidSvg (несущая безопасность); карточка AI-ремонта при сбое парсинга (REPAIR_MERMAID)
```echarts renderEcharts echarts встроенная тема 'dark' + прозрачный canvas, если модель не называет свой собственный фон
```markmap renderMarkmap markmap lib+view pre.replaceWith(wrapper) ДО Markmap.create() (раскладка измеряет текст); mm.rescale() — ОТНОСИТЕЛЬНЫЙ множитель — зум идёт через абсолютное преобразование _markmapZoomBy
```smiles renderSmiles RDKit MinimalLib WASM get_mol/get_rxn; RDKit заодно валидатор (химически невозможное → явная карточка ошибки, сырой блок сохраняется); строка дескрипторов (MW/logP/TPSA…); тёмная тема перекрашивает штрихи, но СОХРАНЯЕТ CPK-цвета гетероатомов (химическая семантика — никогда не CSS-инвертировать)
```pdb renderPdb Mol* (RAW-скопированный IIFE) parsePdbBlock классифицирует: RCSB ID / AlphaFold AF-…-F1 (текущая модель разрешается через API) / инлайновые координаты; preset представления должен ехать в representationPreset; живые вьюеры отслеживаются и диспосятся (браузеры ограничивают ~16 WebGL-контекстов)
```dot renderDot @viz-js/viz (WASM, инлайн base64) переразметка дефолтно-чёрных чернил + контрастное спасение; невалидный DOT → карточка ошибки + сырой код сохранён (без AI-ремонта); контракт с моделью: только ID [label="…"]

Render-контракт (чему модель научена эмитить) имеет ОДИН текстовый источник: CAPABILITY_HINTS_ENTRIES в lib/prompt-assembly.js — и CAPABILITY_HINTS (системный канал LLM/Hermes), и AGENT_RENDER_HINT (первый ход агента) — сборки из него. Ручные зеркала возвращаться не должны (старое зеркало ```nn пропустило обновление на день).

Загрузка вендоров и CSP (см. ADR-0008 / Модель безопасности)

  • Диаграммные вендоры лениво грузятся и прогреваются, только когда fence реально встречен (снифф wantsChartVendors за одноразовым гейтом) — сессия без диаграмм никогда не платит за парсинг ~7MB.
  • Дистрибутив Mol никогда не проходит через esbuild* (RAW-копия прекомпилированного IIFE); embind-клей Mol*/RDKit получает CSP-патчи во время копирования в build.mjs (рукописные инвокеры заменяют new Function; жадный h264-init сделан ленивым) — regex-ы патчей заякорены на стабильных строках и РОНЯЮТ СБОРКУ, когда апгрейд molstar перестаёт совпадать.
  • Верификационный гейт после любой замены вендора — РЕАЛЬНАЯ загрузка расширения (launchPersistentContext --load-extension, xvfb): dev-preview — обычная http-страница, разрешающая eval, и не может поймать CSP-нарушения (урок 2026-09-30).

Освобождение ресурсов

_chartObservers (реестр ResizeObserver) / _renderDisposers (поэлементно) / disposeMolstarViewers() запускаются перед каждым удалением пузыря, перерендером истории и полной очисткой истории. Классический симптом утечки — отсоединённый WebGL-контекст, рендерящий вечно (лимит браузера ~16 контекстов).


Авторитетные версии: Rendering-Pipeline (англ.) / Rendering-Pipeline-zh (кит.) — снимок первичного перевода ИИ, синхронизирован 2026-10-01.

Clone this wiki locally