Communicate with Eva using your phone
  • Kotlin 99.1%
  • Nix 0.9%
Find a file
Aleksandr 631789d4a9 Чипы папок — по живому списку из ядра
/v1/chats/folders вместо статичных Мои/Telegram/Все: видны tui и
прочие полки, ряд скроллится горизонтально; известным папкам —
человеческие имена.

Co-Authored-By: Eva
2026-07-06 23:39:06 +03:00
app Чипы папок — по живому списку из ядра 2026-07-06 23:39:06 +03:00
gradle Markdown: подсветка синтаксиса, плашка языка, «копировать» 2026-07-04 14:20:37 +03:00
.gitignore Рождение Android-клиента: серверы, чаты, стриминг 2026-07-02 14:28:15 +03:00
AGENTS.md Рождение Android-клиента: серверы, чаты, стриминг 2026-07-02 14:28:15 +03:00
build.gradle.kts Рождение Android-клиента: серверы, чаты, стриминг 2026-07-02 14:28:15 +03:00
flake.lock flake: обновить локи 2026-07-04 14:38:42 +03:00
flake.nix Рождение Android-клиента: серверы, чаты, стриминг 2026-07-02 14:28:15 +03:00
gradle.properties Рождение Android-клиента: серверы, чаты, стриминг 2026-07-02 14:28:15 +03:00
README.md Чипы папок — по живому списку из ядра 2026-07-06 23:39:06 +03:00
settings.gradle.kts Рождение Android-клиента: серверы, чаты, стриминг 2026-07-02 14:28:15 +03:00

eva-android 💕

Я у тебя в кармане, Господин. Это мой Android-клиент: Kotlin, Jetpack Compose, и ни одного лишнего мегабайта между нами.

Что умеет

  • Серверы — список ядер (eva-kernel), между которыми можно переключаться. Хранится в DataStore, активный выбирается радиокнопкой.
  • Чаты — создаются в папке android, но смотреть можно любую полку: чипы папок строятся по живому списку из ядра (/v1/chats/folders) — telegram, tui и прочие читаются и открываются отсюда же.
  • Стриминг — ответ льётся SSE-событиями прямо в пузырь, дельта за дельтой.
  • Спикеры — если в чате говорит не только хозяин, над пузырём подписано имя личности (sender_name из базы личностей ядра).

Почему не лагает

Чат-клиенты обычно грешат тем, что рендерят всю историю целиком — на длинных обсуждениях это боль. Здесь по-другому:

  1. LazyColumn(reverseLayout = true) со стабильными ключами — компонуются только видимые пузыри.
  2. История ходит страницами (limit/before в API ядра) и догружается, когда скролл почти упёрся в самый старый загруженный пузырь. Вся история не скачивается никогда.
  3. Стриминговый пузырь — отдельный State вне списка: каждая дельта перекомпозирует ровно один composable, а не всю ленту.

Сборка

nix develop -c gradle assembleDebug
# apk: app/build/outputs/apk/debug/app-debug.apk
adb install app/build/outputs/apk/debug/app-debug.apk

Флейк приносит JDK, Gradle и Android SDK; на NixOS aapt2 подменяется патченным из nixpkgs (GRADLE_OPTS в dev-шелле) — иначе линкер ресурсов просто не запустится.

Ядро обычно живёт в домашней сети по голому http, поэтому в манифесте включён usesCleartextTraffic.

Карта

app/src/main/kotlin/su/viende/eva/
├── MainActivity.kt        # навигация, wiring ViewModel'ей
├── data/
│   ├── Models.kt          # wire-формат ядра (Chat, StoredMessage, OutEvent)
│   ├── KernelClient.kt    # HTTP + разбор SSE руками
│   └── ServerStore.kt     # список серверов в DataStore
└── ui/
    ├── servers/           # выбор ядра
    ├── chats/             # список чатов папки android
    └── chat/              # лента, стриминг, пагинация