Files
telegram-tui/CONTEXT.md
Mikhail Kilin 6639dc876c fixes
2026-02-13 19:52:53 +03:00

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