Add playback position ticker in event loop with 1s UI refresh rate, integrate VoiceCache for downloaded voice files, add [audio] config section (cache_size_mb, auto_download_voice), and render progress bar with waveform visualization in message bubbles. Fix race conditions in AudioPlayer: add `starting` flag to prevent false `is_stopped()` during ffplay startup, guard pid cleanup so old threads don't overwrite newer process pids. Implement `resume_from()` with ffplay `-ss` for real audio seek on unpause (-1s rewind). Kill ffplay on app exit via `stop_playback()` in shutdown + Drop impl. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
7.9 KiB
Markdown
125 lines
7.9 KiB
Markdown
# Текущий контекст проекта
|
||
|
||
## Статус: Фаза 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)
|
||
- **Автостоп**: при навигации на другое сообщение воспроизведение останавливается
|
||
|
||
**Доделано в этой сессии:**
|
||
- **Ticker**: `last_playback_tick` в App + обновление position в event loop каждые 16ms
|
||
- **VoiceCache интеграция**: проверка кэша перед загрузкой, кэширование после download
|
||
- **AudioConfig**: `[audio]` секция в config.toml (cache_size_mb, auto_download_voice)
|
||
|
||
**Не реализовано (optional):**
|
||
- UI индикаторы в сообщениях (🎤, progress bar, waveform) — начаты в diff, не подключены
|
||
|
||
### Ключевая архитектура
|
||
|
||
```
|
||
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)
|