Skip to content

Commit

Permalink
Image previews
Browse files Browse the repository at this point in the history
The `sixel` flag enables sixel support in `ratatu-image`, which then
needs libsixel to build.

Additionally to enable the preview feature, one must add it to
config.json:
```json
    "image_preview": {
      "size": {
        "width": 60,
        "height": 10,
      }
    }
```

There is a `backend: "halfblocks"` option in `image_preview` to force a
specific backend rather than guessing it, which might not be 100%
reliable in all terminals.
  • Loading branch information
benjajaja committed Oct 12, 2023
1 parent df3148b commit e22decd
Show file tree
Hide file tree
Showing 11 changed files with 464 additions and 14 deletions.
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,20 @@ features = ["e2e-encryption", "sled", "rustls-tls"]
version = "1.24.1"
features = ["macros", "net", "rt-multi-thread", "sync", "time"]

[dependencies.ratatui-image]
version = "0.3.2"
default-features = false
features = ["serde"]

[dev-dependencies]
lazy_static = "1.4.0"
pretty_assertions = "1.4.0"

[profile.release]
lto = true
incremental = false

[features]
default = []
preview_sixel_rustix = ["ratatui-image/sixel", "ratatui-image/rustix"]

14 changes: 10 additions & 4 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,24 @@
pname = "iamb";
version = "0.0.7";
src = ./.;
cargoLock.lockFile = ./Cargo.lock;
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"modalkit-0.0.16" = "sha256-mjAD1v0r2+SzPdoB2wZ/5iJ1NZK+3OSvCYcUZ5Ef38Y=";
};
};
nativeBuildInputs = [ pkgs.pkgconfig ];
buildInputs = [ pkgs.openssl ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin
(with pkgs.darwin.apple_sdk.frameworks; [ AppKit Security ]);
};
devShell = mkShell {
buildInputs = [
(rustNightly.override { extensions = [ "rust-src" ]; })
(rustNightly.override {
extensions = [ "rust-src" "rust-analyzer-preview" "rustfmt" "clippy" ];
})
pkg-config
cargo-tarpaulin
rust-analyzer
rustfmt
cargo-watch
];
};
});
Expand Down
14 changes: 13 additions & 1 deletion src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::sync::Arc;
use std::time::{Duration, Instant};

use emojis::Emoji;
use ratatui_image::picker::Picker;
use serde::{
de::Error as SerdeError,
de::Visitor,
Expand Down Expand Up @@ -490,6 +491,12 @@ pub enum IambError {
/// A failure to access the system's clipboard.
#[error("Could not use system clipboard data")]
Clipboard,

#[error("IO error: {0}")]
IOError(#[from] std::io::Error),

#[error("Preview error: {0}")]
Preview(String),
}

impl From<IambError> for UIError<IambInfo> {
Expand Down Expand Up @@ -603,6 +610,10 @@ impl RoomInfo {
self.messages.get(self.get_message_key(event_id)?)
}

pub fn get_event_mut(&mut self, event_id: &EventId) -> Option<&mut Message> {
self.messages.get_mut(self.keys.get(event_id)?.to_message_key()?)
}

/// Insert a reaction to a message.
pub fn insert_reaction(&mut self, react: ReactionEvent) {
match react {
Expand Down Expand Up @@ -825,6 +836,7 @@ pub struct ChatStore {

/// Information gathered by the background thread.
pub sync_info: SyncInfo,
pub picker: Option<Picker>,
}

impl ChatStore {
Expand All @@ -833,7 +845,7 @@ impl ChatStore {
ChatStore {
worker,
settings,

picker: None,
cmds: crate::commands::setup_commands(),
emojis: emoji_map(),

Expand Down
25 changes: 25 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::process;

use clap::Parser;
use matrix_sdk::ruma::{OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UserId};
use ratatui_image::picker::ProtocolType;
use serde::{de::Error as SerdeError, de::Visitor, Deserialize, Deserializer};
use tracing::Level;
use url::Url;
Expand Down Expand Up @@ -246,6 +247,26 @@ pub enum UserDisplayStyle {
DisplayName,
}

#[derive(Clone, Deserialize)]
pub struct ImagePreviewValues {
pub backend: Option<ProtocolType>,
pub size: ImagePreviewSize,
}
impl Default for ImagePreviewValues {
fn default() -> Self {
ImagePreviewValues {
backend: None,
size: ImagePreviewSize { width: 66, height: 10 },
}
}
}

#[derive(Clone, Deserialize)]
pub struct ImagePreviewSize {
pub width: usize,
pub height: usize,
}

#[derive(Clone)]
pub struct TunableValues {
pub log_level: Level,
Expand All @@ -260,6 +281,7 @@ pub struct TunableValues {
pub username_display: UserDisplayStyle,
pub default_room: Option<String>,
pub open_command: Option<Vec<String>>,
pub image_preview: Option<ImagePreviewValues>,
}

#[derive(Clone, Default, Deserialize)]
Expand All @@ -276,6 +298,7 @@ pub struct Tunables {
pub username_display: Option<UserDisplayStyle>,
pub default_room: Option<String>,
pub open_command: Option<Vec<String>>,
pub image_preview: Option<ImagePreviewValues>,
}

impl Tunables {
Expand All @@ -295,6 +318,7 @@ impl Tunables {
username_display: self.username_display.or(other.username_display),
default_room: self.default_room.or(other.default_room),
open_command: self.open_command.or(other.open_command),
image_preview: self.image_preview.or(other.image_preview),
}
}

Expand All @@ -312,6 +336,7 @@ impl Tunables {
username_display: self.username_display.unwrap_or_default(),
default_room: self.default_room,
open_command: self.open_command,
image_preview: self.image_preview,
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use std::sync::Arc;
use std::time::Duration;

use clap::Parser;
#[cfg(feature = "preview_sixel_rustix ")]
use ratatui_image::picker::Picker;
use tokio::sync::Mutex as AsyncMutex;
use tracing_subscriber::FmtSubscriber;

Expand Down Expand Up @@ -68,6 +70,7 @@ mod commands;
mod config;
mod keybindings;
mod message;
mod preview;
mod util;
mod windows;
mod worker;
Expand Down Expand Up @@ -264,9 +267,17 @@ impl Application {
let bindings = KeyManager::new(bindings);

let mut locked = store.lock().await;

#[cfg(feature = "preview_sixel_rustix ")]
if settings.tunables.image_preview.is_some() {
let picker = Picker::from_termios(None).unwrap();
locked.application.picker = Some(picker);
}

let screen = setup_screen(settings, locked.deref_mut())?;

let worker = locked.application.worker.clone();

drop(locked);

let actstack = VecDeque::new();
Expand Down
Loading

0 comments on commit e22decd

Please sign in to comment.