Files
agentik-desktop/MARKDOWN-SOURCE.md
T
subochev 27f349550c Макеты — один sketches/index.html; approved/ удалён
- sketches/index.html: все экраны одним документом в порядке 1·… (14–19 — бывшие approved)
- approved/ (4 файла групп + 3 макета + README) удалён как дубль
- стили вынесены в sketches/style.css
- README, THEMES, BORROW-FROM-ASSISTENT, MARKDOWN-SOURCE, MIC-ASR-SEARCH
  приведены к фактическому состоянию
2026-09-28 10:07:57 +03:00

137 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Отрисовка Markdown: откуда взято и где лежит
**Статус: УЖЕ СКОПИРОВАНО В ПРОЕКТ (28.09.2026).** Код вендорен в `src/`,
собирается, тесты парсера (11 шт.) зелёные. Своё писать не надо.
Источник — приватный репозиторий **`ai/assistent`** (https://git.binom.pw/ai/assistent,
ветка `main`, проверено на коммите `2c5b3f9`, файлы добавлены в `1acced4` 2026-08-13).
Пакеты переименованы под наш проект; внешняя зависимость — одна
(`ru.otpbank.ai:markdown`).
---
## Куда что легло
| В assistent | У нас |
|---|---|
| `client-shared/.../shared/markdown/MarkdownSegment.kt` | `src/jvmMain/kotlin/pw/binom/agentik/desktop/markdown/MarkdownSegment.kt` |
| `client-shared/.../shared/markdown/MarkdownParserImpl.kt` | `src/jvmMain/kotlin/pw/binom/agentik/desktop/markdown/MarkdownParserImpl.kt` |
| `client-shared/.../shared/ui/MarkdownRendering.kt` | `src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/MarkdownRendering.kt` |
| `client-shared/.../shared/ui/ChatImage.kt` | `src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/ChatImage.kt` |
| `client-shared/.../shared/ClientImageSupport.kt` | `src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/ClientImageSupport.kt` |
| `client/.../client/ui/MarkdownRenderer.kt` | `src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/MarkdownRenderer.kt` |
| `client-shared/src/test/.../markdown/MarkdownParserTest.kt` | `src/jvmTest/kotlin/pw/binom/agentik/desktop/markdown/MarkdownParserTest.kt` |
Переименование пакетов:
- `pw.binom.client.shared.markdown` → `pw.binom.agentik.desktop.markdown`
- `pw.binom.client.shared.ui` → `pw.binom.agentik.desktop.ui`
- `pw.binom.client.shared` (`ClientImageSupport`) → `pw.binom.agentik.desktop.ui`
- `pw.binom.client.ui` (`MarkdownRenderer`) → `pw.binom.agentik.desktop.ui`
### Почему файлов шесть, а не четыре
Изначально в доке значились 4 файла. При сборке выяснилось, что
`MarkdownRendering.kt` для сегментов-картинок вызывает `ChatImage(...)`, а тот —
`ClientImageSupport`. Поэтому взяты ещё `ChatImage.kt` и `ClientImageSupport.kt`.
Оба независимы от `chat-*` и `MessageBubble`.
`ClientImageSupport` — это реестр: платформенный клиент при старте регистрирует
`decoder` (байты → `ImageBitmap`) и, при желании, `fetcher` и `serverBaseUrl`.
Пока `decoder` не зарегистрирован, картинки деградируют в текст (alt/ссылку) —
ничего не падает. На десктопе декодер регистрируется так:
```kotlin
ClientImageSupport.decoder = { bytes ->
org.jetbrains.skia.Image.makeFromEncoded(bytes).toComposeImageBitmap()
}
```
## Точки входа
- Парсинг: `parseMarkdown(markdown: String): List<MarkdownSegment>` (в `...desktop.markdown`).
- Отрисовка: `renderMarkdownSegments(content, textColor, fontSize, isBubbleScrolled, onUrlClick)` (в `...desktop.ui`).
- Открытие ссылок/`parseMultiline`: `...desktop.ui.MarkdownRenderer`.
## Зависимости и сборка (уже настроено)
`gradle/libs.versions.toml`:
```toml
compose-multiplatform = "1.7.3" # версия из assistent; собирается на нашем Kotlin 2.4.20
markdown = "0.51.0"
markdown = { module = "ru.otpbank.ai:markdown", version.ref = "markdown" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
compose = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
```
`build.gradle.kts` — плагины `kotlin-compose` + `compose`, зависимости
`compose.foundation` / `compose.ui` / `compose.material3` /
`compose.materialIconsExtended` (иконка копирования) + `libs.markdown`.
`compose.desktop.currentOs` пока НЕ подключён — он понадобится, когда появится
настоящее окно (`compose.desktop { application { mainClass = ... } }`), для
компиляции рендера он не нужен.
`settings.gradle.kts` — репозитории (без них `ru.otpbank.ai:markdown` не найдётся):
```kotlin
maven {
name = "developerspace"
url = uri("http://192.168.76.117/repository/developerspace-prod-mvn-hosted/")
setAllowInsecureProtocol(true)
}
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
maven("https://maven.google.com")
```
Проверено: `./gradlew compileKotlinJvm` — OK; `./gradlew jvmTest` —
`MarkdownParserTest` 11/11, `ConversationMetaRepositoryIT` 14/14.
## Как пересинхронизировать, если в assistent что-то поправят
RAW-адрес: `/raw/branch/main/<путь>` (нужен логин, репозиторий приватный).
```bash
BASE=https://git.binom.pw/ai/assistent/raw/branch/main
AUTH='subochev:ПАРОЛЬ'
for f in \
client-shared/src/main/kotlin/pw/binom/client/shared/markdown/MarkdownSegment.kt \
client-shared/src/main/kotlin/pw/binom/client/shared/markdown/MarkdownParserImpl.kt \
client-shared/src/main/kotlin/pw/binom/client/shared/ui/MarkdownRendering.kt \
client-shared/src/main/kotlin/pw/binom/client/shared/ui/ChatImage.kt \
client-shared/src/main/kotlin/pw/binom/client/shared/ClientImageSupport.kt \
client/src/main/kotlin/pw/binom/client/ui/MarkdownRenderer.kt ; do
curl -sS -u "$AUTH" "$BASE/$f" -o "/tmp/$(basename "$f")"
done
```
После копирования — тем же `sed`-ом переименовать пакеты (см. таблицу выше),
`MarkdownRenderer` дополнительно сослать `renderMarkdownSegments` на
`pw.binom.agentik.desktop.ui`.
## Что рендер умеет (по коду)
- **Текст** с участками: жирный, курсив, код, зачёркнутый, ссылки.
- **Блоки кода** — с названием языка и кнопкой копирования (`LocalClipboardManager`).
- **Таблицы** — заголовки/строки, переключаются на горизонтальный скролл по
`isBubbleScrolled` (иначе Compose падает на бесконечной maxWidth).
- **Картинки** — `data:` и `http(s)`; рисуются через `ChatImage`/`ClientImageSupport`.
- **Разделители** (`---`).
- **Кликабельные ссылки** — нажатие уходит в `onUrlClick`.
## Известный баг на будущее
В assistent в `MessageBubble.kt:153` (НЕ скопирован) `hasTable` вызывает
`parseMarkdown` вторым проходом до основной отрисовки — двойной парс на пузырь.
У нас `MessageBubble` свой, при его написании в 0-й версии просто звать
`renderMarkdownSegments` один раз; кэшировать разбор через
`remember(record.id, record.text)`.
## Что НЕ брали
- `MessageBubble.kt` — тянет `chat-common` и чужие модели сообщений.
- Модули `chat-*`, `ChatScreen`, `ChatViewModel`, `SettingsDialog` и т. п.
- Границы заимствования — в `BORROW-FROM-ASSISTENT.md`.