feat: implement Phase 11 — inline photo viewing with ratatui-image

Add feature-gated (`images`) inline photo support:
- New types: MediaInfo, PhotoInfo, PhotoDownloadState, ImagesConfig
- Media module: ImageCache (LRU filesystem cache), ImageRenderer (terminal protocol detection)
- Photo metadata extraction from TDLib MessagePhoto with download_file() API
- ViewImage command (v/м) to toggle photo expand/collapse in message selection
- Two-pass UI rendering: placeholder lines in message bubbles + StatefulImage overlay
- Collapse all expanded photos on Esc (exit selection mode)

Dependencies: ratatui-image 8.1, image 0.25 (optional, behind `images` feature flag)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-02-06 21:25:17 +03:00
parent 6845ee69bf
commit b0f1f9fdc2
29 changed files with 1505 additions and 102 deletions

View File

@@ -18,92 +18,46 @@
---
## Фаза 11: Показ изображений в чате [PLANNED]
## Фаза 11: Inline просмотр фото в чате [IN PROGRESS]
**UX**: `v`/`м` на фото → загрузка → inline превью (~30x15) → Esc/навигация → свернуть обратно в текст.
Повторное `v` — мгновенно из кэша. Целевой терминал: iTerm2.
### Этап 1: Инфраструктура [TODO]
- [ ] Модуль src/media/
- image_cache.rs - LRU кэш для загруженных изображений
- image_loader.rs - Асинхронная загрузка через TDLib
- image_renderer.rs - Рендеринг в ratatui
- [ ] Зависимости
- ratatui-image 1.0 - поддержка изображений в TUI
- Определение протокола терминала (Sixel/Kitty/iTerm2/Halfblocks)
- [ ] ImageCache с лимитами
- LRU кэш с максимальным размером в МБ
- Автоматическая очистка старых изображений
- MAX_IMAGE_CACHE_SIZE = 100 MB (по умолчанию)
- [ ] Обновить ratatui 0.29 → 0.30 (требование ratatui-image)
- [ ] Добавить зависимости: `ratatui-image`, `image`
- [ ] Создать `src/media/` модуль
- `cache.rs` — LRU кэш файлов, лимит 500 MB, `~/.cache/tele-tui/images/`
- `loader.rs` — загрузка через TDLib downloadFile API
### Этап 2: Интеграция с TDLib [TODO]
- [ ] Обработка MessageContentPhoto
- Добавить PhotoInfo в MessageInfo
- Извлечение file_id, width, height из Photo
- Выбор оптимального размера изображения (до 800px)
- [ ] Загрузка файлов
- Метод TdClient::download_photo(file_id)
- Асинхронная загрузка через downloadFile API
- Обработка состояний загрузки (pending/downloading/ready)
- [ ] Кэширование
- Сохранение путей к загруженным файлам
- Повторное использование уже загруженных изображений
### Этап 2: Расширить MessageInfo [TODO]
- [ ] Добавить `MediaInfo` в `MessageContent` (PhotoInfo: file_id, width, height)
- [ ] Сохранять метаданные фото при конвертации TDLib → MessageInfo
- [ ] Обновить FakeTdClient для тестов
### Этап 3: Рендеринг в UI [TODO]
- [ ] Модификация render_messages()
- Определение возможностей терминала при старте
- Рендеринг изображений через ratatui-image
- Автоматическое масштабирование под размер области
- Сохранение aspect ratio
- [ ] Превью в списке сообщений
- Миниатюры размером 20x10 символов
- Lazy loading (загрузка только видимых)
- Placeholder пока изображение грузится
- [ ] Индикатор загрузки
- Текстовая заглушка "[Загрузка фото...]"
- Progress bar для больших файлов
- Процент загрузки
### Этап 3: Загрузка файлов [TODO]
- [ ] Добавить `download_file()` в TdClientTrait
- [ ] Реализация через TDLib `downloadFile` API
- [ ] Состояния загрузки: Idle → Downloading → Ready → Error
- [ ] Кэширование в `~/.cache/tele-tui/images/`
### Этап 4: Полноэкранный просмотр [TODO]
- [ ] Новый режим: ViewImage
- `v` / `м` в режиме выбора - открыть изображение
- Показ на весь экран терминала
- `Esc` для закрытия
- [ ] Информация об изображении
- Размер файла
- Разрешение (width x height)
- Формат (JPEG/PNG/GIF)
- [ ] Навигация
- `←` / `→` - предыдущее/следующее изображение в чате
- Автоматическая загрузка соседних изображений
### Этап 4: UI рендеринг [TODO]
- [ ] `Picker::from_query_stdio()` при старте (определение iTerm2 протокола)
- [ ] Команда `ViewImage` (`v`/`м`) в режиме выбора → запуск загрузки
- [ ] Inline рендеринг через `StatefulImage` (ширина ~30, высота по aspect ratio)
- [ ] Esc/навигация → сворачивание обратно в текст `📷 caption`
### Этап 5: Конфигурация и UX [TODO]
- [ ] MediaConfig в config.toml
- show_images: bool - включить/отключить показ изображений
- image_cache_mb: usize - размер кэша в МБ
- preview_quality: "low" | "medium" | "high"
- render_protocol: "auto" | "sixel" | "kitty" | "iterm2" | "halfblocks"
- [ ] Поддержка различных терминалов
- Auto-detection протокола при старте
- Fallback на Unicode halfblocks для любого терминала
- Опция отключения изображений если терминал не поддерживает
- [ ] Оптимизация производительности
- Асинхронная загрузка (не блокирует UI)
- Приоритизация видимых изображений
- Fast resize для превью
- Кэширование отмасштабированных версий
### Этап 6: Обработка ошибок [TODO]
- [ ] Graceful fallback
- Текстовая заглушка "[Фото]" если загрузка не удалась
- Повторная попытка по запросу пользователя
- Логирование проблем через tracing
- [ ] Ограничения
- Таймаут загрузки (30 сек)
- Максимальный размер файла для автозагрузки (10 MB)
- Предупреждение для больших файлов
### Этап 5: Полировка [TODO]
- [ ] Индикатор загрузки (`📷 ⏳ Загрузка...`)
- [ ] Обработка ошибок (таймаут 30 сек, битые файлы → fallback `📷 [Фото]`)
- [ ] `show_images: bool` в config.toml
- [ ] Логирование через tracing
### Технические детали
- **Поддерживаемые протоколы:** Sixel, Kitty Graphics, iTerm2 Inline Images, Unicode Halfblocks (fallback)
- **Поддерживаемые форматы:** JPEG, PNG, GIF, WebP, BMP
- **Новые хоткеи:** `v`/`м` - полноэкранный просмотр, `←`/`→` - навигация, `Esc` - закрыть
- **Библиотека:** ratatui-image 10.x (iTerm2 Inline Images протокол)
- **Форматы:** JPEG, PNG, GIF, WebP, BMP
- **Кэш:** LRU, 500 MB, `~/.cache/tele-tui/images/`
- **Хоткеи:** `v`/`м` — показать/скрыть inline превью
---