# Текущий контекст проекта ## Статус: 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` - `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 + 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)