ADR 0129: Intercom — тело сообщения: Markdown, fenced code и preview¶
Статус: Accepted · In progress
Дата: 2026-05-19
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0123 | Skia-лента; фазы отрисовки сообщений |
| 0128 | AttachmentAnchor — ссылка на workspace; ортогонально fenced |
| 0045 | content сообщения в event log |
| 0069 | Полный Markdown: Markdig, MFD / окно |
| 0023 | Диаграммы, Kroki (опционально в preview сообщения) |
| 0120 | Composer в Forward |
Вне ADR (playbook)¶
| Документ | Роль |
|---|---|
| intercom-ux-reference-slack-mattermost-v1.md | Flat feed, макеты ленты |
| intercom-design-hub-v1.md | Домены D1–D9 |
Резюме¶
Зафиксировать, как тело реплики (content как markdown-строка) показывается в ленте Intercom и когда открывается полный preview — отдельно от 0128 (attach / chip / reveal в репозиторий).
- Fenced code (
```lang … ```) — первый класс: сегменты prose + code, mono strip в Skia; неAttachmentAnchor. - Prose в ленте — inline subset (
**,*,`) черезSkiaMarkdownLayout; не полный CommonMark в scroll. - Полный MD — reuse 0069 по действию на сообщении, не Markdig в каждой строке ленты.
- Парсер attach (0128 §5):
[…]не разбирать внутри fenced code-сегментов.
Не входит: attach, chips, /attach, reveal в редактор; composer WYSIWYG; syntax-highlight engine с нуля (stretch — lang tag).
Контекст¶
Агент и человек шлют в Intercom markdown: заголовки, списки, fenced listings, иногда таблицы. В CIDE уже есть:
- Лента:
ChatMessageBodyPresentation.SplitSegments+SkiaMonoCodeStrip+SkiaMarkdownLayout(0123 фаза 3). - Preview:
MarkdigMarkdownPreviewRenderer, MFD tool, MCPide_show_preview(0069).
Без ADR смешивают: «код в чате» = attach к файлу; или требуют полный Markdig в scroll (perf, hit-test); или парсят [M:Foo] внутри ``` в ответе агента.
Граница с 0128:
| Этот ADR (0129) | 0128 | |
|---|---|---|
| Смысл | Цитата / форматирование в тексте сообщения | Указатель на артефакт workspace |
| Wire | content (строка) |
AttachmentAnchor[] + offsets в prose |
| Клик | Копировать / развернуть fence; «Открыть как MD» | Reveal в редакторе |
Проблема¶
- Один парсер на всё: attach-токены и markdown fences конкурируют за
[и```. - Два ожидания рендера: «как Slack» в ленте vs «как документ» для длинного ответа.
- v1 ограничения кода: один fence, без lang-highlight — не задокументированы → кажется багом.
- Дублирование движка: второй Markdig в Skia ломает 0069 и 0123.
Решение¶
1. Канон хранения¶
- Тело сообщения в 0045: поле
content— UTF-8 markdown source (как пришло от агента/человека). - Не дублировать fenced blocks в отдельных файлах в v1 (тяжёлые вложения — отдельная политика 0045 §формат).
- Проекция UI:
ChatMessageBodySegment[]— вычисляется при render, не обязательно в event log v1.
2. Fenced code (канон)¶
Fenced — да: ```optional-lang … ``` разбивает тело на сегменты:
ChatMessageBodySegmentKind |
Отрисовка |
|---|---|
Prose |
SkiaChatBubbleRenderer + SkiaMarkdownLayout (inline subset) |
Code |
SkiaMonoCodeStrip — inset, mono, перенос |
| Аспект | Fenced | 0128 attach |
|---|---|---|
| Привязка к репо | нет (снимок текста) | да (resolve / re-resolve) |
| Клик по умолчанию | копировать / expand | reveal → файл |
| В prompt агента | текст блока | + excerpt, memberKey, file |
v1 (код): SplitSegments — первый fenced block + tail prose; без lang-highlight.
v1.1: все fences в сообщении; copy chip на блоке; опционально lang → лёгкая подсветка или тот же strip.
v2: collapse длинного fence (порог строк — открытый вопрос).
3. Inline Markdown в prose (лента)¶
Подмножество в SkiaMarkdownLayout (0123):
**bold**,*italic*,_italic_,`code`- Заголовки
#, списки, таблицы — не в v1 ленте (видны как plain или через preview)
Лимит тела при measure: trim ~32k символов в SkiaChatBubbleRenderer (защита scroll).
4. Полный Markdown (reuse preview)¶
Целый .md в content допустим. Рендер:
| Поверхность | Механизм |
|---|---|
| Лента (steady) | §2–3 — subset + fences |
| Preview | 0069: MarkdownPreviewPayloadBuilder → MarkdigMarkdownPreviewRenderer |
| MCP | ide_show_preview, show_markdown_preview_page, chat_export_readable |
Не встраивать Markdig/WebView в каждую строку Skia-ленты.
Действие (фаза 2): command_id intercom.open_message_markdown_preview — content сообщения → тот же pipeline, что 0069; заголовок «Сообщение #N».
Эвристика UI (фаза 3, опционально): chip «Открыть как Markdown», если в теле есть ## / таблица / длина > N строк.
Composer остаётся source markdown; WYSIWYG в composer — не цель v1.
5. Согласование с attach-парсером (0128)¶
Порядок разбора тела сообщения для UI:
SplitSegments→ prose | code regions.- Attach bracket parse только в prose-сегментах (и в composer до send).
- В code-сегментах
[M:Foo]— литералы, не anchors.
Экранирование \[ в prose — фаза 2+ 0128.
Фазы внедрения¶
| Фаза | Содержание | Зависимости | CIDE |
|---|---|---|---|
| 0 | Документировать v1 ограничения; тесты SplitSegments (несколько fence) |
— | да — ChatMessageBodyPresentation.SplitSegments (первый fence + tail); ChatMessageBodyPresentationTests |
| 1 | Несколько fenced blocks; copy на блоке | 0123 | нет |
| 2 | intercom.open_message_markdown_preview → 0069 |
0069 | нет |
| 3 | Collapse long fence; chip «Открыть как MD» по эвристике | 0123 | нет |
| 4 (stretch) | Lang highlight в strip; Kroki в preview тела сообщения | 0023 | нет |
Граница с attach (0128): prose vs fenced — в коде v1; bracket attach в prose не парсится в ленте (только slash/MCP reveal-select по 0131).
Не цели¶
- AttachmentAnchor,
/attach, reveal (0128). - Замена 0069 для файлов
.md. - CommonMark-совместимый рендер внутри scroll ленты.
- Подсветка синтаксиса как в VS Code (отдельный проект / lib).
Отклонённые альтернативы¶
| Альтернатива | Почему нет |
|---|---|
| Markdig в каждой строке ленты | Perf, layout, hit-test; дубли 0069 |
| Fenced = attach автоматически | Разный смысл; агент часто шлёт учебный snippet без файла |
| Plain text only в ленте | Уже есть markdown от агента; хуже читаемость |
| Отдельный event type «code_block» | Дубли content; fences выводятся из markdown |
Согласованные решения¶
| Тема | Решение |
|---|---|
| Fenced в ленте | Да, канон; расширять парсер |
| Полный MD | В content да; полный рендер — 0069 по действию |
Attach [ в fence |
Не парсить |
| v1 fences | Один блок — известное ограничение до фазы 1 |
Открытые вопросы¶
- Порог collapse fenced block (строки; по умолчанию свёрнут >40?).
open_message_markdown_preview: MFD tool vs отдельное окно по умолчанию.- Kroki / diagram expansion для тела сообщения в preview (0023).
- Несколько
```подряд без prose между — merge или отдельные strips. - Lang tag: игнорировать vs отображать в заголовке strip.
История¶
| Дата | Изменение |
|---|---|
| 2026-05-19 | Proposed: вынесено из 0128 §11–12; fenced + MD preview. |
| 2026-05-20 | Accepted · In progress; фаза 0 в CIDE; колонка CIDE; граница с attach/0131. |