feat(ui): add support for blurhash rendering in card component
Some checks failed
build / checks-matrix (push) Has been cancelled
build / codecov (push) Has been cancelled
docs / docs (push) Has been cancelled
build / checks-build (push) Has been cancelled

This commit is contained in:
uttarayan21
2025-11-17 04:50:09 +05:30
parent 6ac7aa8ad8
commit c2fdade4d9
3 changed files with 212 additions and 74 deletions

187
Cargo.lock generated
View File

@@ -48,6 +48,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "aligned"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923"
dependencies = [
"as-slice",
]
[[package]] [[package]]
name = "aligned-vec" name = "aligned-vec"
version = "0.6.4" version = "0.6.4"
@@ -172,6 +181,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
name = "as-slice"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
dependencies = [
"stable_deref_trait",
]
[[package]] [[package]]
name = "ash" name = "ash"
version = "0.38.0+1.3.281" version = "0.38.0+1.3.281"
@@ -503,6 +521,26 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av-scenechange"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
dependencies = [
"aligned",
"anyhow",
"arg_enum_proc_macro",
"arrayvec",
"log",
"num-rational",
"num-traits",
"pastey",
"rayon",
"thiserror 2.0.17",
"v_frame",
"y4m",
]
[[package]] [[package]]
name = "av1-grain" name = "av1-grain"
version = "0.2.5" version = "0.2.5"
@@ -587,9 +625,12 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]] [[package]]
name = "bitstream-io" name = "bitstream-io"
version = "2.6.0" version = "4.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
dependencies = [
"core2",
]
[[package]] [[package]]
name = "blade-graphics" name = "blade-graphics"
@@ -695,6 +736,12 @@ dependencies = [
"piper", "piper",
] ]
[[package]]
name = "blurhash"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e79769241dcd44edf79a732545e8b5cec84c247ac060f5252cd51885d093a8fc"
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.12.1" version = "1.12.1"
@@ -731,9 +778,9 @@ dependencies = [
[[package]] [[package]]
name = "built" name = "built"
version = "0.7.7" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
@@ -862,16 +909,6 @@ dependencies = [
"nom 7.1.3", "nom 7.1.3",
] ]
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.4"
@@ -1230,6 +1267,15 @@ dependencies = [
"metal", "metal",
] ]
[[package]]
name = "core2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "core_maths" name = "core_maths"
version = "0.1.1" version = "0.1.1"
@@ -2078,9 +2124,9 @@ dependencies = [
[[package]] [[package]]
name = "gif" name = "gif"
version = "0.13.3" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" checksum = "f954a9e9159ec994f73a30a12b96a702dde78f5547bcb561174597924f7d4162"
dependencies = [ dependencies = [
"color_quant", "color_quant",
"weezl", "weezl",
@@ -2764,9 +2810,9 @@ dependencies = [
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.8" version = "0.25.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder-lite", "byteorder-lite",
@@ -2782,8 +2828,8 @@ dependencies = [
"rayon", "rayon",
"rgb", "rgb",
"tiff", "tiff",
"zune-core", "zune-core 0.5.0",
"zune-jpeg", "zune-jpeg 0.5.5",
] ]
[[package]] [[package]]
@@ -2943,15 +2989,6 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.13.0" version = "0.13.0"
@@ -2981,11 +3018,14 @@ name = "jello"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"api", "api",
"blurhash",
"clap", "clap",
"clap_complete", "clap_complete",
"dotenvy", "dotenvy",
"error-stack", "error-stack",
"gpui", "gpui",
"image",
"tap",
"thiserror 2.0.17", "thiserror 2.0.17",
"tokio", "tokio",
"tracing", "tracing",
@@ -3234,6 +3274,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata",
]
[[package]] [[package]]
name = "maybe-rayon" name = "maybe-rayon"
version = "0.1.1" version = "0.1.1"
@@ -3839,6 +3888,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]] [[package]]
name = "pathdiff" name = "pathdiff"
version = "0.2.3" version = "0.2.3"
@@ -4305,19 +4360,21 @@ checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223"
[[package]] [[package]]
name = "rav1e" name = "rav1e"
version = "0.7.1" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
dependencies = [ dependencies = [
"aligned-vec",
"arbitrary", "arbitrary",
"arg_enum_proc_macro", "arg_enum_proc_macro",
"arrayvec", "arrayvec",
"av-scenechange",
"av1-grain", "av1-grain",
"bitstream-io", "bitstream-io",
"built", "built",
"cfg-if", "cfg-if",
"interpolate_name", "interpolate_name",
"itertools 0.12.1", "itertools 0.14.0",
"libc", "libc",
"libfuzzer-sys", "libfuzzer-sys",
"log", "log",
@@ -4326,23 +4383,21 @@ dependencies = [
"noop_proc_macro", "noop_proc_macro",
"num-derive", "num-derive",
"num-traits", "num-traits",
"once_cell",
"paste", "paste",
"profiling", "profiling",
"rand 0.8.5", "rand 0.9.2",
"rand_chacha 0.3.1", "rand_chacha 0.9.0",
"simd_helpers", "simd_helpers",
"system-deps", "thiserror 2.0.17",
"thiserror 1.0.69",
"v_frame", "v_frame",
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]] [[package]]
name = "ravif" name = "ravif"
version = "0.11.20" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
dependencies = [ dependencies = [
"avif-serialize", "avif-serialize",
"imgref", "imgref",
@@ -5435,19 +5490,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr",
"heck 0.5.0",
"pkg-config",
"toml 0.8.23",
"version-compare",
]
[[package]] [[package]]
name = "taffy" name = "taffy"
version = "0.9.0" version = "0.9.0"
@@ -5472,12 +5514,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.23.0" version = "3.23.0"
@@ -5571,7 +5607,7 @@ dependencies = [
"half", "half",
"quick-error", "quick-error",
"weezl", "weezl",
"zune-jpeg", "zune-jpeg 0.4.21",
] ]
[[package]] [[package]]
@@ -5906,10 +5942,14 @@ version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [ dependencies = [
"matchers",
"nu-ansi-term", "nu-ansi-term",
"once_cell",
"regex-automata",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
"tracing",
"tracing-core", "tracing-core",
"tracing-log", "tracing-log",
] ]
@@ -6197,12 +6237,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@@ -6970,6 +7004,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "y4m"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]] [[package]]
name = "yazi" name = "yazi"
version = "0.2.1" version = "0.2.1"
@@ -7266,6 +7306,12 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
[[package]] [[package]]
name = "zune-inflate" name = "zune-inflate"
version = "0.2.54" version = "0.2.54"
@@ -7281,7 +7327,16 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
dependencies = [ dependencies = [
"zune-core", "zune-core 0.4.12",
]
[[package]]
name = "zune-jpeg"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e"
dependencies = [
"zune-core 0.5.0",
] ]
[[package]] [[package]]

