- C 91.1%
- Python 8.6%
- Makefile 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| mock | ||
| src | ||
| test | ||
| .gitignore | ||
| Makefile | ||
| README.md | ||
| tui18oct26.png | ||
another-im-tui
Терминальный XMPP-клиент на libtacky: весь протокол, хранилище,
OMEMO и переподключения живут в backend'е dist/tackyd-json, который клиент
запускает подпроцессом и говорит с ним JSON-кадрами
(<байты>\n<json>, запрос ["module","method",{args},token]).
Здесь — только ncurses-интерфейс и состояние экрана, на C11.
Работает по ssh: ничего, кроме обычного TTY, не нужно.
Сборка и запуск
Нужны: gcc, ncursesw (libncursesw-dev), python3 для тестов, и собранный
backend (make tackyd-json MEDIA_BACKEND=none в чекауте libtacky — собирается
в его docker-образе).
make # bin/another-im-tui
make test # json/wire-тесты + смоуки UI (мок и настоящий backend)
make run # с backend'ом из ../libtacky/dist/tackyd-json
./bin/another-im-tui /путь/к/tackyd-json # или так; или TACKY_BACKEND=...
./bin/another-im-tui mock/mockd.py # мок вместо backend'а, для разработки
Профиль (журнал backend'а, база, ключи): ~/.local/share|.cache|.config/another-im-tui,
либо всё в одном месте через AIT_HOME=/путь.
Клавиши и команды
| ↑ / ↓, колесо мыши | прокрутка ленты сообщений |
| PgUp / PgDn, клик мышью по ростеру | переход по чатам — открывает сразу; черновик ввода остаётся за чатом |
| Ctrl+Tab / Ctrl+Shift+Tab | то же листание чатов — в терминалах, отличающих их от Tab (kitty, foot, WezTerm, xterm с modifyOtherKeys); в прочих Ctrl+Tab приходит как обычный Tab |
| Enter (пустой ввод) | открыть выбранный чат (после /open) |
| Enter | compose-режим (умолчание): перенос строки, два Enter подряд — отправить; send-режим: отправить (меняется в /conf). Команды /… выполняются первым Enter всегда. С курсором на сообщении — ответ цитатой |
| Alt+Enter, Ctrl+J | перенос строки всегда |
| ↑ / ↓ при многострочном вводе | курсор по строкам текста (чат листается колесом) |
| Ctrl+V | картинка из буфера обмена: экран превью ~70% терминала, Enter — отправить, Esc — отмена |
| Ctrl+↑ / Ctrl+↓ | курсор по сообщениям (для цитаты и копии) |
| Shift+↑ / Shift+↓ | растянуть выделение сообщений |
| Ctrl+Space | отметить/снять сообщение (Shift+Space терминал не отличает от пробела) |
| Ctrl+C | скопировать выделенное: локально wl-copy/xclip/xsel, параллельно OSC 52 (сквозь ssh и tmux); в копию входят цитата реплая (> автор: …) и описание файла; уровень copy_media в /conf: 0 — только описание, 1 — с хешем (умолчание), 2 — ещё и секстантная мозаика картинки; без выделения: дважды подряд — выход |
| Ctrl+Q, /quit | выход |
| ← / → · Home / End (Ctrl-A/E) | курсор в строке ввода |
| Ctrl+← / Ctrl+→ | на слово влево/вправо |
| Ctrl+Backspace (Ctrl-W, Alt+Backspace) | стереть слово |
| Delete | удалить символ под курсором |
| Esc | снять выделение; затем — очистить ввод |
| Ctrl+S | сменить свой статус (подставляет /status [away|dnd|xa|chat|online] текст; статус сохраняется и переприменяется при reconnect) |
| Ctrl+B / Ctrl+Y / Ctrl+U | маркеры разметки XEP-0393: *жирный* / _курсив_ / ~зачёркнутый~ (подчёркивания в XMPP-разметке нет; Ctrl+I не используется — в терминале это Tab) |
| Ctrl+K | стереть весь ввод |
| F12 | отдать/забрать мышь: выкл — родное выделение терминала без Shift (как в bash), вкл — наши клики и колесо |
Ростер — три строки на чат: имя (+непрочитанные), превью последнего сообщения, время/дата у правого края с галками доставки справа от них. Слева аватарка 6×3 клетки: настоящая картинка контакта (avatar metadata/data, PNG/JPEG через stb_image) секстантами 12×9 «пикселей» — truecolor или 256 цветов; без аватарки — квадрат цвета XEP-0392 с инициалом. Работает в любом юникодном терминале, включая ssh.
Шапка открытого чата — тоже три строки с той же аватаркой: имя; состояние
связи и N devices (1:1, из presence resources) или N participants
(конференция, из muc occupants); третья строка — по приоритету:
«печатает…» (XEP-0085, в комнате — с никами), статусный текст
собеседника, last seen M minutes ago (idle по XEP-0319). Свои
«печатает…» клиент шлёт с троттлингом 4 с, пустеющий ввод отзывает их.
Команды: /account add jid@сервер пароль — добавить аккаунт, /account list;
/add jid — заявка в друзья (ростер); /open jid — открыть чат;
/conf — экран настроек: свои (высота превью, режим Enter; tui.conf)
и все настройки backend'а (модуль setting: числа крутятся ←/→, строки
правятся через Enter→/set ключ значение); /omemo — свой device id и
отпечаток; /quit. Поле ввода многострочное, 2–8 строк, растёт с текстом.
Сообщения: исходящие прижаты вправо (мета 🔒 02:37 ✓✓ у нижней
строки текста,
галки зелёные только у прочитанных — backend шлёт это как
remote_status: "read"), входящие слева. Картинки-вложения
показываются секстантным превью (высота настраивается в /conf), под ними
строка ⤓ файл — клик или Ctrl+O открывает (xdg-open; AIT_OPENER
переопределяет), Ctrl+P копирует локальный путь в буфер — удобно по ssh.
OMEMO при этом уже работает: libtacky включает его сам, когда у собеседника есть ключи; зашифрованные строки помечены 🔒. «Прочитано» синхронизируется на другие устройства (XEP-0490/0333) при открытии чата.
Устройство
src/json.c разбор/сборка JSON под wire-протокол (без внешних библиотек)
src/wire.c подпроцесс backend'а + кадрирование
src/main.c состояние (аккаунт, чаты, лента) + ncurses + event loop (poll)
mock/mockd.py мок backend'а: история, эхо, события — для разработки без сборки libtacky
test/test_wire.c json + кадрирование против мока
test/ui_smoke.py клиент в pty против мока: /add → список → история → отправка → эхо
test/backend_smoke.py, test/real_ui_smoke.py то же с настоящим tackyd-json
Под сообщением видны реакции («👍2», своя — в скобках) и пометка ✎ edited;
прокрутка к верху ленты подгружает старые страницы истории; сообщения в
чужих чатах дают звонок терминала и notify-send (режим в /conf); открытый
чат объявляется backend'у через chats show/hide, так что по нему
уведомлений нет. Замок — текстовый 🔒︎ (U+FE0E).
Дальше по плану: поиск (message search), переход по цитате (goto_reply),
день-разделители, доверие OMEMO (trust_list/accept_identity), экран
аккаунтов и мультиаккаунт; затем kitty/sixel-уровни картинок и аудиозвонки
(сборка backend'а с MEDIA_BACKEND=datachannel).
