Files
telegram-tui/CONTEXT.md
Mikhail Kilin 8bd08318bb
Some checks failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
fixes
2026-02-14 17:57:37 +03:00

245 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Текущий контекст проекта
## Статус: Фаза 14 — Мультиаккаунт (IN PROGRESS)
### Оптимизация: Ленивая загрузка сообщений при открытии чата (DONE)
Чат открывается мгновенно (< 1 сек) вместо 5-30 сек для больших чатов.
**Проблема**: `open_chat_and_load_data()` блокировал UI до полной загрузки ВСЕХ сообщений (`get_chat_history(chat_id, i32::MAX)`). Для чата с 500+ сообщениями это 10+ запросов к TDLib.
**Решение**:
- Загрузка только 50 последних сообщений (один запрос) → чат виден сразу
- Фоновые задачи (reply info, pinned, photos) — на следующем тике main loop через `pending_chat_init`
- Старые сообщения подгружаются при скролле вверх (существующий `load_older_messages_if_needed`)
**Модифицированные файлы:**
- `src/app/mod.rs` — поле `pending_chat_init: Option<ChatId>`
- `src/input/handlers/chat_list.rs``open_chat_and_load_data()`: 50 сообщений + `pending_chat_init`
- `src/main.rs` — обработка `pending_chat_init` в main loop (reply info, pinned, photos)
- `src/app/methods/navigation.rs`сброс `pending_chat_init` в `close_chat()`
---
### Bugfix: Авто-загрузка фото в чате (DONE)
Фото не отображались — отсутствовал код загрузки файлов после открытия чата.
**Проблема**: `extract_media_info()` создавал `PhotoInfo` с `PhotoDownloadState::NotDownloaded`, но никакой код не инициировал `download_file()`. Фото оставались в состоянии "📷 [Фото]" без inline превью.
**Исправление:**
- **Авто-загрузка при открытии чата**: после загрузки истории сообщений скачиваются фото из последних 30 сообщений (если `auto_download_images = true` и `show_images = true`). Каждый файл — с таймаутом 5 сек.
- **Загрузка по `v`**: вместо "Фото не загружено" — скачивание + открытие модалки. Также повторная попытка при `Error`.
- Обновление `PhotoDownloadState` в сообщении после успешной/неуспешной загрузки.
**Модифицированные файлы:**
- `src/input/handlers/chat_list.rs` — авто-загрузка фото в `open_chat_and_load_data()`
- `src/input/handlers/chat.rs``handle_view_image()`: download on NotDownloaded + retry on Error
---
### Этап 2+3: Account Switcher Modal + Переключение аккаунтов (DONE)
Реализована модалка переключения аккаунтов и механизм переключения:
- **Модалка `Ctrl+A`**: глобальный оверлей поверх любого экрана (Loading/Auth/Main)
- **Навигация**: `j/k` по списку, `Enter` выбор, `a` добавление, `Esc` закрытие
- **Переключение**: close TDLib → `recreate_client(new_db_path)` → auth flow
- **Добавление аккаунта**: ввод имени в модалке → валидация → `add_account()` → переключение
- **Footer индикатор**: `[account_name]` если не "default"
- **`AccountSwitcherState`**: enum `SelectAccount` / `AddAccount` — глобальный оверлей в `App<T>`
- **`recreate_client()`**: новый метод в `TdClientTrait` — close old → new TdClient → spawn set_tdlib_parameters
**Новые файлы:**
- `src/ui/modals/account_switcher.rs` — UI рендеринг (SelectAccount + AddAccount)
- `tests/account_switcher.rs` — 12 тестов
**Модифицированные файлы:**
- `src/app/mod.rs``AccountSwitcherState` enum, 3 поля (`account_switcher`, `current_account_name`, `pending_account_switch`), 8 методов
- `src/accounts/manager.rs``add_account()` (validate + save + ensure_dir)
- `src/accounts/mod.rs` — re-export `add_account`
- `src/tdlib/trait.rs``recreate_client(&mut self, db_path)` в TdClientTrait
- `src/tdlib/client.rs` — реализация `recreate_client` (close → new → set_tdlib_parameters)
- `src/tdlib/client_impl.rs` — trait impl делегирование
- `tests/helpers/fake_tdclient_impl.rs` — no-op `recreate_client`
- `src/input/main_input.rs` — account_switcher роутинг (highest priority)
- `src/input/handlers/global.rs``Ctrl+A` → open_account_switcher
- `src/input/handlers/modal.rs``handle_account_switcher()` (SelectAccount + AddAccount input)
- `src/ui/modals/mod.rs``pub mod account_switcher;`
- `src/ui/mod.rs` — overlay поверх любого экрана
- `src/ui/footer.rs``[account_name]` индикатор
- `src/main.rs``pending_account_switch` check в run_app, `Ctrl+A` из любого экрана
### Этап 1: Инфраструктура профилей аккаунтов (DONE)
Реализована инфраструктура для мультиаккаунта:
- **Модуль `accounts/`**: `profile.rs` (типы + валидация) + `manager.rs` (загрузка/сохранение/миграция)
- **`accounts.toml`**: конфиг списка аккаунтов в `~/.config/tele-tui/accounts.toml`
- **XDG data dir**: БД TDLib хранится в `~/.local/share/tele-tui/accounts/{name}/tdlib_data/`
- **Автомиграция**: `./tdlib_data/` → XDG path при первом запуске
- **CLI флаг `--account <name>`**: выбор аккаунта при запуске
- **Параметризация `db_path`**: `TdClient::new(db_path)`, `App::new(config, db_path)`
---
## Предыдущий статус: Multiline Message Display (DONE)
### Multiline в сообщениях
- **Multiline в сообщениях**: `\n` корректно отображается в пузырях сообщений (split по `\n` + word wrap)
- **Маркер выделения**: ▶ показывается только на первой строке multiline-сообщения
- Перенос строки в инпуте отключён (Shift+Enter/Alt+Enter/Ctrl+J не вставляют `\n`)
**Файлы изменены:**
- `ui/components/message_bubble.rs``wrap_text_with_offsets()` split по `\n` + `wrap_paragraph()` + selection marker fix
---
### Vim Normal/Insert Mode (DONE)
Реализован Vim-like режим работы с двумя состояниями:
- **Normal mode** (по умолчанию при открытии чата): навигация j/k, команды d/r/f/y, автоматический вход в MessageSelection
- **Insert mode** (нажать `i`/`ш`): набор текста, Esc возвращает в Normal
- Автопереключение в Insert при Reply (`r`) и Edit (`Enter`)
- Визуальные индикаторы: `[NORMAL]`/`[INSERT]` в footer, зелёная/серая рамка compose bar
- В Insert mode блокируются все команды кроме текстового ввода и Esc
**Файлы изменены:**
- `app/chat_state.rs` — enum `InputMode`
- `app/mod.rs` — поле `input_mode` в `App<T>`
- `config/keybindings.rs``Command::EnterInsertMode` + keybinding `i`/`ш`
- `app/methods/navigation.rs``close_chat()` сбрасывает input_mode
- `input/main_input.rs` — главный роутер Insert/Normal
- `input/handlers/chat.rs` — EnterInsertMode, auto-Insert при Reply/Edit
- `input/handlers/chat_list.rs` — auto-MessageSelection при открытии чата
- `ui/footer.rs` — mode indicator
- `ui/compose_bar.rs` — visual mode differentiation
- `tests/` — обновлены для нового поведения
---
## Предыдущий статус: Фаза 12 — Прослушивание голосовых сообщений (DONE)
### Завершённые фазы (краткий итог)
| Фаза | Описание | Статус |
|------|----------|--------|
| 1 | Базовая инфраструктура (ratatui + crossterm, vim-навигация) | DONE |
| 2 | TDLib интеграция (авторизация, чаты, сообщения) | DONE |
| 3 | Улучшение UX (отправка, поиск, скролл, realtime) | DONE |
| 4 | Папки и фильтрация (загрузка папок, переключение 1-9) | DONE |
| 5 | Расширенный функционал (онлайн-статус, медиа-заглушки, muted) | DONE |
| 6 | Полировка (60 FPS, память, graceful shutdown, динамический инпут) | DONE |
| 7 | Рефакторинг памяти (единый источник данных, LRU-кэш) | DONE |
| 8 | Дополнительные фичи (markdown, edit/delete, reply/forward, блочный курсор) | DONE |
| 9 | Расширенные возможности (typing, pinned, поиск в чате, черновики, профиль, копирование, реакции, конфиг) | DONE |
| 10 | Desktop уведомления (notify-rust, muted фильтр, mentions, медиа) | DONE (83%) |
| 11 | Inline просмотр фото (ratatui-image, кэш, загрузка) | DONE |
| 12 | Прослушивание голосовых сообщений (ffplay, play/pause, seek, ticker, cache, config) | DONE |
| 13 | Глубокий рефакторинг архитектуры (7 этапов) | DONE |
### Фаза 11: Inline фото + оптимизации (подробности)
Feature-gated (`images`), 2-tier архитектура:
**Базовая реализация:**
1. **Типы**: `MediaInfo`, `PhotoInfo`, `PhotoDownloadState`, `ImageModalState`, `ImagesConfig`
2. **Зависимости**: `ratatui-image 8.1`, `image 0.25` (feature-gated)
3. **Media модуль**: `ImageCache` (LRU), dual `ImageRenderer` (inline + modal)
4. **UX**: Always-show inline preview (фикс. ширина 50 chars) + полноэкранная модалка на `v`/`м`
5. **Метаданные**: `extract_media_info()` из TDLib MessagePhoto; auto-download visible photos
**Оптимизации производительности:**
1. **Dual protocol strategy**:
- `inline_image_renderer`: Halfblocks → быстро (Unicode блоки), для навигации
- `modal_image_renderer`: iTerm2/Sixel → медленно (high quality), для просмотра
2. **Frame throttling**: inline images 15 FPS (66ms), текст 60 FPS
3. **Lazy loading**: загрузка только видимых изображений (не все сразу)
4. **LRU кэш**: max 100 протоколов, eviction старых
5. **Loading indicator**: "⏳ Загрузка..." в модалке при первом открытии
6. **Navigation hotkeys**: `←`/`→` между фото, `Esc`/`q` закрыть модалку
**UI рендеринг**:
- `message_bubble.rs`: photo status (Downloading/Error/placeholder), inline preview
- `messages.rs`: второй проход с `render_images()` + throttling + только видимые
- `modals/image_viewer.rs`: fullscreen modal с aspect ratio + loading state
### Фаза 13: Рефакторинг (подробности)
Разбиты 5 монолитных файлов (4582 строк) на модульную архитектуру:
- **input/main_input.rs** (1199→164): чистый роутер + 5 handler модулей в `handlers/`
- **app/mod.rs** (1015→371): 5 trait модулей в `methods/` (Navigation, Message, Compose, Search, Modal)
- **ui/messages.rs** (893→365): модули `modals/` (search, pinned, delete, reactions) + `compose_bar.rs`
- **tdlib/messages.rs** (836→3 файла): `messages/` (mod, convert, operations)
- **config/mod.rs** (642→3 файла): validation.rs, loader.rs
- **Очистка дублей**: ~220 строк удалено (shared components, format_user_status, scroll_to_message)
- **Документация**: PROJECT_STRUCTURE.md переписан, 16 файлов получили `//!` docs
### Фаза 12: Голосовые сообщения (подробности)
**Реализовано:**
- **AudioPlayer** на ffplay (subprocess): play, pause (SIGSTOP), resume (SIGCONT), stop
- **VoiceCache**: LRU кэш OGG файлов в `~/.cache/tele-tui/voice/` (max 100 MB)
- **Типы**: `VoiceInfo`, `VoiceDownloadState`, `PlaybackState`, `PlaybackStatus`
- **TDLib интеграция**: `download_voice_note()`, конвертация `MessageVoiceNote`
- **Хоткеи**: Space (play/pause), ←/→ (seek ±5s via ffplay restart с `-ss`)
- **Автостоп**: при навигации на другое сообщение воспроизведение останавливается
- **Ticker**: `last_playback_tick` в App + обновление position в event loop (1 FPS redraw)
- **VoiceCache**: проверка кэша перед загрузкой, кэширование после download
- **AudioConfig**: `[audio]` секция в config.toml (cache_size_mb, auto_download_voice)
- **UI**: progress bar (━●─) + waveform (▁▂▃▄▅▆▇█) + иконки статуса в `message_bubble.rs`
- **Race condition fixes**: `starting` flag + pid ownership guard в потоках AudioPlayer
- **Seek**: `resume_from()` перезапускает ffplay с `-ss` offset; MoveLeft/MoveRight как alias для SeekBackward/SeekForward
- **Resume with rewind**: пауза→продолжение откатывает на 1 секунду назад
- **Graceful shutdown**: `stop_playback()` + Drop impl для AudioPlayer
### Ключевая архитектура
```
main.rs → event loop (16ms poll)
├── input/ → роутер + handlers/ (chat, chat_list, compose, modal, search)
├── app/ → App<T: TdClientTrait> + methods/ (5 traits, 67 методов)
├── ui/ → рендеринг (messages, chat_list, modals/, compose_bar, components/)
├── audio/ → player.rs (ffplay), cache.rs (VoiceCache)
├── media/ → [feature=images] cache.rs, image_renderer.rs
└── tdlib/ → TDLib wrapper (client, auth, chats, messages/, users, reactions, types)
```
### Тестирование
500+ тестов (0 failures):
- Snapshot tests: 57 (UI компоненты)
- Integration tests: 93 (user flows)
- E2E tests: 12 (smoke + journey)
- Utils tests: 18
- Performance benchmarks: 8
### Ключевые решения
1. **Неблокирующий receive**: TDLib updates через `mpsc::channel` в отдельном потоке
2. **Trait-based App**: методы разбиты на traits — требуют `use` import на call site
3. **FakeTdClient**: mock для тестов без TDLib (реализует TdClientTrait)
4. **Оптимизация рендеринга**: `needs_redraw` флаг, рендеринг только при изменениях
5. **Конфиг**: TOML `~/.config/tele-tui/config.toml`, credentials с приоритетом (XDG → .env)
6. **Feature-gated images**: `images` feature flag для ratatui-image + image deps
7. **Dual renderer**: inline (Halfblocks, 15 FPS) + modal (iTerm2/Sixel, high quality) для баланса скорости/качества
8. **Audio via ffplay**: subprocess с SIGSTOP/SIGCONT для pause/resume, автостоп при навигации
### Зависимости (основные)
```toml
ratatui = "0.29" # TUI фреймворк
crossterm = "0.28" # Терминальный backend
tdlib-rs = "1.1" # Telegram TDLib binding
tokio = "1" # Async runtime
notify-rust = "4.11" # Desktop уведомления (feature flag)
ratatui-image = "8.1" # Inline images (feature flag)
image = "0.25" # Image decoding (feature flag)
```
Полная структура проекта: см. [PROJECT_STRUCTURE.md](PROJECT_STRUCTURE.md)
Подробный план: см. [ROADMAP.md](ROADMAP.md)