# Что откуда взято **Правило простое:** чужое не переписываем заново, а берём и говорим, откуда взяли. Ниже по каждому куску: откуда, что именно, и что с ним делать на телефоне. | Куски кода | Откуда | Состояние | |---|---|---| | Ядро агента | `subochev/agentik` → Nexus `caffeine` | **подключено** | | Скелет приложения, тема | свой, в этом репозитории | **есть** | | Отрисовка Markdown | `ai/assistent` | **ещё не перенесена** | | Макеты экранов | свои, из десктопного дизайна | **лежат в `design/`** | --- ## 1. Ядро агента — берём из Nexus **Откуда:** репозиторий `https://git.binom.pw/subochev/agentik`, модуль `client`. Библиотека публикуется в Nexus (репозиторий `caffeine`) — **руками не публикуем**, публикует CI/CD по релизу. **Что берём:** `pw.binom.agentik:client`, версия задана в `gradle/libs.versions.toml` (`agentik = "5"`). **Что важно знать про неё:** движок библиотека **не задаёт** — он передаётся снаружи. На Android это OkHttp: ```kotlin agentikHttpClient(OkHttp, token) { ... } // ktor-client-okhttp уже подключён ``` Это не наша выдумка, а условие задачи: библиотека не должна тянуть на телефон чужой движок. Своя авторизация у agentik — **отдельная**, не копия A2A. ## 2. Скелет приложения и тема — своё `app/src/main/kotlin/pw/binom/agentik/android/` — написано в этом репозитории, коммит `ef4804a` («Скелет Android-клиента agentik»). **Тема** (`ui/theme/Color.kt`, `Theme.kt`) — цвета **согласованы с десктопным клиентом**: они пришли из одобренного десктопного макета `agentik-desktop/approved/001-sidebar-utility.html`. Значения: фон `#121218`, панель `#17212B`, акцент `#6AB2F2`, текст `#EBEBEB`, приглушённый `#768C9E`, ошибка `#EC3942`. Экраны обязаны читать `MaterialTheme.colorScheme.*`, а **не** палитру напрямую — чтобы позже появились другие темы без правки экранов (требование `R1.1` десктопных требований). ## 3. Отрисовка Markdown — берём из `ai/assistent` **Откуда:** https://git.binom.pw/ai/assistent (ветка `main`, проверено на коммите `2c5b3f9`). Репозиторий приватный — скачивать по RAW-адресу с логином: `https://git.binom.pw/ai/assistent/raw/branch/main/<путь>`. **Писать своё не надо — надо взять это.** Четыре файла: | Файл | Строк | Что внутри | |---|---|---| | `client-shared/src/main/kotlin/pw/binom/client/shared/markdown/MarkdownSegment.kt` | 59 | куски разметки: текст, блок кода, таблица, картинка, разделитель + стилевые участки | | `client-shared/src/main/kotlin/pw/binom/client/shared/markdown/MarkdownParserImpl.kt` | 353 | `parseMarkdown(markdown): List` | | `client-shared/src/main/kotlin/pw/binom/client/shared/ui/MarkdownRendering.kt` | 377 | сама отрисовка: inline-стили, ссылки, код с копированием, таблицы, картинки | | `client/src/main/kotlin/pw/binom/client/ui/MarkdownRenderer.kt` | 25 | обёртка: открытие ссылки в браузере, `parseMultiline` — **образец использования** | ### Как переносить на Android Модуль `client-shared` собран под **Compose Desktop** (JVM) — целиком как зависимость его взять нельзя, на телефоне не соберётся. Поэтому **файлы копируются в наш модуль**, а не подключаются. Проверено по коду — переносятся почти дословно: - `MarkdownSegment.kt`, `MarkdownParserImpl.kt` — **ни одной строки** под JVM, Swing, AWT или Skiko. Чистая логика; - `MarkdownRendering.kt` — только `androidx.compose.*` и **ни одного диалога** и ни одного оконного примитива. Единственная платформенная мелочь — `LocalClipboardManager` (для копирования кода), на Android он есть. **Переделать придётся только одно:** открытие ссылок. В четвёртом файле это `java.awt.Desktop.getDesktop().browse(...)` — **на телефоне AWT нет**. Вместо этого системный переход: ```kotlin context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri())) ``` ### Зависимость Одна, и она уже есть в нашем Nexus: ```kotlin implementation("ru.otpbank.ai:markdown:0.51.0") ``` Плюс репозиторий в `build.gradle.kts` — без него не соберётся: ```kotlin maven { url = uri("http://192.168.76.117/repository/developerspace-prod-mvn-hosted/") isAllowInsecureProtocol = true } ``` ### Что из assistent НЕ берём `MessageBubble.kt` (789 строк, пузырь сообщения целиком) — тянет за собой модуль `chat-common`. Нам он не нужен: пузырь у нас свой, из макетов. Архитектуру, слои, готовые экраны (`ChatScreen`, `SettingsDialog`) и модули `chat-*` — **тоже не берём**, у нас своё (границы заимствования разобраны в десктопном репозитории: `BORROW-FROM-ASSISTENT.md`). ### Приёмы отрисовки — самое ценное В десктопном репозитории разобраны чужие грабли с указанием мест в коде: `BORROW-FROM-ASSISTENT.md`, раздел «Приёмы отрисовки». Читать **до** переноса. Главная из них: `Box(horizontalScroll)` внутри контейнера с бесконечной максимальной шириной (`wrapContentWidth`) роняет Compose с ошибкой `measured with an infinity maximum width` — таблицы размечаются парой разных режимов. На телефоне экран узкий, и это особенно чувствительно. **Состояние:** файлы ещё не перенесены — это отдельная задача. Переносить из `ai/assistent` коммита `2c5b3f9` (свежее проверять там же). ## 4. Макеты экранов — свои, но выросли из десктопных `design/mockups/` — пятнадцать экранов, по одному состоянию на файл. Рисовались как продолжение одобренного десктопного дизайна (`agentik-desktop/approved/001-sidebar-utility.html` — «привычный мессенджер»), поэтому палитра, значки и порядок экранов совпадают с десктопом. Чем отличаются — `PRINCIPLES.md`. Кратко: телефон всегда узкий, вместо «Назад внутри узкого окна» — системная кнопка и жест, вместо модальных окон — шторки снизу. ## 5. Принципы — не копируем, ссылаемся Принципы (что видно на экране, группы, агенты, непрочитанные, «нет данных — нет цифры», сообщения как источник правды) описаны в десктопном репозитории: `REQUIREMENTS.md`, `approved/README.md`, `THEMES.md`, `STORAGE.md`, `CACHE.md`. **Копий не делаем** — один источник правды лучше двух, которые разъедутся. Отличия Android — в `PRINCIPLES.md`. --- ## Про доступы Адрес и токен агента — **настройки приложения**, вводятся человеком, в коде репозитория их нет и быть не должно. В историю коммитов секреты не попадают.