- design/mockups/ — 15 макетов (одно состояние = один файл), из /tmp перенесены в репо - design/screenshots/ — те же экраны PNG - design/PRINCIPLES.md — принципы: что берём у десктопа, что на телефоне иначе - design/BORROW.md — что откуда: ядро (Nexus), тема (десктопный макет), отрисовка Markdown (ai/assistent) - design/gen_mockups.py — генератор макетов, путь вывода теперь рядом с ним - README: разделы «Принципы» и «Откуда код» со ссылками
8.9 KiB
Что откуда взято
Правило простое: чужое не переписываем заново, а берём и говорим, откуда взяли. Ниже по каждому куску: откуда, что именно, и что с ним делать на телефоне.
| Куски кода | Откуда | Состояние |
|---|---|---|
| Ядро агента | 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:
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<MarkdownSegment> |
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 нет. Вместо
этого системный переход:
context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
Зависимость
Одна, и она уже есть в нашем Nexus:
implementation("ru.otpbank.ai:markdown:0.51.0")
Плюс репозиторий в build.gradle.kts — без него не соберётся:
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.
Про доступы
Адрес и токен агента — настройки приложения, вводятся человеком, в коде репозитория их нет и быть не должно. В историю коммитов секреты не попадают.