- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Auth Surface
- Avatar
- Badge
- Browse Catalog Dialog
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Command
- Connector Setup Dialog
- Cookie Banner
- Dialog
- Directory Card
- Directory Detail
- Directory Skeleton
- DrawerНовое
- Token Parts Input
- Empty
- Field
- Fieldset
- File Preview Modal
- File Preview Skeleton
- Form
- Frame
- Group
- Icon
- Input
- Input Group
- Kbd
- Label
- Legal Shell
- Menu
- Mermaid Diagram
- Mind Map Diagram
- Not Found Screen
- Onboarding Frame
- Popover
- PDF Thumbnail
- Personalization Landing
- Preview Card
- Pricing Page
- Progress
- Radio Group
- Ring Spinner
- Scroll Area
- Select
- Separator
- Settings Page
- Settings Skills
- Settings Connectors
- Settings Capabilities
- Settings Usage
- Settings Account
- Settings Billing
- Sheet
- Sidebar
- Skeleton
- Skill Create Dialog
- Slider
- Spinner
- Stat
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Tooltip
- Компоненты AI
- Chat Conversation
- Chat Message
- Chat Response
- Chat Suggestion
- Chat Prompt Input
- Slash Highlighted Textarea
- Chat Search Dialog
- Chat Skill Doc
- Chat Connector Detail
- Chat Attachments
- Chat File Card
- Chat Token Chip
- Chat Code Block
- Chat Image
- Chat Inline Citation
- Chat Sources
- Chat Web Search
- Chat Research
- Chat Source
- Chat Actions
- Chat Context
- Chat Loader
- Chat Compaction
- Chat Timeline
- Chat Snippet
- Chat Terminal
- Chat Stack Trace
- Chat Test Results
- Chat File Tree
- Chat Environment Variables
- Chat Audio Player
- Chat Transcription
- Chat Speech Input
- Chat Mic Selector
- Chat Voice Selector
- Chat Agent
- Chat Persona
- Chat Connection
- Chat Connector Suggestion
- Chat Queue
- Chat Checkpoint
- Chat Confirmation
- Chat Artifact
- Chat JSX Preview
- Chat Schema Display
- Chat Package Info
- Chat Commit
- Chat Plan
- Chat Open In Chat
- Chat Sandbox
- Chat Model Selector
- Chat Canvas
- Chat Node
- Chat Edge
Chat Response
Per-element markdown-рендер ответа ассистента 1:1 с Claude — serif-проза, ссылки, GitHub-alerts, блоки кода, таблицы, чек-листы, картинки-слайдеры, kbd, details, сноски. Стриминг через streamdown.
Настройка Next.js с App Router
Краткое руководство по созданию нового проекта. App Router используется по умолчанию начиная с Next.js 13 — он сразу даёт React Server Components, стриминг и вложенные макеты.
Создание проекта
npx create-next-app@latest my-app --typescript --app
cd my-app
Это создаёт каталог app/. Точка входа — app/page.tsx, а общая обвязка
располагается в app/layout.tsx.
Ключевые идеи
- Серверные компоненты по умолчанию — компоненты рендерятся на сервере, пока вы
не добавите директиву
"use client". - Загрузка данных рядом с компонентом — пишите
awaitпрямо внутри компонента; никакогоgetServerSideProps. - Стриминг — оборачивайте медленные поддеревья в
<Suspense>, чтобы HTML отдавался постепенно.
Нажмите ⌘ K в панели, чтобы сразу перейти к любому маршруту.
Сравнение
| Возможность | Pages Router | App Router |
|---|---|---|
| Загрузка данных | getServerSideProps | async-компоненты |
| Макеты | _app.tsx | вложенные layout.tsx |
| Стриминг | ограниченный | из коробки |
Подробнее в официальной документации.
import {
ChatMessage,
ChatMessageAvatar,
ChatMessageContent,
} from "@/components/ui/chat-message";
import { ChatResponse } from "@/components/ui/chat-response";
const markdown = `## Настройка Next.js с App Router
Краткое руководство по созданию нового проекта. **App Router** используется по
умолчанию начиная с Next.js 13 — он сразу даёт React Server Components, стриминг и
вложенные макеты.
### Создание проекта
\`\`\`bash
npx create-next-app@latest my-app --typescript --app
cd my-app
\`\`\`
Это создаёт каталог \`app/\`. Точка входа — \`app/page.tsx\`, а общая обвязка
располагается в \`app/layout.tsx\`.
### Ключевые идеи
1. **Серверные компоненты по умолчанию** — компоненты рендерятся на сервере, пока вы
не добавите директиву \`"use client"\`.
2. **Загрузка данных рядом с компонентом** — пишите \`await\` прямо внутри компонента;
никакого \`getServerSideProps\`.
3. **Стриминг** — оборачивайте медленные поддеревья в \`<Suspense>\`, чтобы HTML
отдавался постепенно.
> [!TIP]
> Нажмите <kbd>⌘</kbd> <kbd>K</kbd> в панели, чтобы сразу перейти к любому маршруту.
### Сравнение
| Возможность | Pages Router | App Router |
| --- | --- | --- |
| Загрузка данных | \`getServerSideProps\` | \`async\`-компоненты |
| Макеты | \`_app.tsx\` | вложенные \`layout.tsx\` |
| Стриминг | ограниченный | из коробки |
Подробнее в [официальной документации](https://nextjs.org/docs/app).`;
export default function Particle() {
return (
<div className="w-full max-w-2xl p-4">
<ChatMessage from="assistant">
<ChatMessageAvatar fallback="O" />
<ChatMessageContent>
<ChatResponse>{markdown}</ChatResponse>
</ChatMessageContent>
</ChatMessage>
</div>
);
}
Установка
pnpm dlx shadcn@latest add @oracul/chat-response
Требует пакет streamdown — движок инкрементального рендера markdown (shiki-подсветка, GFM, math, mermaid) по мере поступления токенов.
Использование
import { ChatResponse } from "@/components/ui/chat-response";
<ChatMessage from="assistant">
<ChatMessageContent>
<ChatResponse>{streamingText}</ChatResponse>
</ChatMessageContent>
</ChatMessage>Принимает текст markdown как children. Каждый элемент рендерится своим renderer-ом через components-карту Streamdown, чтобы повторить разметку Claude 1:1, но на DS-токенах: serif-проза font-serif text-base leading-[1.65], заголовки font-semibold, ссылки-info, alert-варианты на семантических токенах.
Декаплинг от приложения
Никаких @/lib/*, lightbox, toast или union-типов message-part. Сайд-эффекты — через опциональные колбэки:
<ChatResponse
onImageClick={(images, index) => lightbox.open(images, index)}
onImageDownload={(src) => download(src)}
onCopy={(text) => toast("Скопировано")}
>
{markdown}
</ChatResponse>| Свойство | Тип | Описание |
|---|---|---|
children | string | Markdown-источник. Пусто → … (empty-fallback). |
onImageClick | (images: string[], index: number) => void | Открыть lightbox с навигацией по всем картинкам ответа. |
onImageDownload | (src: string) => void | Скачать картинку. Без него overlay-кнопка скрыта. |
onCopy | (text: string) => void | Хук после копирования блока кода (флаг «copied» — внутренний). |
getImages | (markdown: string) => string[] | Сбор всех URL картинок для lightbox. По умолчанию — regex по исходнику. |
isAnimating | boolean | Memo-gate стриминга (см. ниже). |
Остальные пропсы (shikiTheme, remarkPlugins, rehypePlugins, controls, className, …) пробрасываются в Streamdown.
Поведение при стриминге
Компонент мемоизирован: ре-рендер только когда меняется children или isAnimating. Безопасно прокидывать частичные строки "Hello, I a" → "Hello, I am" → "Hello, I am Claude." — рендер инкрементальный, без потери layout.
Состояния
Чек-лист релиза
Оповещения в стиле GitHub отрисовываются с семантическими токенами — note, tip, important, warning и caution соответствуют своей цветовой роли в DS:
Отведите релизную ветку от main перед тем, как ставить тег.
Запустите pnpm changeset, чтобы автоматически подготовить changelog.
Миграцию в 0003_add_index.sql нельзя откатить.
Деплои старше 24 часов нельзя откатить через интерфейс.
Никогда не запускайте db:reset на продакшен-строке подключения.
Отмечайте пункты — список интерактивный1:
Горячая клавиша для публикации: ⌘ Enter. Встроенный код вроде
pnpm release читается прямо в предложении, а блоки кода получают кнопку копирования
при наведении:
export async function release(tag: string) {
await build();
await publish(tag);
}
Footnotes
-
Состояние хранится внутри компонента; переключатели сохраняются, пока ответ примонтирован. ↩
"use client";
import { useState } from "react";
import {
ChatMessage,
ChatMessageAvatar,
ChatMessageContent,
} from "@/components/ui/chat-message";
import { ChatResponse } from "@/components/ui/chat-response";
const markdown = `### Чек-лист релиза
Оповещения в стиле GitHub отрисовываются с семантическими токенами — note, tip,
important, warning и caution соответствуют своей цветовой роли в DS:
> [!NOTE]
> Отведите релизную ветку от \`main\` перед тем, как ставить тег.
> [!TIP]
> Запустите \`pnpm changeset\`, чтобы автоматически подготовить changelog.
> [!IMPORTANT]
> Миграцию в \`0003_add_index.sql\` **нельзя** откатить.
> [!WARNING]
> Деплои старше 24 часов нельзя откатить через интерфейс.
> [!CAUTION]
> Никогда не запускайте \`db:reset\` на продакшен-строке подключения.
Отмечайте пункты — список интерактивный[^1]:
- [x] Поднять версию
- [x] Обновить changelog
- [ ] Поставить тег на коммит
- [ ] Опубликовать в npm
Горячая клавиша для публикации: <kbd>⌘</kbd> <kbd>Enter</kbd>. Встроенный код вроде
\`pnpm release\` читается прямо в предложении, а блоки кода получают кнопку копирования
при наведении:
\`\`\`ts
export async function release(tag: string) {
await build();
await publish(tag);
}
\`\`\`
[^1]: Состояние хранится внутри компонента; переключатели сохраняются, пока ответ примонтирован.`;
export default function Particle() {
const [copied, setCopied] = useState<string | null>(null);
return (
<div className="w-full max-w-2xl p-4">
<ChatMessage from="assistant">
<ChatMessageAvatar fallback="O" />
<ChatMessageContent>
<ChatResponse
onCopy={(text) => {
setCopied(text);
setTimeout(() => setCopied(null), 1500);
}}
>
{markdown}
</ChatResponse>
{copied ? (
<p className="mt-2 text-muted-foreground text-xs" role="status">
Скопировано символов в буфер обмена: {copied.length}
</p>
) : null}
</ChatMessageContent>
</ChatMessage>
</div>
);
}
| Состояние | Что рендерится |
|---|---|
| idle / result | Полная проза: заголовки h1-h3, параграфы, списки, strong/em/del. |
| hover | Ссылки сдвигают стрелку (ArrowUpRight) и усиливают подчёркивание; строки таблицы подсвечиваются bg-muted/40; карточка картинки даёт shadow-md и scale; в блоке кода появляется кнопка копирования; строка задачи подсвечивается bg-accent/50; в слайдере на group-hover проявляются стрелки. |
| focus-visible | Кнопка копирования в блоке кода проявляется (focus-visible:opacity-100). |
| streaming / isAnimating | Memo-gate: ре-рендер только по смене children/isAnimating. |
| empty | children пусто → выводит …. |
| error (kbd guard) | Ломаный <kbd> (пустой или содержит </>/kbd/span) → отдаётся сырым текстом, не ломает строку. |
| error (LaTeX) | Кривой LaTeX рендерится исходником (throwOnError:false в math-плагине). |
| open | <details> поворачивает chevron на 90°; слайдер пересчитывает canPrev/canNext по скроллу. |
| selected | Чек-бокс задачи отмечен → заливка bg-foreground + line-through текста. |
| alert-варианты | note → info, tip → success, important → info, warning → warning, caution → destructive (только семантические токены). |
| code copied | После клика — Check-иконка text-success на 1.5 с. |
| auto-link | Если текст ссылки равен URL — отображается без протокола (stripProtocol). |
| image single vs slider | Одна картинка — карточка max-w-md; подряд идущие — горизонтальный слайдер с wheel-to-horizontal и стрелками. |
Поддерживаемая разметка
- Заголовки
h1/h2→text-lg font-semibold,h3→text-base font-semibold. - Ссылки —
text-infoс подчёркиваниемdecoration-info/30, стрелкаArrowUpRight; auto-link срезает протокол; якорные ссылки#idскроллят плавно. - GitHub-alerts —
[!NOTE],[!TIP],[!IMPORTANT],[!WARNING],[!CAUTION]с иконкой, eyebrow-лейблом и семантической рамкой/заливкой. - Блоки кода — единый блок с подписью языка (алиасы
js → javascript), shiki-подсветка от Streamdown, кнопка копирования reveal-on-hover. - Инлайн-код — мономоноширинный с мягкой подложкой, без кнопки копирования (1:1 с Claude).
- Таблицы — обёртка
rounded-xl border bg-card,thead bg-muted/60, зебра чётных строк, row-stagger анимация появления. - Чек-листы (
- [ ]/- [x]) — интерактивные: клик переключает состояние, отмеченные перечёркнуты. - Картинки — карточка/слайдер с overlay-кнопкой скачивания и кликом в lightbox.
<kbd>,<details>/<summary>, сноски (GFM),<hr>, математика ($$…$$).
Кастомизация
Базовая типографика идёт на DS-токенах. Дополнительная стилизация — через className; токены кода/таблиц/alerts можно переопределить точечно:
<ChatResponse className="text-[15px]">{markdown}</ChatResponse>