From b4355d0f82090b97f5bc743706919ee10663d2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 8 May 2026 11:47:38 -0400 Subject: [PATCH] Add bookmark thumbnail metadata --- .../src/shell/internal_pages/bookmarks.rs | 3 +++ .../ely_browser_core/src/state/bookmarks.rs | 17 +++++++++++++ crates/ely_browser_core/tests/bookmarks.rs | 24 +++++++++++++++++++ crates/ely_domain/src/bookmark.rs | 19 +++++++++++++++ docs/ui-shell.md | 15 ++++++++++++ 5 files changed, 78 insertions(+) diff --git a/crates/ely_app/src/shell/internal_pages/bookmarks.rs b/crates/ely_app/src/shell/internal_pages/bookmarks.rs index e3ab239..5be0572 100644 --- a/crates/ely_app/src/shell/internal_pages/bookmarks.rs +++ b/crates/ely_app/src/shell/internal_pages/bookmarks.rs @@ -286,6 +286,9 @@ fn bookmark_metadata_label(bookmark: &BookmarkEntry) -> String { if let Some(note) = bookmark.note() { parts.push(note.to_string()); } + if bookmark.thumbnail_key().is_some() { + parts.push("Snapshot saved".to_string()); + } parts.join(" - ") } diff --git a/crates/ely_browser_core/src/state/bookmarks.rs b/crates/ely_browser_core/src/state/bookmarks.rs index b10dc35..c3e06e1 100644 --- a/crates/ely_browser_core/src/state/bookmarks.rs +++ b/crates/ely_browser_core/src/state/bookmarks.rs @@ -63,6 +63,23 @@ impl BrowserCore { Ok(()) } + pub fn set_bookmark_thumbnail_key( + &mut self, + bookmark_id: &BookmarkId, + thumbnail_key: impl Into, + ) -> Result<(), CoreError> { + self.bookmark_mut(bookmark_id)?.set_thumbnail_key(thumbnail_key)?; + Ok(()) + } + + pub fn clear_bookmark_thumbnail_key( + &mut self, + bookmark_id: &BookmarkId, + ) -> Result<(), CoreError> { + self.bookmark_mut(bookmark_id)?.clear_thumbnail_key(); + Ok(()) + } + pub fn update_bookmark_metadata( &mut self, bookmark_id: &BookmarkId, diff --git a/crates/ely_browser_core/tests/bookmarks.rs b/crates/ely_browser_core/tests/bookmarks.rs index 81e7b65..6f55a6b 100644 --- a/crates/ely_browser_core/tests/bookmarks.rs +++ b/crates/ely_browser_core/tests/bookmarks.rs @@ -25,6 +25,7 @@ fn bookmark_active_tab_records_current_context() -> Result<(), Box> { assert_eq!(bookmark.url().as_str(), "https://example.com/research"); assert!(bookmark.tags().is_empty()); assert_eq!(bookmark.note(), None); + assert_eq!(bookmark.thumbnail_key(), None); Ok(()) } @@ -65,6 +66,22 @@ fn bookmark_metadata_updates_collection_tags_and_note() -> Result<(), Box Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + core.open_tab(UrlText::parse("https://example.com/research")?); + let bookmark_id = core.bookmark_active_tab()?; + + core.set_bookmark_thumbnail_key(&bookmark_id, " screenshots/example.avif ")?; + let snapshot = core.snapshot()?; + assert_eq!(snapshot.bookmarks[0].thumbnail_key(), Some("screenshots/example.avif")); + + core.clear_bookmark_thumbnail_key(&bookmark_id)?; + let snapshot = core.snapshot()?; + assert_eq!(snapshot.bookmarks[0].thumbnail_key(), None); + Ok(()) +} + #[test] fn bookmark_metadata_batch_update_is_atomic() -> Result<(), Box> { let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; @@ -111,6 +128,13 @@ fn bookmark_metadata_rejects_empty_fields() -> Result<(), Box> { return Err("expected empty bookmark note error".into()); }; assert_eq!(note_error, CoreError::Domain(DomainError::EmptyField { field: "bookmark note" })); + let Err(thumbnail_error) = core.set_bookmark_thumbnail_key(&bookmark_id, " ") else { + return Err("expected empty bookmark thumbnail key error".into()); + }; + assert_eq!( + thumbnail_error, + CoreError::Domain(DomainError::EmptyField { field: "bookmark thumbnail key" }) + ); Ok(()) } diff --git a/crates/ely_domain/src/bookmark.rs b/crates/ely_domain/src/bookmark.rs index 44794b0..7bbd799 100644 --- a/crates/ely_domain/src/bookmark.rs +++ b/crates/ely_domain/src/bookmark.rs @@ -12,6 +12,7 @@ pub struct BookmarkEntry { url: UrlText, tags: Vec, note: Option, + thumbnail_key: Option, added_at: SystemTime, } @@ -36,6 +37,7 @@ impl BookmarkEntry { url, tags: Vec::new(), note: None, + thumbnail_key: None, added_at, }) } @@ -85,6 +87,11 @@ impl BookmarkEntry { self.note.as_deref() } + #[must_use] + pub fn thumbnail_key(&self) -> Option<&str> { + self.thumbnail_key.as_deref() + } + #[must_use] pub fn added_at(&self) -> SystemTime { self.added_at @@ -111,6 +118,18 @@ impl BookmarkEntry { pub fn clear_note(&mut self) { self.note = None; } + + pub fn set_thumbnail_key( + &mut self, + thumbnail_key: impl Into, + ) -> Result<(), DomainError> { + self.thumbnail_key = Some(non_empty_text("bookmark thumbnail key", thumbnail_key.into())?); + Ok(()) + } + + pub fn clear_thumbnail_key(&mut self) { + self.thumbnail_key = None; + } } fn non_empty_text(field: &'static str, value: String) -> Result { diff --git a/docs/ui-shell.md b/docs/ui-shell.md index 1a24dfd..8907e53 100644 --- a/docs/ui-shell.md +++ b/docs/ui-shell.md @@ -194,3 +194,18 @@ Group archive uses the explicit archive command for the same active group flow: │ example.com │ │ └──────────────────────────────┴───────────────────────────────────────────────┘ ``` + +Bookmarks with saved page thumbnails keep the list compact and show the snapshot state inline: + +```text +┌──────────────────────────────────────────────────────────────────────────────┐ +│ ELY Browser [ ely://bookmarks............................ ] [pin] [*] [+] │ +├──────────────────────────────┬───────────────────────────────────────────────┤ +│ Tabs │ Bookmarks 1 bookmark │ +│ Bookmarks │ Profile: Default │ +│ │ │ +│ │ [book] example.com [open][edit]│ +│ │ https://example.com/research │ +│ │ Work - Snapshot saved │ +└──────────────────────────────┴───────────────────────────────────────────────┘ +```