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:
@@ -48,6 +48,9 @@ pub enum Command {
|
||||
ReactMessage,
|
||||
SelectMessage,
|
||||
|
||||
// Media
|
||||
ViewImage,
|
||||
|
||||
// Input
|
||||
SubmitMessage,
|
||||
Cancel,
|
||||
@@ -201,7 +204,13 @@ impl Keybindings {
|
||||
KeyBinding::new(KeyCode::Char('у')), // RU
|
||||
]);
|
||||
// Note: SelectMessage обрабатывается через Command::SubmitMessage в handle_enter_key()
|
||||
|
||||
|
||||
// Media
|
||||
bindings.insert(Command::ViewImage, vec![
|
||||
KeyBinding::new(KeyCode::Char('v')),
|
||||
KeyBinding::new(KeyCode::Char('м')), // RU
|
||||
]);
|
||||
|
||||
// Input
|
||||
bindings.insert(Command::SubmitMessage, vec![
|
||||
KeyBinding::new(KeyCode::Enter),
|
||||
|
||||
@@ -43,6 +43,10 @@ pub struct Config {
|
||||
/// Настройки desktop notifications.
|
||||
#[serde(default)]
|
||||
pub notifications: NotificationsConfig,
|
||||
|
||||
/// Настройки отображения изображений.
|
||||
#[serde(default)]
|
||||
pub images: ImagesConfig,
|
||||
}
|
||||
|
||||
/// Общие настройки приложения.
|
||||
@@ -105,6 +109,27 @@ pub struct NotificationsConfig {
|
||||
pub urgency: String,
|
||||
}
|
||||
|
||||
/// Настройки отображения изображений.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ImagesConfig {
|
||||
/// Показывать превью изображений в чате
|
||||
#[serde(default = "default_show_images")]
|
||||
pub show_images: bool,
|
||||
|
||||
/// Размер кэша изображений (в МБ)
|
||||
#[serde(default = "default_image_cache_size_mb")]
|
||||
pub cache_size_mb: u64,
|
||||
}
|
||||
|
||||
impl Default for ImagesConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
show_images: default_show_images(),
|
||||
cache_size_mb: default_image_cache_size_mb(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Дефолтные значения (используются serde атрибутами)
|
||||
fn default_timezone() -> String {
|
||||
"+03:00".to_string()
|
||||
@@ -146,6 +171,14 @@ fn default_notification_urgency() -> String {
|
||||
"normal".to_string()
|
||||
}
|
||||
|
||||
fn default_show_images() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_image_cache_size_mb() -> u64 {
|
||||
crate::constants::DEFAULT_IMAGE_CACHE_SIZE_MB
|
||||
}
|
||||
|
||||
impl Default for GeneralConfig {
|
||||
fn default() -> Self {
|
||||
Self { timezone: default_timezone() }
|
||||
@@ -183,6 +216,7 @@ impl Default for Config {
|
||||
colors: ColorsConfig::default(),
|
||||
keybindings: Keybindings::default(),
|
||||
notifications: NotificationsConfig::default(),
|
||||
images: ImagesConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user