From 72f4258410070f7bc7cd70d3b63e7023a58ba64d Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 28 Oct 2026 00:32:04 +0100 Subject: [PATCH] Replace stale desktop architecture documentation and honor the temporary directory --- src/utun_instance.c | 12 +- tools/chatgui/chatgui_doc.md | 333 ++++++++++++----------------------- 2 files changed, 126 insertions(+), 219 deletions(-) diff --git a/src/utun_instance.c b/src/utun_instance.c index 1ea6e50e..afc22f38 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -403,7 +403,17 @@ struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct // Create instance from config text string (writes to temp file, parses, cleans up) struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text) { if (!ua || !config_text) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "instance create requires uasync and config text"); return NULL; } - char tmp_path[] = "/tmp/utun_cfg_XXXXXX"; + const char* tmp_dir = getenv("TMPDIR"); +#ifdef _WIN32 + if (!tmp_dir || !tmp_dir[0]) tmp_dir = getenv("TEMP"); + if (!tmp_dir || !tmp_dir[0]) tmp_dir = getenv("TMP"); +#endif + if (!tmp_dir || !tmp_dir[0]) tmp_dir = "/tmp"; + char tmp_path[1024]; + int path_len = snprintf(tmp_path, sizeof(tmp_path), "%s/utun_cfg_XXXXXX", tmp_dir); + if (path_len < 0 || (size_t)path_len >= sizeof(tmp_path)) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "temporary config directory path too long"); return NULL; + } int fd = mkstemp(tmp_path); if (fd < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "cannot create temporary config: %s", strerror(errno)); return NULL; } size_t len = strlen(config_text); diff --git a/tools/chatgui/chatgui_doc.md b/tools/chatgui/chatgui_doc.md index efb52785..37f0a93a 100644 --- a/tools/chatgui/chatgui_doc.md +++ b/tools/chatgui/chatgui_doc.md @@ -1,253 +1,150 @@ -# chatgui +# Desktop chatgui -## 1. Назначение +## Запуск и сборка -Десктопный GUI-чат (Telegram-подобный) со встроенным P2P uTun-узлом. Приложение объединяет полнофункциональный интерфейс обмена сообщениями и децентрализованный транспортный слой в едином процессе. Рассчитан на работу без центральных серверов — вся маршрутизация, синхронизация и доставка сообщений идут напрямую между узлами по ETCP-протоколу. +Приложение называется `vibechat`: Qt Widgets UI и общее C-ядро в одном процессе. +Обычная desktop-сборка использует Qt Multimedia, FFmpeg и встроенный голосовой стек. +CMake требует C++20 для GUI и C99 для ядра; Qt 6 предпочтителен, предусмотрен Qt 5.15 fallback. +Зависимости и платформенные настройки находятся в `CMakeLists.txt` и `libutun/CMakeLists.txt`. -Язык интерфейса: русский или английский. По умолчанию используется первый поддерживаемый язык из списка языков интерфейса ОС; -если поддерживаемых языков нет, используется английский. Выбор в «Настройки → Профиль → Язык интерфейса»: -«Язык системы», English или Русский. Смена языка применяется после перезапуска, настройка хранится в `[gui] language=system|en|ru`. -Региональный формат дат и чисел остаётся системным. Диагностические дампы ядра сохраняют исходные имена полей. +Из корня репозитория: -Каталоги `translations/chatgui_en.ts` и `translations/chatgui_ru.ts` компилируются `lrelease` и встраиваются в приложение вместе -с `qtbase_ru.qm` для стандартных виджетов Qt. Для сборки нужны инструменты Qt Linguist и пакет переводов Qt -(Debian/Ubuntu: `qt6-tools-dev-tools`, `qt6-translations-l10n`; для Qt 5: `qttools5-dev-tools`, `qttranslations5-l10n`). -После добавления строк обновить каталоги из `tools/chatgui`: -`lupdate src -no-obsolete -locations relative -ts translations/chatgui_en.ts translations/chatgui_ru.ts`, -заполнить новые переводы и пересобрать. Проверка: `ctest --test-dir build -R test_gui_language --output-on-failure`. - -## 2. Архитектура - -### 2.1. Уровни приложения - -``` -┌──────────────────────────────────────┐ -│ GUI (Qt 6/5 Widgets, C++20) │ поток GUI -│ ├─ MainWindow (QSplitter) │ -│ ├─ ChannelList / MessageList │ -│ ├─ MessageDelegate (бабблы+реакции) │ -│ ├─ EmojiPanel (статика+анимация) │ -│ └─ LottieIcon / AnimTimer │ -├──────────────────────────────────────┤ -│ GuiBridge (Qt signals ↔ uasync) │ очередь событий -│ gui_bridge_post / uasync_post │ -├──────────────────────────────────────┤ -│ Транспорт (C, поток uasync) │ поток uasync -│ ├─ UtunNode (std::thread + UTUN_INSTANCE)│ -│ ├─ ChatCore (отправка, БД, invite) │ -│ ├─ ChatSync (P2P синхронизация) │ -│ ├─ MemberSync (мемберы через Merkle)│ -│ └─ MerkleSync (Merkle-дерево, 5 ур.)│ -├──────────────────────────────────────┤ -│ libutun (C, статическая библиотека) │ uasync -│ ├─ uasync (event loop, таймеры) │ -│ ├─ ETCP (надежная доставка, ACK) │ -│ ├─ SecureChannel (X25519+AES-CCM) │ -│ ├─ conn_mgr (управление подключ.) │ -│ ├─ topo_node/topo_group (BGP) │ -│ └─ ntp_time (синхронизация времени) │ -├──────────────────────────────────────┤ -│ SQLite3 (WAL mode) │ чтение+запись -│ ├─ DbManager (C++, read-only для GUI)│ -│ └─ chat_core (C, write из uasync) │ -└──────────────────────────────────────┘ +```sh +cmake -S tools/chatgui -B tools/chatgui/build +cmake --build tools/chatgui/build --target vibechat -j4 +./tools/chatgui/build/vibechat ``` -### 2.2. Потоковая модель +Корневой `build.sh` также собирает GUI при наличии `tools/chatgui/build/CMakeCache.txt`. +Скрипты desktop-сборки: `build.sh`, `build_nix.sh`, `build_nix_full.sh`, `build.bat` внутри этого каталога. +Native API и протоколы находятся в общем `../../src`; копий chat_core/chat_sync в `transport/` нет. -Приложение работает в двух потоках: +## Владельцы и потоки -- **Поток GUI** — QApplication, все виджеты, отрисовка, QTimer для анимаций (AnimTimer 30fps). Только *чтение* из SQLite через DbManager. -- **Поток uasync** — UTUN_INSTANCE в выделенном std::thread. Вся сеть, криптография, ETCP, синхронизация, *запись* в SQLite. Один uasync-экземпляр на поток (правило u_async). +- GUI-поток владеет QApplication, виджетами, моделями, Qt-таймерами и desktop media engines. +- `UtunNode` создаёт uasync и UTUN_INSTANCE в worker, явно запускает core/chat и медиапривязки. + `[gui] utun_enabled=yes` дополнительно запускает UTUN; создание TUN управляется отдельно. +- Сетевые операции, подписанные записи, чат и изменения данных SQLite выполняет uasync. + Декодеры, FFmpeg, media_async и audio callbacks работают в своих workers и не обращаются к QWidget. +- `start()` возвращает подтверждённую готовность либо причину отказа с откатом ресурсов. + Идентичность, NTP и фактический db_path читаются в GUI через защищённую копию состояния. +- `requestStop()` просит выход, `stop()` выполняется в GUI и присоединяет worker. + Worker перед destroy ждёт подтверждения `stopping` в GUI. В этом сигнале GUI снимает SQLite handle; + слоты не должны вызывать stop/join. Синхронный stop сам выполняет отсоединение до join. +- Instance уничтожает worker; uasync освобождается после join. Старый queued `stopped` отсеивается по поколению запуска. -**Мост между потоками (gui_bridge):** -- **uasync → GUI:** `gui_bridge_post(event_type, data, len)` — сериализованные события в Qt main event loop через `QMetaObject::invokeMethod` с `Qt::QueuedConnection`. 10 типов событий: MSG_RECEIVED, CONNECT_RESULT, NEW_PEER, CHANNEL_UPDATED, MEMBERS_CHANGED, MY_NODE_ID, AUTO_CONNECT_STATUS, CHANNEL_PEERS_ONLINE, DB_READY, STATUS_REFRESH. -- **GUI → uasync:** `gui_bridge_post_uasync_fn(fn, arg)` — выполнение функции в uasync-потоке через `uasync_post`. Используется для отправки сообщений, создания каналов, invite-подключений. +Штатный выход MainWindow ставит stop после принятых записей геометрии/read/scroll. +Закрытие крестиком сохраняет геометрию и скрывает окно в tray. +Перед native teardown очищаются выбранный target, ожидания auto-play и заимствованный handle БД. -Всё взаимодействие с БД (INSERT/UPDATE/DELETE) выполняется только из uasync-потока. GUI читает через DbManager (read-only), синхронизация не требуется — SQLite в WAL-режиме обеспечивает одновременное чтение. +## Мост GUI ↔ ядро -## 3. Ключевые компоненты +`transport/gui_bridge.h` описывает контракты; номера и форматы событий определяет `src/chat/chat_event.h`. -### 3.1. GUI-слой +Ядро отправляет `gui_bridge_post(type, data, len)`: байты копируются синхронно, +обработка и зарегистрированные callbacks выполняются через Qt queued connection в GUI. +Данные callback заимствованы только до возврата; строка с длиной не обязана иметь NUL. -**MainWindow** (`src/mainwindow.h`) — главное окно 900×600. Горизонтальный QSplitter из трёх панелей: -- **ChannelList** — список каналов (QListView + QStandardItemModel), контекстное меню (invite, join, create group, settings). -- **MessageList** — список сообщений канала (ChatView + QStandardItemModel), поле ввода InputBar, EmojiPanel. -- **AccountList** — список участников канала (MemberListModel), онлайн-статус, имена. +GUI использует API с владением запросами: -Системный трей: QSystemTrayIcon + QMenu (показать/скрыть/выход). Закрытие окна сворачивает в трей. +- `gui_bridge_post_setting` копирует key/value в ui_state либо live chat_setting. +- `gui_bridge_submit_message` и `gui_bridge_submit_dm_message` фиксируют composer request_id. + Результат локальной записи возвращается MSG_SUBMITTED; отмена до исполнения даёт отказ. +- `gui_bridge_submit_attachment` передаёт подготовку PCM/файла; отмена освобождает PCM owner, + compressor и собственный временный source. При отказе постановки caller вызывает `attachment_send_discard`. +- `gui_bridge_download_attachment` обслуживает delegate, menu и auto-play. +- Обычные звонки, приглашения в рацию, редактирование участников, member-files и JOIN имеют специализированные API. +- `gui_bridge_post_bound_fn` предназначен для оставшихся простых native-команд: + один u_malloc-блок без вложенных ресурсов и адрес его поля inst. + Проверка running, привязка к ядру и постановка выполняются под mutex. + При -1 caller освобождает блок; при принятии его освобождает trampoline либо мост при отмене. +- `gui_bridge_post_uasync_fn` оставляет arg заимствованным; он подходит только для данных + с отдельно гарантированным временем жизни. Чтение `gui_bridge_get_inst()` не резервирует ядро. -**Уведомления о сообщениях** (`src/message_notification.*`, `src/user_activity_monitor.*`): чужое новое сообщение -в канале или личной беседе показывает одно окно возле трея, если главное окно не активно. Загрузка истории не уведомляет: -`MSG_RECEIVED` содержит `[ch_id_len:1][ch_id:var][author_node_id:8][initial_sync:1]`. -Без активности уведомление остаётся; первый ввод запускает отсчёт 10 секунд, дальнейший ввод его не продлевает. -Новое сообщение заменяет содержимое и отменяет прежний отсчёт. Нажатие открывает соответствующий чат; крестик закрывает окно. +Detach закрывает приём и отменяет ожидающие команды до освобождения native-ресурсов. +Приём команды подтверждает enqueue, а не успех её последующего исполнения. -Windows использует `GetLastInputInfo`, macOS — CoreGraphics, X11 — опциональную библиотеку Xss (`libxss-dev`). -Wayland/XWayland в GNOME использует асинхронный `org.gnome.Mutter.IdleMonitor` через D-Bus. При нативном Qt Wayland -доступен также опциональный KF6/KF5 KIdleTime с установленным backend композитора; idle inhibitors могут ограничивать его события. -X11 idle time в Wayland-сессии не используется: он не гарантирует активность в других приложениях. -Если системный механизм недоступен, учитываются только события внутри ChatGUI; закрытие крестиком остаётся доступным. -При отсутствии координат трея выбирается угол доступной области экрана; нативный Wayland управляет позицией и порядком окон. -В X11/XWayland уведомление — отдельное overlay-окно (`override_redirect`) типа notification: -оконный менеджер не привязывает его к одному виртуальному столу, оно остаётся доступным при переключении столов. -Уведомление не является дочерним окном главного чата: сворачивание или рабочий стол главного окна не определяют его видимость. -На нативном Wayland поверхность пересоздаётся при новом сообщении, чтобы не сохранять прежний рабочий стол; -выбор текущего стола и возможность показа поверх других окон остаются политикой композитора. -Показ/скрытие и причины ограничений логируются в `general`; запуск отсчёта и первая активность — на уровне `debug`. -Проверка: `ctest --test-dir build -R 'test_message_(notification|events)' --output-on-failure`. -При доступном X11/XWayland: `./build/test_message_notification_x11` проверяет overlay после показа, обновления и скрытия. +## SQLite и профиль -**ChatView** (`src/chatview.h`) — QListView с фоновым изображением и drag-to-select. При движении >5px от точки нажатия стартует выделение сообщений. Ctrl+C копирует: для одного — выделенный текст, для нескольких — `[time] author: text`. Сигнал `hoveredIndexChanged` используется для активации анимаций эмодзи в MessageList. +Ядро владеет общей SQLite: `db_path/chats.db`, без db_path — in-memory. +Соединение открывается с FULLMUTEX, WAL и foreign_keys. +`DbManager` заимствует handle; обычные запросы GUI только читают и возвращают Qt-копии. +Каждый statement закрывается до возврата; ошибки bind/step/finalize не выдаются за частичные успешные снимки. +Низкоуровневый `prepareOrNull` отклоняет write statements, caller обязан завершить read statement до следующего GUI-события. +WAL не продлевает время жизни handle и не заменяет барьер stopping. -**MessageList / MessageDelegate** — баббл-интерфейс сообщений: -- Аватары (32×32, цвет зависит от node_id), бабблы со скруглёнными углами (r=10px) и хвостиком. -- Цитаты: цветная полоса + мини-аватар + автор + текст. -- Реакции (эмодзи) в status bar под бабблом. -- Inline-анимированные эмодзи: при hover над сообщением — перебор кадров из LottieIcon, отрисовка поверх текста через QTextLayout. -- Растягивающийся InputBar (QTextEdit, Enter — отправка, Shift+Enter — перевод строки). +Основные данные: `channels`, `msg_`, `peers_`, `nodes`, `node_addresses`, +`accounts`, `ui_state`, DM history/media и attachment keys. +Сообщение имеет подписанный 32-байтный message_id, версию и reply_to. +Локальный rowid применяется для read/scroll; timestamp/message_id определяют порядок истории. +История PM имеет generation: старый снимок или результат удаления не применяется к новой истории. -Карточка опроса открывает «Голосование / результаты» одиночным левым кликом по телу сообщения. -Перетаскивание выделяет текст; аватар и строка реакций обрабатывают свои действия. +В DbManager нет устаревшего sync-протокола, долгоживущих sync-курсоров и GUI-удаления peers. +Синхронизация принадлежит общему ядру: `src/chat`, `src/routing_layer`. +Подробные wire-контракты находятся в заголовках этих модулей и документах `../../doc`. -**EmojiPanel** (`src/emojipanel.h`) — QTabWidget с категориями (смайлики, жесты, символы, анимации). Каждая категория — QGridLayout из EmojiButton (QPushButton без Q_OBJECT, hover-анимация через per-widget QTimer). Статические эмодзи — Unicode символы. Анимированные — TGS-файлы, рендерятся в EmojiButton::paintEvent. +PRAGMA quick_check/integrity_check проверяют текстовый ответ SQLite, optimize оценивает SQL-успех +без требования строки результата. Обслуживание синхронно; handle сохраняется живым до возврата. +Оно не меняет подписанные сообщения/членство. -**LottieIcon** (`src/lottieicon.h`) — загрузка и рендер TGS-анимаций: -1. Gzip-декомпрессия через zlib (`inflateInit2` с `16+MAX_WBITS`) -2. Парсинг Lottie JSON через rlottie C API (`lottie_animation_from_data`) -3. Рендер кадра: `lottie_animation_render` → ARGB32 буфер → QImage +Фактический каталог профиля публикует UtunNode после применения override. +GUI использует его для media; пустой native db_path означает текущий каталог. +Идентичность INI нормализует общий config_updater, сохраняя корректный private key. +NodeConfig хранит GUI-проекцию, сетевые секции проверяет общий parser в worker. -5 встроенных TGS-анимаций (вшиты в бинарник через chatgui.qrc): sparkles, white_flag, greeting, stop_hand, robot. +## Модель и отрисовка сообщений -**AnimTimer** (`src/animtimer.h`) — синглтон-таймер 30fps с refcount-механизмом: -- `activate()` / `deactivate()` увеличивают/уменьшают счётчик ссылок. -- Срабатывает только когда есть активные анимации в видимых сообщениях. -- Сигнал `ticked` обновляет кадры всех зарегистрированных LottieIcon. +`MainWindow` связывает ChannelList, MessageList, AccountList, tray, media windows и bridge. +`MessageList` владеет моделью, composer и панелями; db/recorder заимствованы. +Основная история ограничена 200 строками. Навигация по reply загружает окно вокруг оригинала; +пока это окно открыто, новые сообщения не вытесняют его и не двигают read watermark свежей истории. -**Invite-ссылки** (`src/invite_link.h`) — кодирование/декодирование приглашений в формат `utun://...` (base64, содержит channel_id, pubkey, список адресов). QR-коды через zxing-cpp. +`MessageDelegate` использует один layout для sizeHint, paint и hit-test. +Аватар имеет размер 54px; узкий режим убирает его. Поддерживаются текст, цитаты, реакции, +опросы, voice, файлы, изображения, видео и состояния encrypted-вложений. +Медиа открывается левым кликом по телу; drag или release без соответствующего press не запускает действие. +QPersistentModelIndex и QPointer защищают от замены модели и уничтожения UI в модальном цикле. +Опросы и реакции реализованы в `src/messagefeedback.cpp`; канал диалога фиксируется до exec. -### 3.2. Транспортный слой +PCM-позиция voice берётся из SoundManager. Таймер обновляет состояние вне paint, +останавливается после затухания/паузы и не удерживает уничтоженный view. +VoicePlayer отменяет устаревшую подготовку; загруженные PCM-сессии принадлежат SoundManager. +Auto-play не реагирует на initial sync/собственные сообщения, подавляет повторы и различает (channel,rowid). +Encrypted voice распознаётся по проверенным metadata/plaintext после decrypt, неизвестный MIME не считается voice. -**UtunNode** (`transport/utun_node.h`) — C++ обёртка над UTUN_INSTANCE: -- В `runLoop()` создаёт uasync и UTUN_INSTANCE, явно запускает core/chat и проверяет медиапривязки; `[gui] utun_enabled=yes` добавляет UTUN. -- `start()` подтверждает готовность или возвращает причину отказа с откатом ресурсов. Команды и события чата проходят через GuiBridge. -- `stop()` просит выход из poll, ждёт worker; instance освобождает worker, затем GUI освобождает uasync. -- Идентичность и NTP доступны GUI через защищённую копию состояния, без чтения живых структур ядра. -- Перед чтением существующего профиля desktop вызывает общий `config_ensure_keys_and_node_id()`: корректный X25519 private key сохраняется, public key и node_id восстанавливаются из него. +Превью видео создаёт ограниченный QThreadPool; paint не запускает декодеры. +Сетевые загрузки и подготовка вложений принадлежат native media_delivery/media_async. +Обычный phone call и адресное приглашение в групповую рацию используют разные сервисы; +FINISHED приглашения в рацию завершает приглашение, дальнейшая рация живёт обычным циклом. -**NodeConfig** (`transport/node_config.h`) — управление INI-конфигом uTun-узла: -- `create(name)` создаёт новый профиль с X25519-ключами, производным node_id, автосокетами TCP+UDP и control 127.0.0.1:9999. Ошибки генерации/сохранения возвращаются вызывающему. -- `load()` читает собственную GUI-проекцию без DNS и заменяет её целиком; удалённые поля не остаются от предыдущего чтения. Ошибки полей доступны через `hasErrors()/errors()`. -- Сетевые секции, включая `[server:name]` и `[client:name]`, проверяет общий парсер в потоке ядра. GUI не содержит отдельного списка допустимых сетевых опций. -- `save()` обновляет поля идентичности, control и `[gui]`/`[debug]`, сохраняя остальные секции и комментарии. Неиспользуемый режим полной перезаписи из неполной модели удалён. -- ConfigUpdater проверяет UTF-8/физический INI-формат и публикует весь набор правок через QSaveFile; replace обновляет все дубли, append добавляет значение. Отказ записи не усекает исходный файл. +## Анимации, уведомления и язык -**ChatCore** (`transport/chat_core.h`) — центральный API чата в потоке uasync: -- `chat_core_submit_message` — отправка сообщения: сохранение в локальную БД (таблица `msg_`), рассылка онлайн-пирам через etcp_send. -- `chat_core_create_channel` — создание канала: генерация X25519/Ed25519 ключей канала, подпись Ed25519, запись в `channels`, членство владельца. -- `chat_core_connect_from_invite` — подключение к пиру из invite-ссылки. -- `chat_core_connect_auto` — авто-подключение к узлу из SQLite (topo_node), коллбэк с результатом. -- Работа с БД: цепочка хешей (chain hash = SHA256(prev_hash || timestamp || data_hash)), список каналов, список пиров. +AnimTimer управляет общими LottieIcon; регистрация выполняется до EmojiPanel, shutdown — до очистки иконок. +TGS декомпрессируется zlib и рендерится rlottie. Unicode emoji и ресурсы перечислены в `src/emoji.cpp` и `resources/chatgui.qrc`. -**ChatSync** (`transport/chat_sync.h`) — P2P синхронизация каналов: -- ETCP-сервисы: `0x30` (chat_sync — синхронизация каналов), `0x31` (member_sync — синхронизация участников). -- Протокол сообщений: INIT_SYNC → INIT_RESP → SEND_DATA ↔ PUSH+ACK_PUSH → SYNC_DONE. -- Batch-загрузка: до 32 сообщений за запрос, sparse-синхронизация (до 16 диапазонов). -- Таймаут пира: 5 секунд. -- Auto-connect: параллельное подключение до 10 узлов, останавливается при 3 успешных. Переключение группы при смене канала. +Окно нового сообщения показывает MessageNotification, активность пользователя отслеживает UserActivityMonitor. +Нотификация не показывается для своего сообщения, initial sync и активного главного окна. +Ограничения размещения/показа определяет compositor; детали — в заголовках этих компонентов. -**MemberSync** (`transport/member_sync.h`) — синхронизация участников канала на основе Merkle-деревьев: -- Данные: таблицы `peers_`, `nodes`, `node_addresses`. -- Хеш мембера = SHA256(node_id || x25519 || ed25519 || join_sig || join_ts || update_sig || update_ts || addrs). -- Автоматический пересчёт дерева при добавлении/удалении/обновлении мембера. -- Онлайн-статус: `member_sync_set_online(node_id, 0/1)`. +Язык выбирается через `[gui] language=system|en|ru`; смена применяется после перезапуска. +Русский и английский каталоги компилирует lrelease, инструменты Linguist обязательны. +Региональные форматы дат/чисел остаются системными. +Новые UI-строки обновляются из `tools/chatgui`: -**MerkleSync** (`transport/merkle_sync.h`) — универсальный протокол синхронизации на Merkle-деревьях: -- 5-уровневое префиксное дерево над 64-битными ключами (node_id). 32 бакета на уровень (по 5 бит). -- SHA256-хеши бакетов. Два пира обмениваются хешами, находят различающиеся бакеты, передают только их. -- Wire-протокол: MSG_HASHES → MSG_REQUEST → MSG_BATCH, рекурсивно до совпадения хешей. -- Таймаут 10s, 3 ретрая. Фоновая проверка консистентности (`merkle_sync_bg_check`). -- Не зависит от типа данных — потребитель предоставляет три коллбэка (update_bucket_hash, get_items, apply_items). member_sync адаптирует это под мемберов каналов. - -### 3.3. База данных - -**DbManager** (`db/db_manager.h`) — C++/Qt обёртка над SQLite3 (read-only для GUI-потока): -- **Схема:** `channels` (каналы с ключами X25519/Ed25519, подписи), `msg_` (per-channel таблицы сообщений с chain_hash), `peers_` (участники), `nodes` (узлы), `node_addresses`, `accounts`, `ui_state`, `merkle_tree_hash`. -- Чтение: `getChannels()`, `getMessages()`, `getChannelMembers()`, `loadNodeInfo()`, `getAccounts()`. -- Методы для GUI: список каналов, сообщения с пагинацией, участники, онлайн-статус. -- Sync-операции (курсоры): `syncListChannels()`, `syncCount()`, `syncChainHashAt()`, `syncCursorOpen/Next/Close()`. -- Проверка целостности: `integrityCheckQuick()` (PRAGMA quick_check), `integrityCheckFull()` (PRAGMA integrity_check), `integrityOptimize()` (PRAGMA optimize). - -## 4. Сборка - -**Система сборки:** CMake 3.16+ -**Стандарты:** C++20 (GUI), C99 (транспорт/libutun) -**Зависимости:** -- Qt 6 (Widgets + Network) или Qt 5.15+ -- rlottie (анимированные эмодзи) -- zlib (gzip-декомпрессия TGS) -- OpenSSL (X25519, Ed25519, SHA256, AES-CCM) -- zxing-cpp (QR-коды, встроен как submodule) - -**Структура сборки:** -- `libutun/` — статическая библиотека uTun: все исходники из `src/` (кроме utun.c) + lib/ (uasync, ll_queue, memory_pool, etc) + транспорт (chat_sync, member_sync, merkle_sync) -- `chatgui` — финальный бинарник: GUI-исходники + транспортные C++/C файлы + SQLite3 amalgamation -- Платформенный TUN: `tun_linux.c` / `tun_freebsd.c` / `tun_windows.c` - -```bash -mkdir -p build && cd build -cmake .. && make -j4 -./chatgui +```sh +lupdate src -no-obsolete -locations relative -ts translations/chatgui_en.ts translations/chatgui_ru.ts ``` -## 5. Файловая структура +## Проверки +Состав целей и окружение тестов определяет CMakeLists.txt. Основные команды: + +```sh +cmake --build tools/chatgui/build --target vibechat test_desktop_commands test_utun_node -j4 +ctest --test-dir tools/chatgui/build -R 'test_(desktop_commands|utun_node|message_events|channel_replies|database_maintenance)' --output-on-failure ``` -tools/chatgui/ -├── CMakeLists.txt # проект CMake -├── build.sh / build.bat # скрипты сборки -├── src/ # GUI-слой (Qt C++) -│ ├── main.cpp # точка входа, QApplication -│ ├── mainwindow.cpp # главное окно, трей -│ ├── channellist.cpp # список каналов -│ ├── chatview.cpp # QListView с фоном и drag-to-select -│ ├── messagelist.cpp # модель сообщений, инициализация анимаций -│ ├── messagedelegate.cpp # отрисовка бабблов, цитат, реакций, эмодзи -│ ├── inputbar.cpp # поле ввода + кнопка эмодзи -│ ├── emojipanel.cpp # QTabWidget с категориями эмодзи -│ ├── emojitabbar.cpp # кастомный QTabBar -│ ├── emoji.cpp # данные эмодзи, builtinEmojiSet() -│ ├── lottieicon.cpp # загрузка TGS, рендер через rlottie -│ ├── animtimer.cpp # синглтон-таймер 30fps с refcount -│ ├── accountlist.cpp # список участников канала -│ ├── memberlistmodel.cpp # модель для AccountList -│ ├── invite_link.cpp # кодирование/декодирование utun:// ссылок -│ ├── invitedialog.cpp # диалог приглашения -│ ├── joindialog.cpp # диалог входа по invite-ссылке -│ ├── settingsdialog.cpp # окно настроек -│ ├── networksettingspage.cpp # страница сетевых настроек -│ ├── databasesettingspage.cpp # настройки БД -│ ├── statuspage.cpp # страница статуса -│ ├── creategroupdialog.cpp # создание группы -│ └── qrcode_utils.cpp # генерация QR-кодов -├── transport/ # транспортный слой (C/C++) -│ ├── utun_node.cpp # C++ обёртка UTUN_INSTANCE в std::thread -│ ├── node_config.cpp # INI-конфиг узла (ключи, серверы, клиенты) -│ ├── config_updater.cpp # утилита редактирования INI-файлов -│ ├── gui_bridge_impl.cpp # мост uasync↔Qt (10 типов событий) -│ ├── chat_core.c # центральный API чата (отправка, БД, invite) -│ ├── chat_sync.c # P2P синхронизация каналов (ETCP 0x30) -│ ├── member_sync.c # синхронизация мемберов через Merkle -│ └── merkle_sync.c # универсальный Merkle-протокол синхронизации -├── db/ # база данных -│ ├── db_manager.h/cpp # C++/Qt обёртка SQLite3 (read-only для GUI) -│ └── sqlite3.c/h # SQLite3 amalgamation (из ../../lib/) -├── libutun/ # сборка статической библиотеки uTun -│ └── CMakeLists.txt # uasync + utun = все .c из lib/ и src/ -├── resources/ # ресурсы -│ ├── chatgui.qrc # Qt resource file (TGS вшиты в бинарник) -│ └── animations/ # TGS-анимации (5 шт.) -└── doc/ # документация - ├── desc.txt # описание схемы БД - └── db_schema.md # детальная схема таблиц SQLite -``` + +Linux-тесты команд используют allocation hooks и null audio backend с обычной Multimedia-сборкой. +Это позволяет проверить очередь, native trampolines, SQLite и QWidget без физических audio devices. +Категории диагностики определяются `lib/debug_config.h`; используйте уровни config/chat/dm/media/radio/sys +для ошибок, при проверке состояний повышайте нужную категорию до debug.