From e621c437572482634cd0e3cefb50b3c280c300ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 9 May 2026 15:47:00 -0400 Subject: [PATCH] Update split view and web surface to glass morphism styling Split panes use rounded corners, glass backgrounds, and accent dot indicators matching the design reference. Web surface error page uses centered layout with proper typography. Remove opaque white backgrounds in favor of transparent glass surfaces. --- crates/ely_app/src/shell/splits.rs | 100 ++++++++--------- crates/ely_app/src/shell/web_surface_view.rs | 109 +++++-------------- 2 files changed, 70 insertions(+), 139 deletions(-) diff --git a/crates/ely_app/src/shell/splits.rs b/crates/ely_app/src/shell/splits.rs index 96604ad..2833540 100644 --- a/crates/ely_app/src/shell/splits.rs +++ b/crates/ely_app/src/shell/splits.rs @@ -3,7 +3,7 @@ use ely_design_system::{colors, spacing}; use ely_domain::{BrowserTab, SplitAxis, SplitLayout}; use gpui::{ AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, - StatefulInteractiveElement, Styled, Window, div, px, rgb, + StatefulInteractiveElement, Styled, Window, div, px, rgb, rgba, }; use gpui_component::{ IconName, Selectable, Sizable, StyledExt, @@ -130,12 +130,11 @@ impl ElyShell { .flex_1() .h_full() .min_w_0() - .p_3() - .gap_3() + .p(px(14.0)) + .gap(px(10.0)) .flex() .flex_col() - .overflow_hidden() - .bg(rgb(colors::CANVAS)); + .overflow_hidden(); let pane_area = div().flex_1().min_h_0().gap_3().overflow_hidden(); let compact_canvas = layout.axis() == &SplitAxis::Vertical && layout.pane_count() >= 4; @@ -176,7 +175,7 @@ impl ElyShell { fn render_split_pane( &mut self, - index: usize, + _index: usize, tab: &BrowserTab, snapshot: &BrowserSnapshot, compact_canvas: bool, @@ -184,9 +183,8 @@ impl ElyShell { ) -> AnyElement { let active = tab.id() == &snapshot.active_tab_id; let tab_id = tab.id().clone(); - let pane_number = index + 1; - let border = if active { colors::PRIMARY } else { colors::HAIRLINE_STRONG }; - let title_color = if active { colors::INK } else { colors::BODY }; + let border = if active { colors::ACCENT } else { colors::HAIRLINE_STRONG }; + let title_color = if active { colors::INK } else { colors::INK_2 }; div() .id(SharedString::from(format!("split-pane-{}", tab.id().as_str()))) @@ -196,41 +194,37 @@ impl ElyShell { .flex() .flex_col() .overflow_hidden() - .rounded_md() + .rounded(px(10.0)) .border_1() .border_color(rgb(border)) - .bg(rgb(colors::SURFACE_CARD)) + .bg(rgba(colors::GLASS_2)) .cursor_pointer() - .hover(|style| style.bg(rgb(colors::CANVAS_SOFT))) + .hover(|style| style.bg(rgba(colors::GLASS_3))) .active(|style| style.opacity(0.92)) .on_click(cx.listener(move |shell, _, window, cx| { shell.select_tab(&tab_id, window, cx); })) .child( div() - .h(px(spacing::COMMAND_BAR_HEIGHT - spacing::MD)) - .px_3() - .gap_2() + .h(px(30.0)) + .px(px(10.0)) + .gap(px(6.0)) .flex() .items_center() - .border_b_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::CANVAS_SOFT)) - .child(div().text_color(rgb(colors::MUTED)).child(IconName::Frame)) + .flex_shrink_0() .child( div() - .text_xs() - .font_semibold() - .text_color(rgb(colors::MUTED)) - .child(format!("Pane {pane_number}")), + .size(px(6.0)) + .rounded_full() + .bg(rgb(if active { colors::ACCENT } else { colors::INK_4 })), ) .child( div() .flex_1() .min_w_0() .truncate() - .text_sm() - .font_semibold() + .text_size(px(12.0)) + .font_weight(gpui::FontWeight(500.0)) .text_color(rgb(title_color)) .child(tab.title().to_string()), ), @@ -255,10 +249,8 @@ impl ElyShell { .flex() .items_center() .justify_center() - .border_1() - .border_color(rgb(colors::HAIRLINE)) - .rounded_md() - .bg(rgb(colors::CANVAS_SOFT)) + .rounded(px(10.0)) + .bg(rgba(colors::GLASS_2)) .child( div() .text_xs() @@ -391,28 +383,25 @@ impl ElyShell { cx: &mut Context, ) -> Option { let first_tab_id = layout.panes().first()?.tab_id().clone(); - let background = if active { colors::SURFACE_CARD } else { colors::CANVAS }; - let border = if active { colors::PRIMARY } else { colors::HAIRLINE }; + let bg = if active { colors::GLASS_3 } else { 0x00000000 }; Some( div() .id(SharedString::from(format!("saved-split-{}", layout.id().as_str()))) - .rounded_md() - .border_1() - .border_color(rgb(border)) - .bg(rgb(background)) - .px_3() - .py_2() - .gap_2() + .rounded(px(spacing::RADIUS_NAV)) + .bg(rgba(bg)) + .px(px(10.0)) + .py(px(7.0)) + .gap(px(10.0)) .flex() .items_center() .cursor_pointer() - .hover(|style| style.bg(rgb(colors::SURFACE_CARD))) + .hover(|style| style.bg(rgba(colors::GLASS_3))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(move |shell, _, window, cx| { shell.select_tab(&first_tab_id, window, cx); })) - .child(div().text_color(rgb(colors::PRIMARY)).child(IconName::Frame)) + .child(div().text_color(rgb(colors::ACCENT)).child(IconName::Frame)) .child( div() .min_w_0() @@ -421,16 +410,16 @@ impl ElyShell { .gap_1() .child( div() - .text_sm() - .font_semibold() + .text_size(px(13.0)) + .font_weight(gpui::FontWeight(500.0)) .truncate() .text_color(rgb(colors::INK)) .child(layout.title().to_string()), ) .child( div() - .text_xs() - .text_color(rgb(colors::MUTED)) + .text_size(px(11.0)) + .text_color(rgb(colors::INK_4)) .child(format!("{} panes", layout.pane_count())), ), ) @@ -452,21 +441,18 @@ fn render_compact_split_canvas(tab: &BrowserTab) -> AnyElement { .flex_1() .min_h_0() .p_2() - .bg(rgb(colors::CANVAS_SOFT)) .child( div() .size_full() .min_h_0() - .rounded_md() - .border_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::SURFACE_CARD)) - .px_3() - .py_2() - .gap_2() + .rounded(px(10.0)) + .bg(rgba(colors::GLASS_2)) + .px(px(10.0)) + .py(px(8.0)) + .gap(px(8.0)) .flex() .items_center() - .child(div().text_color(rgb(colors::MUTED)).child(IconName::Globe)) + .child(div().text_color(rgb(colors::INK_4)).child(IconName::Globe)) .child( div() .min_w_0() @@ -475,15 +461,15 @@ fn render_compact_split_canvas(tab: &BrowserTab) -> AnyElement { .child( div() .truncate() - .text_sm() - .font_semibold() + .text_size(px(13.0)) + .font_weight(gpui::FontWeight(500.0)) .child(tab.title().to_string()), ) .child( div() .truncate() - .text_xs() - .text_color(rgb(colors::MUTED)) + .text_size(px(11.0)) + .text_color(rgb(colors::INK_4)) .child(split_canvas_status(tab)), ), ), diff --git a/crates/ely_app/src/shell/web_surface_view.rs b/crates/ely_app/src/shell/web_surface_view.rs index c074caf..eb6c8f3 100644 --- a/crates/ely_app/src/shell/web_surface_view.rs +++ b/crates/ely_app/src/shell/web_surface_view.rs @@ -1,12 +1,11 @@ use ely_domain::{BrowserTab, TabId}; use gpui::{ AnyElement, App, Entity, ImageSource, InteractiveElement, IntoElement, MouseButton, ObjectFit, - ParentElement, Styled, StyledImage, Window, canvas, div, img, prelude::FluentBuilder, px, rgb, + ParentElement, Styled, StyledImage, Window, canvas, div, img, px, rgb, }; -use gpui_component::StyledExt; use super::{ElyShell, web_surface_frame::WebSurfaceFrame}; -use ely_design_system::{colors, spacing}; +use ely_design_system::colors; pub(super) fn render_ready_web_surface( frame: &WebSurfaceFrame, @@ -16,10 +15,7 @@ pub(super) fn render_ready_web_surface( render_web_surface( tab, state_entity, - frame.title_label(), - frame.url_label().to_string(), - Some(frame.detail_label()), - img(ImageSource::Render(frame.image.clone())).size_full().object_fit(ObjectFit::Contain), + img(ImageSource::Render(frame.image.clone())).size_full().object_fit(ObjectFit::Fill), ) } @@ -27,14 +23,7 @@ pub(super) fn render_loading_web_surface( tab: &BrowserTab, state_entity: Entity, ) -> AnyElement { - render_web_surface( - tab, - state_entity, - tab.title().to_string(), - tab.url().as_str().to_string(), - Some("Rendering".to_string()), - centered_status(tab.title(), tab.url().as_str(), "Rendering page with Servo", colors::BODY), - ) + render_web_surface(tab, state_entity, div().size_full()) } pub(super) fn render_failed_web_surface( @@ -42,66 +31,36 @@ pub(super) fn render_failed_web_surface( message: &str, state_entity: Entity, ) -> AnyElement { - render_web_surface( - tab, - state_entity, - tab.title().to_string(), - tab.url().as_str().to_string(), - Some("Render failed".to_string()), - centered_status(tab.title(), tab.url().as_str(), message, colors::ERROR), - ) + render_web_surface(tab, state_entity, error_page(message)) } -fn render_web_surface_header(title: String, url: String, detail: Option) -> AnyElement { - div() - .h(px(34.0)) - .px_3() - .gap_3() - .flex() - .items_center() - .border_b_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::CANVAS_SOFT)) - .child( - div() - .min_w_0() - .flex_1() - .truncate() - .text_sm() - .font_semibold() - .text_color(rgb(colors::INK)) - .child(title), - ) - .when_some(detail, |this, detail| { - this.child(div().text_xs().text_color(rgb(colors::MUTED)).child(detail)) - }) - .child( - div().max_w(px(420.0)).truncate().text_xs().text_color(rgb(colors::MUTED)).child(url), - ) - .into_any_element() -} - -fn centered_status(title: &str, url: &str, detail: &str, detail_color: u32) -> impl IntoElement { +fn error_page(message: &str) -> impl IntoElement { div() .size_full() - .p_8() + .p(px(48.0)) .flex() .flex_col() .items_center() .justify_center() - .gap_2() - .bg(rgb(colors::SURFACE_CARD)) - .child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child(title.to_string())) - .child(div().text_sm().text_color(rgb(colors::MUTED)).child(url.to_string())) - .child(div().text_sm().text_color(rgb(detail_color)).child(detail.to_string())) + .gap(px(12.0)) + .child( + div() + .text_size(px(22.0)) + .font_weight(gpui::FontWeight(400.0)) + .text_color(rgb(colors::INK)) + .child("Page unavailable"), + ) + .child( + div() + .text_size(px(14.0)) + .text_color(rgb(colors::INK_3)) + .child(message.to_string()), + ) } fn render_web_surface( tab: &BrowserTab, state_entity: Entity, - title: String, - url: String, - detail: Option, content: impl IntoElement, ) -> AnyElement { let input_tab_id = tab.id().clone(); @@ -112,30 +71,16 @@ fn render_web_surface( div() .flex_1() .h_full() - .p(px(spacing::SM)) - .bg(rgb(colors::CANVAS_SOFT)) + .min_w_0() + .overflow_hidden() .child( div() + .relative() .size_full() .overflow_hidden() - .rounded_md() - .border_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::SURFACE_CARD)) - .flex() - .flex_col() - .child(render_web_surface_header(title, url, detail)) - .child( - div() - .relative() - .flex_1() - .min_h_0() - .overflow_hidden() - .bg(rgb(colors::SURFACE_CARD)) - .child(content) - .child(render_viewport_tracker(tab.id().clone(), tracker_entity)) - .child(render_input_overlay(input_tab_id, input_url, input_entity)), - ), + .child(content) + .child(render_viewport_tracker(tab.id().clone(), tracker_entity)) + .child(render_input_overlay(input_tab_id, input_url, input_entity)), ) .into_any_element() }