View File

@@ -9,12 +9,15 @@ license = "MIT"
[dependencies] [dependencies]
api = { version = "0.1.0", path = "api" } api = { version = "0.1.0", path = "api" }
blurhash = "0.2.3"
clap = { version = "4.5", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
clap_complete = "4.5" clap_complete = "4.5"
dotenvy = "0.15.7" dotenvy = "0.15.7"
error-stack = "0.6" error-stack = "0.6"
gpui = { version = "0.2.2", default-features = false, features = ["wayland"] } gpui = { version = "0.2.2", default-features = false, features = ["wayland"] }
image = "0.25.9"
tap = "1.0.1"
thiserror = "2.0" thiserror = "2.0"
tokio = { version = "1.43.1", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.43.1", features = ["macros", "rt-multi-thread"] }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = { version = "0.3", features = ["env-filter"] }

View File

@@ -1,18 +1,20 @@
mod movies; // mod movies;
mod player; // mod player;
mod series; // mod series;
use ::tap::*;
use std::collections::BTreeMap; use std::{collections::BTreeMap, sync::Arc};
use gpui::{ use gpui::{
App, Application, Bounds, ClickEvent, Context, SharedString, Window, WindowBounds, App, Application, Bounds, ClickEvent, Context, ImageId, ImageSource, RenderImage, Resource,
WindowOptions, actions, div, prelude::*, px, rgb, size, SharedString, Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, rgb, size,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AppState { pub struct AppState {
pub title: SharedString, pub title: SharedString,
pub items: BTreeMap<SharedString, Item>, pub items: BTreeMap<SharedString, Item>,
pub item_ids: BTreeMap<usize, SharedString>,
pub current_item: Option<SharedString>, pub current_item: Option<SharedString>,
pub errors: Vec<String>, pub errors: Vec<String>,
pub jellyfin_client: api::JellyfinClient, pub jellyfin_client: api::JellyfinClient,
@@ -47,6 +49,7 @@ impl AppState {
AppState { AppState {
title: SharedString::new(title.as_ref()), title: SharedString::new(title.as_ref()),
items: BTreeMap::new(), items: BTreeMap::new(),
item_ids: BTreeMap::new(),
current_item: None, current_item: None,
errors: Vec::new(), errors: Vec::new(),
jellyfin_client, jellyfin_client,
@@ -64,7 +67,10 @@ impl AppState {
fn load_item(id: usize) -> impl Fn(&mut Self, &ClickEvent, &mut Window, &mut Context<Self>) { fn load_item(id: usize) -> impl Fn(&mut Self, &ClickEvent, &mut Window, &mut Context<Self>) {
move |state: &mut Self, event: &ClickEvent, window: &mut Window, cx: &mut Context<Self>| { move |state: &mut Self, event: &ClickEvent, window: &mut Window, cx: &mut Context<Self>| {
dbg!("Loading item with ID: {}", id); let item_id = id;
cx.spawn(async move |entity, app| {
tracing::info!("Loading item with id: {}", item_id);
});
} }
} }
@@ -183,3 +189,77 @@ pub fn ui(jellyfin_client: api::JellyfinClient) {
.expect("Failed to open window"); .expect("Failed to open window");
}) })
} }
#[derive(Clone, Debug)]
pub struct Card {
pub id: usize,
pub title: SharedString,
pub description: SharedString,
pub image: SharedString,
pub image_blurhash: BlurHash,
pub media_type: SharedString,
pub loading: bool,
}
impl Render for Card {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div()
.id(self.id)
.flex()
.flex_col()
.w_48()
.h_64()
.p_10()
.bg(rgb(0xff00ff))
.rounded_lg()
.pipe(|card| {
if self.loading {
card.child(self.image_blurhash.clone())
} else {
card.child(gpui::img(self.image.clone()))
}
})
}
}
#[derive(Clone, Debug)]
pub struct BlurHash {
pub id: ImageId,
pub data: Arc<RenderImage>,
}
impl BlurHash {
pub fn new(
data: impl AsRef<str>,
width: u32,
height: u32,
punch: f32,
) -> Result<Self, error_stack::Report<crate::Error>> {
use error_stack::ResultExt;
let decoded =
blurhash::decode(data.as_ref(), width, height, punch).change_context(crate::Error)?;
let buffer = image::RgbaImage::from_raw(width, height, decoded)
.ok_or(crate::Error)
.attach("Failed to convert")?;
let frame = image::Frame::new(buffer);
let render_image = RenderImage::new([frame]);
Ok(Self {
id: render_image.id,
data: Arc::from(render_image),
})
}
}
impl Render for BlurHash {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
gpui::img(ImageSource::Render(self.data.clone()))
}
}
impl IntoElement for BlurHash {
type Element = gpui::Img;
fn into_element(self) -> Self::Element {
gpui::img(ImageSource::Render(self.data.clone()))
}
}