@ai37/copilotkit-md-renderer
v0.2.0
Published
CopilotKit assistant markdown renderer with KaTeX formula support, built on Streamdown. KaTeX core + extension points.
Readme
@ai37/copilotkit-md-renderer
Рендерер markdown ответов ассистента для CopilotKit v2 с поддержкой
KaTeX-формул ($…$ / $$…$$) и безопасным sanitize. Обёртка над
streamdown.
Ядро + точки расширения: компонент владеет порядком рехайп-пайплайна
[rehypeKatex, …extra, rehypeSanitize], а приложение добавляет свои плагины/схему
снаружи, не форкая компонент.
Установка
pnpm add @ai37/copilotkit-md-renderer
# peer: react ^18 || ^19Важно: пакет не несёт CSS KaTeX. Подключи стили один раз в корне приложения:
import "katex/dist/katex.min.css"Использование
Слот рендерера ассистента <CopilotChat>:
import { AssistantMarkdown } from "@ai37/copilotkit-md-renderer"
<CopilotChat
chatView={{
messageView: {
assistantMessage: { markdownRenderer: AssistantMarkdown },
},
}}
/>Точки расширения
| Проп | Назначение |
| --- | --- |
| content: string | Markdown-источник. |
| extraRemarkPlugins?: PluggableList | Доп. remark-плагины, ПОСЛЕ remark-math. |
| extraRehypePlugins?: PluggableList | Доп. rehype-плагины, ПОСЛЕ rehype-katex и ПЕРЕД rehype-sanitize. |
| sanitizeSchema?: Schema | Схема финального sanitize. По умолчанию katexSchema. |
| foldSections?: boolean \| { shouldFold? } | Фолдинг секций в <details> (см. ниже). По умолчанию выключен. |
remarkPlugins/rehypePlugins намеренно недоступны — базовый KaTeX-пайплайн
заменить нельзя, только дополнить.
Пример: своя rehype-надстройка
Если extra-плагин эмитит свою разметку, разреши её в sanitizeSchema (иначе
sanitize её срежет):
import { AssistantMarkdown, katexSchema } from "@ai37/copilotkit-md-renderer"
import type { Schema } from "hast-util-sanitize"
import { myRehypePlugin } from "./myRehypePlugin"
const schema: Schema = {
...katexSchema,
tagNames: [...(katexSchema.tagNames ?? []), "section"],
attributes: {
...katexSchema.attributes,
"*": [...((katexSchema.attributes?.["*"] as string[]) ?? []), "data-foo"],
},
}
function MyRenderer(props: { content: string }) {
return (
<AssistantMarkdown
{...props}
extraRehypePlugins={[myRehypePlugin]}
sanitizeSchema={schema}
/>
)
}Спредь
katexSchema.attributesцеликом — иначе потеряются per-tag KaTeX-атрибуты и формулы сломаются.
Фолдинг секций (foldSections)
Секция ответа агента «заголовок + контент до следующего заголовка любого
уровня» сворачивается в нативный <details><summary>…</summary>…</details>.
Заголовок остаётся элементом hN внутри summary. Секция рождается свёрнутой уже
при стриминге; дальше состоянием владеет DOM.
Контракт маркера (дефолт): сворачивается заголовок, чей текст начинается с
[>] (пробел после маркера необязателен); маркер вычищается из вывода.
Маркеры ставит шаблон/промпт агента:
### [>] Ход расчёта
…этот контент спрячется под details…<AssistantMarkdown content={content} foldSections />Проп сам собирает обязательную связку из трёх частей: rehype-плагин
rehypeFoldSections (в конец extra-цепочки, до sanitize), схему
withFoldTags(sanitizeSchema) и сплиттер splitMarkdownAtHeadings — Streamdown
гоняет rehype-пайплайн по каждому блоку отдельно, и без нарезки «секция = один
блок» плагин видел бы заголовок без контента. Свой
parseMarkdownIntoBlocksFn, если передан, остаётся в приоритете.
Внешний вид пакет не несёт (ни иконок, ни CSS) — стилизуй слотом
components.summary:
<AssistantMarkdown
content={content}
foldSections
components={{ summary: MySummary }}
/>Без слота — нативный браузерный маркер details.
Кастомный предикат — например «сворачивать все h5» (тогда маркер не нужен и ничего не вычищается):
<AssistantMarkdown
content={content}
foldSections={{
shouldFold: (heading) => heading.tagName === "h5",
}}
/>Для сборки руками (без пропа) экспортируются rehypeFoldSections,
splitMarkdownAtHeadings, withFoldTags.
Экспорты
AssistantMarkdown,AssistantMarkdownPropskatexSchema— KaTeX-friendly схема sanitize (база для расширения)MATHML_TAGS— список MathML-тегов, которые эмитит KaTeXrehypeFoldSections,RehypeFoldSectionsOptions— rehype-плагин фолдингаsplitMarkdownAtHeadings— сплиттер блоков «секция = один блок»withFoldTags— расширение sanitize-схемы наdetails/summary
License
Apache-2.0
