diff --git a/crates/ely_app/src/shell/internal_pages.rs b/crates/ely_app/src/shell/internal_pages.rs new file mode 100644 index 0000000..6c43251 --- /dev/null +++ b/crates/ely_app/src/shell/internal_pages.rs @@ -0,0 +1,197 @@ +use ely_browser_core::BrowserSnapshot; +use ely_design_system::{colors, spacing}; +use ely_domain::{BrowserTab, HistoryEntry}; +use gpui::{ + AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, + StatefulInteractiveElement, Styled, div, px, rgb, +}; +use gpui_component::{IconName, StyledExt, scroll::ScrollableElement}; + +use super::ElyShell; + +impl ElyShell { + pub(super) fn render_web_canvas( + &mut self, + tab: &BrowserTab, + snapshot: &BrowserSnapshot, + cx: &mut Context, + ) -> AnyElement { + match tab.url().as_str() { + "ely://history" => self.render_history_page(snapshot, cx), + _ => render_default_page(tab), + } + } + + fn render_history_page( + &mut self, + snapshot: &BrowserSnapshot, + cx: &mut Context, + ) -> AnyElement { + render_canvas_surface( + div() + .size_full() + .p_8() + .flex() + .flex_col() + .gap_5() + .child( + div() + .flex() + .items_end() + .justify_between() + .child( + div() + .flex() + .flex_col() + .gap_2() + .child( + div() + .text_size(px(26.0)) + .text_color(rgb(colors::INK)) + .child("History"), + ) + .child(div().text_sm().text_color(rgb(colors::MUTED)).child( + format!( + "{} / {}", + snapshot.active_profile_name, snapshot.active_space_name + ), + )), + ) + .child( + div() + .text_xs() + .text_color(rgb(colors::MUTED)) + .child(format!("{} entries", snapshot.history_entries.len())), + ), + ) + .child(self.render_history_list(snapshot, cx)), + ) + } + + fn render_history_list( + &mut self, + snapshot: &BrowserSnapshot, + cx: &mut Context, + ) -> AnyElement { + if snapshot.history_entries.is_empty() { + return div() + .flex_1() + .border_t_1() + .border_color(rgb(colors::HAIRLINE)) + .pt_5() + .text_sm() + .text_color(rgb(colors::MUTED)) + .child("No history entries for this Space and Profile.") + .into_any_element(); + } + + div() + .flex_1() + .flex() + .flex_col() + .overflow_y_scrollbar() + .border_t_1() + .border_color(rgb(colors::HAIRLINE)) + .children( + snapshot + .history_entries + .iter() + .rev() + .enumerate() + .map(|(index, entry)| self.render_history_row(index, entry, cx)), + ) + .into_any_element() + } + + fn render_history_row( + &mut self, + index: usize, + entry: &HistoryEntry, + cx: &mut Context, + ) -> AnyElement { + let url = entry.url().clone(); + + div() + .id(SharedString::from(format!("history-{index}"))) + .py_3() + .border_b_1() + .border_color(rgb(colors::HAIRLINE)) + .flex() + .items_center() + .justify_between() + .gap_4() + .cursor_pointer() + .hover(|style| style.bg(rgb(colors::CANVAS_SOFT))) + .active(|style| style.opacity(0.82)) + .on_click(cx.listener(move |shell, _, window, cx| { + shell.open_url(url.clone(), window, cx); + })) + .child( + div() + .min_w_0() + .flex() + .flex_col() + .gap_1() + .child( + div() + .text_sm() + .font_semibold() + .truncate() + .text_color(rgb(colors::INK)) + .child(entry.title().to_string()), + ) + .child( + div() + .text_xs() + .truncate() + .text_color(rgb(colors::MUTED)) + .child(entry.url().display_url()), + ), + ) + .child(div().text_color(rgb(colors::MUTED_SOFT)).child(IconName::ExternalLink)) + .into_any_element() + } +} + +fn render_default_page(tab: &BrowserTab) -> AnyElement { + render_canvas_surface( + div() + .size_full() + .p_8() + .flex() + .flex_col() + .gap_4() + .child( + div() + .text_size(px(26.0)) + .text_color(rgb(colors::INK)) + .child(tab.title().to_string()), + ) + .child(div().text_sm().text_color(rgb(colors::MUTED)).child(render_tab_status(tab))), + ) +} + +fn render_canvas_surface(content: impl IntoElement) -> AnyElement { + div() + .flex_1() + .h_full() + .p(px(spacing::LG)) + .bg(rgb(colors::CANVAS_SOFT)) + .child( + div() + .size_full() + .rounded_lg() + .border_1() + .border_color(rgb(colors::HAIRLINE)) + .bg(rgb(colors::SURFACE_CARD)) + .child(content), + ) + .into_any_element() +} + +fn render_tab_status(tab: &BrowserTab) -> String { + match tab.url().as_str() { + "ely://new-tab" => "Ready".to_string(), + url => url.to_string(), + } +} diff --git a/crates/ely_app/src/shell/mod.rs b/crates/ely_app/src/shell/mod.rs index 4f2e0bf..2997a55 100644 --- a/crates/ely_app/src/shell/mod.rs +++ b/crates/ely_app/src/shell/mod.rs @@ -1,3 +1,4 @@ +mod internal_pages; mod render; use ely_browser_core::{BrowserCore, InitialBrowserConfig}; @@ -97,9 +98,13 @@ impl ElyShell { } fn open_internal_tab(&mut self, url_text: &str, window: &mut Window, cx: &mut Context) { - if let ShellState::Ready(core) = &mut self.state - && let Ok(url) = UrlText::parse(url_text) - { + if let Ok(url) = UrlText::parse(url_text) { + self.open_url(url, window, cx); + } + } + + fn open_url(&mut self, url: UrlText, window: &mut Window, cx: &mut Context) { + if let ShellState::Ready(core) = &mut self.state { core.open_tab(url); self.sync_address_input(window, cx); self.focus_address_bar(window, cx); diff --git a/crates/ely_app/src/shell/render.rs b/crates/ely_app/src/shell/render.rs index 0ff3f91..83b02b2 100644 --- a/crates/ely_app/src/shell/render.rs +++ b/crates/ely_app/src/shell/render.rs @@ -58,7 +58,7 @@ impl ElyShell { .flex_1() .overflow_hidden() .child(self.render_sidebar(&snapshot, cx)) - .child(render_web_canvas(&active_tab)), + .child(self.render_web_canvas(&active_tab, &snapshot, cx)), ) .into_any_element() } @@ -409,36 +409,6 @@ impl ElyShell { } } -fn render_web_canvas(tab: &BrowserTab) -> AnyElement { - div() - .flex_1() - .h_full() - .p_6() - .bg(rgb(colors::CANVAS_SOFT)) - .child( - div() - .size_full() - .rounded_lg() - .border_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::SURFACE_CARD)) - .p_8() - .flex() - .flex_col() - .gap_4() - .child( - div() - .text_size(px(26.0)) - .text_color(rgb(colors::INK)) - .child(tab.title().to_string()), - ) - .child( - div().text_sm().text_color(rgb(colors::MUTED)).child(render_tab_status(tab)), - ), - ) - .into_any_element() -} - fn render_error(message: String) -> AnyElement { div() .size_full() @@ -455,13 +425,6 @@ fn section_label(label: &'static str) -> impl IntoElement { div().text_xs().font_semibold().text_color(rgb(colors::MUTED)).child(label) } -fn render_tab_status(tab: &BrowserTab) -> String { - match tab.url().as_str() { - "ely://new-tab" => "Ready".to_string(), - url => url.to_string(), - } -} - fn archive_source_label(source: &ArchiveSource) -> &'static str { match source { ArchiveSource::ManualClose => "Closed",