From ef7698d2480cd042ff0ecb7897d1c2ffdf257381 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 29 Jul 2026 10:37:55 +0700 Subject: [PATCH] update ui --- Cargo.lock | 69 +-- crates/chat/src/lib.rs | 4 +- crates/chat_ui/src/lib.rs | 7 +- crates/state/src/constants.rs | 7 +- crates/theme/src/colors.rs | 4 +- crates/theme/src/lib.rs | 2 +- crates/ui/src/dock/mod.rs | 38 +- crates/ui/src/dock/stack_panel.rs | 31 +- crates/ui/src/dock/tab_panel.rs | 34 +- crates/ui/src/input/input.rs | 21 - crates/ui/src/resizable/resize_handle.rs | 7 +- crates/ui/src/tab/mod.rs | 512 +----------------- crates/ui/src/tab/tab_bar.rs | 124 +---- crates/ui/src/title_bar.rs | 2 - crates/workspace/src/dialogs/import.rs | 61 ++- crates/workspace/src/lib.rs | 28 +- crates/workspace/src/panels/backup.rs | 17 +- crates/workspace/src/panels/contact_list.rs | 9 +- .../workspace/src/panels/messaging_relays.rs | 9 +- crates/workspace/src/panels/profile.rs | 6 +- crates/workspace/src/panels/relay_list.rs | 9 +- crates/workspace/src/sidebar/mod.rs | 73 +-- web/Cargo.toml | 1 + web/src/lib.rs | 24 +- 24 files changed, 208 insertions(+), 891 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47f46e2..d868c6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1230,7 +1230,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "gpui_util", "indexmap", @@ -1393,6 +1393,7 @@ dependencies = [ "gpui", "gpui_platform", "gpui_web", + "instant", "log", "person", "settings", @@ -1713,7 +1714,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "proc-macro2", "quote", @@ -1825,13 +1826,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] @@ -1906,7 +1907,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 1.1.3+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "vswhom", "winreg", ] @@ -2609,7 +2610,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "accesskit", "anyhow", @@ -2691,7 +2692,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "accesskit", "accesskit_unix", @@ -2743,7 +2744,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "accesskit", "accesskit_macos", @@ -2792,7 +2793,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2803,7 +2804,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "console_error_panic_hook", "gpui", @@ -2816,7 +2817,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "schemars", "serde", @@ -2826,7 +2827,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "gpui", @@ -2837,7 +2838,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "log", @@ -2847,7 +2848,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "console_error_panic_hook", @@ -2870,7 +2871,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "bytemuck", @@ -2900,7 +2901,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "accesskit", "accesskit_windows", @@ -3190,7 +3191,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "async-compression", @@ -3210,7 +3211,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "rustls", "rustls-platform-verifier", @@ -3502,6 +3503,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -4014,7 +4018,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "bindgen", @@ -4917,7 +4921,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "collections", "serde", @@ -5729,7 +5733,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "derive_refineable", ] @@ -5811,7 +5815,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "bytes", @@ -6126,7 +6130,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "async-task", "backtrace", @@ -6750,7 +6754,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "heapless 0.9.3", "log", @@ -7286,9 +7290,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.3+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -7751,7 +7755,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "perf", "quote", @@ -7961,8 +7965,7 @@ dependencies = [ [[package]] name = "wasm_thread" version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7516db7f32decdadb1c3b8deb1b7d78b9df7606c5cc2f6241737c2ab3a0258e" +source = "git+https://github.com/zed-industries/wasm_thread?rev=0cf96c7708dfb97ccf3da50347e25edcf75d6937#0cf96c7708dfb97ccf3da50347e25edcf75d6937" dependencies = [ "futures", "js-sys", @@ -9485,7 +9488,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "anyhow", "chrono", @@ -9502,7 +9505,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" dependencies = [ "tracing", "tracing-subscriber", @@ -9513,7 +9516,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" +source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64" [[package]] name = "zune-core" diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index 9f7a0fe..2754239 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -2,7 +2,6 @@ use std::cmp::Reverse; use std::collections::{BTreeSet, HashMap, HashSet}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, LazyLock, RwLock}; -use instant::Duration; use anyhow::{Error, anyhow}; use common::EventExt; @@ -12,6 +11,7 @@ use gpui::{ App, AppContext, Context, Entity, EventEmitter, Global, SharedString, Subscription, Task, WeakEntity, Window, }; +use instant::Duration; use nostr_sdk::prelude::*; use smallvec::{SmallVec, smallvec}; use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP, UniversalSigner}; @@ -399,7 +399,7 @@ impl ChatRegistry { } let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); - let id = SubscriptionId::new(format!("{}-msg", public_key.to_hex())); + let id = SubscriptionId::new(USER_GIFTWRAP); let target: HashMap = relays .into_iter() diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index 9d3a002..2e69f65 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -1474,12 +1474,7 @@ impl Render for ChatPanel { .gap_2() .border_b_1() .border_color(cx.theme().border) - .child( - Input::new(&self.subject_input) - .text_sm() - .small() - .bordered(false), - ) + .child(Input::new(&self.subject_input).text_sm().small()) .child( Button::new("change") .icon(IconName::CheckCircle) diff --git a/crates/state/src/constants.rs b/crates/state/src/constants.rs index c0473cf..a84bea9 100644 --- a/crates/state/src/constants.rs +++ b/crates/state/src/constants.rs @@ -39,14 +39,15 @@ pub const NOSTR_CONNECT_RELAY: &str = "wss://relay.nip46.com"; pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"]; /// Default search relays -pub const INDEXER_RELAYS: [&str; 1] = ["wss://indexer.coracle.social"]; +pub const INDEXER_RELAYS: [&str; 2] = ["wss://indexer.coracle.social", "wss://user.kindpag.es"]; /// Default search relays pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.today"]; /// Default bootstrap relays -pub const BOOTSTRAP_RELAYS: [&str; 3] = [ +pub const BOOTSTRAP_RELAYS: [&str; 4] = [ "wss://relay.ditto.pub", "wss://relay.primal.net", - "wss://user.kindpag.es", + "wss://relay.nostr.net", + "wss://profiles.nostr1.com", ]; diff --git a/crates/theme/src/colors.rs b/crates/theme/src/colors.rs index 46915dd..fc6bd65 100644 --- a/crates/theme/src/colors.rs +++ b/crates/theme/src/colors.rs @@ -112,7 +112,7 @@ impl ThemeColors { elevated_surface_background: neutral().light().step_3(), panel_background: neutral().light().step_1(), overlay: neutral().light_alpha().step_3(), - title_bar: neutral().light().step_3(), + title_bar: neutral().light().step_2(), title_bar_inactive: neutral().light().step_1(), window_border: hsl(240.0, 5.9, 78.0), @@ -198,7 +198,7 @@ impl ThemeColors { elevated_surface_background: neutral().dark().step_3(), panel_background: neutral().dark().step_1(), overlay: neutral().dark_alpha().step_3(), - title_bar: neutral().dark().step_3(), + title_bar: neutral().dark().step_2(), title_bar_inactive: neutral().dark().step_1(), window_border: hsl(240.0, 3.7, 28.0), diff --git a/crates/theme/src/lib.rs b/crates/theme/src/lib.rs index e11adcd..9804f3e 100644 --- a/crates/theme/src/lib.rs +++ b/crates/theme/src/lib.rs @@ -34,7 +34,7 @@ pub const CLIENT_SIDE_DECORATION_BORDER: Pixels = px(1.0); pub const TITLEBAR_HEIGHT: Pixels = px(36.0); /// Defines workspace tabbar height -pub const TABBAR_HEIGHT: Pixels = px(28.0); +pub const TABBAR_HEIGHT: Pixels = px(36.0); /// Defines default sidebar width pub const SIDEBAR_WIDTH: Pixels = px(240.); diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index ad8f365..b08b9b0 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -2,11 +2,10 @@ use std::sync::Arc; use gpui::prelude::FluentBuilder; use gpui::{ - AnyElement, AnyView, App, AppContext, Axis, Bounds, Context, Decorations, Edges, Entity, - EntityId, EventEmitter, Focusable, InteractiveElement as _, IntoElement, ParentElement as _, - Pixels, Render, SharedString, Styled, Subscription, WeakEntity, Window, actions, div, px, + AnyElement, AnyView, App, AppContext, Axis, Bounds, Context, Edges, Entity, EntityId, + EventEmitter, Focusable, InteractiveElement as _, IntoElement, ParentElement as _, Pixels, + Render, SharedString, Styled, Subscription, WeakEntity, Window, actions, div, px, }; -use theme::CLIENT_SIDE_DECORATION_ROUNDING; use crate::ElementExt; @@ -110,15 +109,8 @@ impl DockItem { window: &mut Window, cx: &mut App, ) -> Self { - let mut items = items; - let stack_panel = cx.new(|cx| { let mut stack_panel = StackPanel::new(axis, window, cx); - for (i, item) in items.iter_mut().enumerate() { - let view = item.view(); - let size = sizes.get(i).copied().flatten(); - stack_panel.add_panel(view.clone(), size, dock_area.clone(), window, cx) - } for (i, item) in items.iter().enumerate() { let view = item.view(); @@ -745,34 +737,22 @@ impl EventEmitter for DockArea {} impl Render for DockArea { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let view = cx.entity().clone(); - let decorations = window.window_decorations(); div() .id("dock-area") .relative() .size_full() - .overflow_hidden() .on_prepaint(move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds)) .map(|this| { if let Some(zoom_view) = self.zoom_view.clone() { - this.map(|this| match decorations { - Decorations::Server => this, - Decorations::Client { tiling } => this - .when(!(tiling.top || tiling.right), |div| { - div.rounded_br(CLIENT_SIDE_DECORATION_ROUNDING) - }) - .when(!(tiling.top || tiling.left), |div| { - div.rounded_bl(CLIENT_SIDE_DECORATION_ROUNDING) - }), - }) - .child(zoom_view) + this.child(zoom_view) } else { // render dock this.child( div() .flex() .flex_row() - .h_full() + .size_full() // Left dock .when_some(self.left_dock.clone(), |this, dock| { this.child(div().flex().flex_none().child(dock)) @@ -783,14 +763,8 @@ impl Render for DockArea { .flex() .flex_1() .flex_col() - .overflow_hidden() // Top center - .child( - div() - .flex_1() - .overflow_hidden() - .child(self.render_items(window, cx)), - ) + .child(div().flex_1().child(self.render_items(window, cx))) // Bottom Dock .when_some(self.bottom_dock.clone(), |this, dock| { this.child(dock) diff --git a/crates/ui/src/dock/stack_panel.rs b/crates/ui/src/dock/stack_panel.rs index c8e68b6..fff92ec 100644 --- a/crates/ui/src/dock/stack_panel.rs +++ b/crates/ui/src/dock/stack_panel.rs @@ -1,13 +1,12 @@ use std::sync::Arc; -use gpui::prelude::FluentBuilder; use gpui::{ App, AppContext, Axis, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Pixels, Render, SharedString, Styled, Subscription, WeakEntity, Window, }; use smallvec::SmallVec; -use theme::{ActiveTheme, AxisExt as _, CLIENT_SIDE_DECORATION_ROUNDING, Placement}; +use theme::{AxisExt as _, Placement}; use super::{DockArea, PanelEvent}; use crate::dock::panel::{Panel, PanelView}; @@ -369,26 +368,20 @@ impl Focusable for StackPanel { } impl EventEmitter for StackPanel {} + impl EventEmitter for StackPanel {} impl Render for StackPanel { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - h_flex() - .size_full() - .overflow_hidden() - .bg(cx.theme().panel_background) - .when(cx.theme().platform.is_linux(), |this| { - this.rounded_br(CLIENT_SIDE_DECORATION_ROUNDING) - }) - .child( - ResizablePanelGroup::new("stack-panel-group") - .with_state(&self.state) - .axis(self.axis) - .children(self.panels.clone().into_iter().map(|panel| { - resizable_panel() - .child(panel.view()) - .visible(panel.visible(cx)) - })), - ) + h_flex().size_full().overflow_hidden().child( + ResizablePanelGroup::new("stack-panel-group") + .with_state(&self.state) + .axis(self.axis) + .children(self.panels.clone().into_iter().map(|panel| { + resizable_panel() + .child(panel.view()) + .visible(panel.visible(cx)) + })), + ) } } diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index 36f4b03..24f6387 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -7,7 +7,7 @@ use gpui::{ ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window, div, px, rems, }; -use theme::{ActiveTheme, AxisExt, CLIENT_SIDE_DECORATION_ROUNDING, Placement, TABBAR_HEIGHT}; +use theme::{ActiveTheme, AxisExt, Placement, TABBAR_HEIGHT}; use crate::button::{Button, ButtonVariants as _}; use crate::dock::dock::DockPlacement; @@ -567,6 +567,7 @@ impl TabPanel { let left_dock_button = self.render_dock_toggle_button(DockPlacement::Left, window, cx); let bottom_dock_button = self.render_dock_toggle_button(DockPlacement::Bottom, window, cx); let right_dock_button = self.render_dock_toggle_button(DockPlacement::Right, window, cx); + let has_extend_dock_button = left_dock_button.is_some() || bottom_dock_button.is_some(); let tabs_count = self.panels.len(); let is_bottom_dock = bottom_dock_button.is_some(); @@ -586,6 +587,7 @@ impl TabPanel { .py_2() .pl_3() .pr_2() + .rounded_t(cx.theme().radius_lg) .bg(cx.theme().panel_background) .when(left_dock_button.is_some(), |this| this.pl_2()) .when(right_dock_button.is_some(), |this| this.pr_2()) @@ -641,17 +643,14 @@ impl TabPanel { TabBar::new("tab-bar") .track_scroll(&self.tab_bar_scroll_handle) .h(TABBAR_HEIGHT) + .bg(cx.theme().panel_background) + .rounded_t(cx.theme().radius_lg) .when(has_extend_dock_button, |this| { this.prefix( h_flex() .items_center() .top_0() .right(-px(1.)) - .border_r_1() - .border_b_1() - .h_full() - .border_color(cx.theme().border) - .bg(cx.theme().tab_background) .pl_0p5() .pr_1() .children(left_dock_button) @@ -780,12 +779,8 @@ impl TabPanel { .top_0() .right_0() .h_full() - .border_l_1() - .border_b_1() .px_0p5() .gap_1() - .border_color(cx.theme().border) - .bg(cx.theme().tab_background) .child(self.render_toolbar(state, window, cx)) .when_some(right_dock_button, |this, btn| this.child(btn)), ) @@ -815,10 +810,8 @@ impl TabPanel { .child( div() .size_full() + .rounded_b(cx.theme().radius_lg) .bg(cx.theme().panel_background) - .when(cx.theme().platform.is_linux(), |this| { - this.rounded_b(CLIENT_SIDE_DECORATION_ROUNDING) - }) .overflow_hidden() .child( active_panel @@ -1140,17 +1133,24 @@ impl Render for TabPanel { state.closable = false; } - v_flex() + div() .when(!self.collapsed, |this| { this.on_action(cx.listener(Self::on_action_toggle_zoom)) .on_action(cx.listener(Self::on_action_close_panel)) }) .id("tab-panel") - .tab_group() .track_focus(&focus_handle) .size_full() + .p_1() .overflow_hidden() - .child(self.render_title_bar(&state, window, cx)) - .child(self.render_active_panel(&state, window, cx)) + .child( + v_flex() + .rounded(cx.theme().radius_lg) + .when(cx.theme().shadow, |this| this.shadow_xs()) + .size_full() + .tab_group() + .child(self.render_title_bar(&state, window, cx)) + .child(self.render_active_panel(&state, window, cx)), + ) } } diff --git a/crates/ui/src/input/input.rs b/crates/ui/src/input/input.rs index 47d760a..5efa0a0 100644 --- a/crates/ui/src/input/input.rs +++ b/crates/ui/src/input/input.rs @@ -35,8 +35,6 @@ pub struct Input { cleanable: bool, mask_toggle: bool, disabled: bool, - bordered: bool, - focus_bordered: bool, tab_index: isize, selected: bool, } @@ -73,8 +71,6 @@ impl Input { cleanable: false, mask_toggle: false, disabled: false, - bordered: true, - focus_bordered: true, tab_index: 0, selected: false, } @@ -108,18 +104,6 @@ impl Input { self } - /// Set the bordered for the input, default: true - pub fn bordered(mut self, bordered: bool) -> Self { - self.bordered = bordered; - self - } - - /// Set focus border for the input, default is true. - pub fn focus_bordered(mut self, bordered: bool) -> Self { - self.focus_bordered = bordered; - self - } - /// Set whether to show the clear button when the input field is not empty, default is false. pub fn cleanable(mut self, cleanable: bool) -> Self { self.cleanable = cleanable; @@ -332,11 +316,6 @@ impl RenderOnce for Input { this.bg(bg) .when(self.disabled, |this| this.opacity(0.5)) .rounded(cx.theme().radius) - .when(self.bordered, |this| { - this.border_color(cx.theme().border) - .border_1() - .when(cx.theme().shadow, |this| this.shadow_xs()) - }) }) .items_center() .gap(gap_x) diff --git a/crates/ui/src/resizable/resize_handle.rs b/crates/ui/src/resizable/resize_handle.rs index e982c9f..2cbd353 100644 --- a/crates/ui/src/resizable/resize_handle.rs +++ b/crates/ui/src/resizable/resize_handle.rs @@ -117,7 +117,7 @@ impl Element for ResizeHandle { cx.theme().border }; - let mut el = div() + let mut ele = div() .id(self.id.clone()) .occlude() .absolute() @@ -160,16 +160,15 @@ impl Element for ResizeHandle { }) .child( div() - .bg(bg_color) .group_hover("handle", |this| this.bg(bg_color)) .when(axis.is_horizontal(), |this| this.h_full().w(HANDLE_SIZE)) .when(axis.is_vertical(), |this| this.w_full().h(HANDLE_SIZE)), ) .into_any_element(); - let layout_id = el.request_layout(window, cx); + let layout_id = ele.request_layout(window, cx); - ((layout_id, el), state) + ((layout_id, ele), state) }) } diff --git a/crates/ui/src/tab/mod.rs b/crates/ui/src/tab/mod.rs index 25d0025..0bf2665 100644 --- a/crates/ui/src/tab/mod.rs +++ b/crates/ui/src/tab/mod.rs @@ -2,396 +2,15 @@ use std::rc::Rc; use gpui::prelude::FluentBuilder as _; use gpui::{ - AnyElement, App, ClickEvent, Div, Edges, Hsla, InteractiveElement, IntoElement, MouseButton, - ParentElement, Pixels, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, - div, px, relative, + AnyElement, App, ClickEvent, Div, InteractiveElement, IntoElement, MouseButton, ParentElement, + RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, div, px, relative, }; -use theme::ActiveTheme; +use theme::{ActiveTheme, TABBAR_HEIGHT}; -use crate::{Icon, IconName, Selectable, Sizable, Size, StyledExt, h_flex}; +use crate::{Icon, IconName, Selectable, h_flex}; pub mod tab_bar; -/// Tab variants. -#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)] -pub enum TabVariant { - #[default] - Tab, - Outline, - Pill, - Segmented, - Underline, -} - -impl TabVariant { - fn height(&self, size: Size) -> Pixels { - match size { - Size::XSmall => match self { - TabVariant::Underline => px(26.), - _ => px(20.), - }, - Size::Small => match self { - TabVariant::Underline => px(30.), - _ => px(24.), - }, - Size::Large => match self { - TabVariant::Underline => px(44.), - _ => px(36.), - }, - _ => match self { - TabVariant::Underline => px(36.), - _ => px(32.), - }, - } - } - - fn inner_height(&self, size: Size) -> Pixels { - match size { - Size::XSmall => match self { - TabVariant::Tab | TabVariant::Outline | TabVariant::Pill => px(18.), - TabVariant::Segmented => px(16.), - TabVariant::Underline => px(20.), - }, - Size::Small => match self { - TabVariant::Tab | TabVariant::Outline | TabVariant::Pill => px(22.), - TabVariant::Segmented => px(18.), - TabVariant::Underline => px(22.), - }, - Size::Large => match self { - TabVariant::Tab | TabVariant::Outline | TabVariant::Pill => px(36.), - TabVariant::Segmented => px(28.), - TabVariant::Underline => px(32.), - }, - _ => match self { - TabVariant::Tab => px(30.), - TabVariant::Outline | TabVariant::Pill => px(26.), - TabVariant::Segmented => px(24.), - TabVariant::Underline => px(26.), - }, - } - } - - /// Default px(12) to match panel px_3, See [`crate::dock::TabPanel`] - fn inner_paddings(&self, size: Size) -> Edges { - let mut padding_x = match size { - Size::XSmall => px(8.), - Size::Small => px(10.), - Size::Large => px(16.), - _ => px(12.), - }; - - if matches!(self, TabVariant::Underline) { - padding_x = px(0.); - } - - Edges { - left: padding_x, - right: padding_x, - ..Default::default() - } - } - - fn inner_margins(&self, size: Size) -> Edges { - match size { - Size::XSmall => match self { - TabVariant::Underline => Edges { - top: px(1.), - bottom: px(2.), - ..Default::default() - }, - _ => Edges::all(px(0.)), - }, - Size::Small => match self { - TabVariant::Underline => Edges { - top: px(2.), - bottom: px(3.), - ..Default::default() - }, - _ => Edges::all(px(0.)), - }, - Size::Large => match self { - TabVariant::Underline => Edges { - top: px(5.), - bottom: px(6.), - ..Default::default() - }, - _ => Edges::all(px(0.)), - }, - _ => match self { - TabVariant::Underline => Edges { - top: px(3.), - bottom: px(4.), - ..Default::default() - }, - _ => Edges::all(px(0.)), - }, - } - } - - fn normal(&self, cx: &App) -> TabStyle { - match self { - TabVariant::Tab => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - borders: Edges { - left: px(1.), - right: px(1.), - ..Default::default() - }, - border_color: gpui::transparent_black(), - ..Default::default() - }, - TabVariant::Outline => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - borders: Edges::all(px(1.)), - border_color: cx.theme().border, - ..Default::default() - }, - TabVariant::Pill => TabStyle { - fg: cx.theme().text, - bg: gpui::transparent_black(), - ..Default::default() - }, - TabVariant::Segmented => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - ..Default::default() - }, - TabVariant::Underline => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - inner_bg: gpui::transparent_black(), - borders: Edges { - bottom: px(2.), - ..Default::default() - }, - border_color: gpui::transparent_black(), - ..Default::default() - }, - } - } - - fn hovered(&self, selected: bool, cx: &App) -> TabStyle { - match self { - TabVariant::Tab => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - borders: Edges { - left: px(1.), - right: px(1.), - ..Default::default() - }, - border_color: gpui::transparent_black(), - ..Default::default() - }, - TabVariant::Outline => TabStyle { - fg: cx.theme().secondary_foreground, - bg: cx.theme().secondary_hover, - borders: Edges::all(px(1.)), - border_color: cx.theme().border, - ..Default::default() - }, - TabVariant::Pill => TabStyle { - fg: cx.theme().secondary_foreground, - bg: cx.theme().secondary_background, - ..Default::default() - }, - TabVariant::Segmented => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - inner_bg: if selected { - cx.theme().background - } else { - gpui::transparent_black() - }, - ..Default::default() - }, - TabVariant::Underline => TabStyle { - fg: cx.theme().tab_foreground, - bg: gpui::transparent_black(), - inner_bg: gpui::transparent_black(), - borders: Edges { - bottom: px(2.), - ..Default::default() - }, - border_color: gpui::transparent_black(), - ..Default::default() - }, - } - } - - fn selected(&self, cx: &App) -> TabStyle { - match self { - TabVariant::Tab => TabStyle { - fg: cx.theme().tab_active_foreground, - bg: cx.theme().tab_active_background, - borders: Edges { - left: px(1.), - right: px(1.), - ..Default::default() - }, - border_color: cx.theme().border, - ..Default::default() - }, - TabVariant::Outline => TabStyle { - fg: cx.theme().text_accent, - bg: gpui::transparent_black(), - borders: Edges::all(px(1.)), - border_color: cx.theme().element_active, - ..Default::default() - }, - TabVariant::Pill => TabStyle { - fg: cx.theme().element_foreground, - bg: cx.theme().element_background, - ..Default::default() - }, - TabVariant::Segmented => TabStyle { - fg: cx.theme().tab_active_foreground, - bg: gpui::transparent_black(), - inner_bg: cx.theme().background, - shadow: true, - ..Default::default() - }, - TabVariant::Underline => TabStyle { - fg: cx.theme().tab_active_foreground, - bg: gpui::transparent_black(), - borders: Edges { - bottom: px(2.), - ..Default::default() - }, - border_color: cx.theme().element_active, - ..Default::default() - }, - } - } - - fn disabled(&self, selected: bool, cx: &App) -> TabStyle { - match self { - TabVariant::Tab => TabStyle { - fg: cx.theme().text_muted, - bg: gpui::transparent_black(), - border_color: if selected { - cx.theme().border - } else { - gpui::transparent_black() - }, - borders: Edges { - left: px(1.), - right: px(1.), - ..Default::default() - }, - ..Default::default() - }, - TabVariant::Outline => TabStyle { - fg: cx.theme().text_muted, - bg: gpui::transparent_black(), - borders: Edges::all(px(1.)), - border_color: if selected { - cx.theme().element_active - } else { - cx.theme().border - }, - ..Default::default() - }, - TabVariant::Pill => TabStyle { - fg: if selected { - cx.theme().element_foreground.opacity(0.5) - } else { - cx.theme().text_muted - }, - bg: if selected { - cx.theme().element_background.opacity(0.5) - } else { - gpui::transparent_black() - }, - ..Default::default() - }, - TabVariant::Segmented => TabStyle { - fg: cx.theme().text_muted, - bg: cx.theme().tab_background, - inner_bg: if selected { - cx.theme().background - } else { - gpui::transparent_black() - }, - ..Default::default() - }, - TabVariant::Underline => TabStyle { - fg: cx.theme().text_muted, - bg: gpui::transparent_black(), - border_color: if selected { - cx.theme().border - } else { - gpui::transparent_black() - }, - borders: Edges { - bottom: px(2.), - ..Default::default() - }, - ..Default::default() - }, - } - } - - pub(super) fn tab_bar_radius(&self, size: Size, cx: &App) -> Pixels { - if *self != TabVariant::Segmented { - return px(0.); - } - - match size { - Size::XSmall | Size::Small => cx.theme().radius, - Size::Large => cx.theme().radius_lg, - _ => cx.theme().radius_lg, - } - } - - fn radius(&self, size: Size, cx: &App) -> Pixels { - match self { - TabVariant::Outline | TabVariant::Pill => px(99.), - TabVariant::Segmented => match size { - Size::XSmall | Size::Small => cx.theme().radius, - Size::Large => cx.theme().radius_lg, - _ => cx.theme().radius_lg, - }, - _ => px(0.), - } - } - - fn inner_radius(&self, size: Size, cx: &App) -> Pixels { - match self { - TabVariant::Segmented => match size { - Size::Large => self.tab_bar_radius(size, cx) - px(3.), - _ => self.tab_bar_radius(size, cx) - px(2.), - }, - _ => px(0.), - } - } -} - -#[allow(dead_code)] -struct TabStyle { - borders: Edges, - border_color: Hsla, - bg: Hsla, - fg: Hsla, - shadow: bool, - inner_bg: Hsla, -} - -impl Default for TabStyle { - fn default() -> Self { - TabStyle { - borders: Edges::all(px(0.)), - border_color: gpui::transparent_white(), - bg: gpui::transparent_white(), - fg: gpui::transparent_white(), - shadow: false, - inner_bg: gpui::transparent_white(), - } - } -} - #[allow(clippy::type_complexity)] /// A Tab element for the [`super::TabBar`]. #[derive(IntoElement)] @@ -404,8 +23,6 @@ pub struct Tab { pub(super) tab_bar_prefix: Option, suffix: Option, children: Vec, - variant: TabVariant, - size: Size, pub(super) disabled: bool, pub(super) selected: bool, on_click: Option>, @@ -454,8 +71,6 @@ impl Default for Tab { selected: false, prefix: None, suffix: None, - variant: TabVariant::default(), - size: Size::default(), on_click: None, } } @@ -479,36 +94,6 @@ impl Tab { self } - /// Set Tab Variant. - pub fn with_variant(mut self, variant: TabVariant) -> Self { - self.variant = variant; - self - } - - /// Use Pill variant. - pub fn pill(mut self) -> Self { - self.variant = TabVariant::Pill; - self - } - - /// Use outline variant. - pub fn outline(mut self) -> Self { - self.variant = TabVariant::Outline; - self - } - - /// Use Segmented variant. - pub fn segmented(mut self) -> Self { - self.variant = TabVariant::Segmented; - self - } - - /// Use Underline variant. - pub fn underline(mut self) -> Self { - self.variant = TabVariant::Underline; - self - } - /// Set the left side of the tab pub fn prefix(mut self, prefix: impl IntoElement) -> Self { self.prefix = Some(prefix.into_any_element()); @@ -580,42 +165,16 @@ impl Styled for Tab { } } -impl Sizable for Tab { - fn with_size(mut self, size: impl Into) -> Self { - self.size = size.into(); - self - } -} - impl RenderOnce for Tab { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { - let mut tab_style = if self.selected { - self.variant.selected(cx) + let fg = if self.disabled { + cx.theme().text_muted + } else if self.selected { + cx.theme().tab_active_foreground } else { - self.variant.normal(cx) + cx.theme().tab_foreground }; - let mut hover_style = self.variant.hovered(self.selected, cx); - - if self.disabled { - tab_style = self.variant.disabled(self.selected, cx); - hover_style = self.variant.disabled(self.selected, cx); - } - - let tab_bar_prefix = self.tab_bar_prefix.unwrap_or_default(); - - if !tab_bar_prefix && self.ix == 0 && self.variant == TabVariant::Tab { - tab_style.borders.left = px(0.); - hover_style.borders.left = px(0.); - } - - let radius = self.variant.radius(self.size, cx); - let inner_radius = self.variant.inner_radius(self.size, cx); - let inner_paddings = self.variant.inner_paddings(self.size); - let inner_margins = self.variant.inner_margins(self.size); - let inner_height = self.variant.inner_height(self.size); - let height = self.variant.height(self.size); - self.base .id(self.ix) .flex() @@ -623,74 +182,39 @@ impl RenderOnce for Tab { .gap_1() .items_center() .flex_shrink_0() - .h(height) + .h(TABBAR_HEIGHT) .overflow_hidden() - .text_color(tab_style.fg) - .map(|this| match self.size { - Size::XSmall => this.text_xs(), - Size::Large => this.text_base(), - _ => this.text_sm(), - }) - .bg(tab_style.bg) - .border_l(tab_style.borders.left) - .border_r(tab_style.borders.right) - .border_t(tab_style.borders.top) - .border_b(tab_style.borders.bottom) - .border_color(tab_style.border_color) - .rounded(radius) + .text_color(fg) + .text_sm() .when(!self.selected && !self.disabled, |this| { - this.hover(|this| { - this.text_color(hover_style.fg) - .bg(hover_style.bg) - .border_l(hover_style.borders.left) - .border_r(hover_style.borders.right) - .border_t(hover_style.borders.top) - .border_b(hover_style.borders.bottom) - .border_color(hover_style.border_color) - .rounded(radius) - }) + this.hover(|this| this.text_color(cx.theme().secondary_foreground)) }) .when_some(self.prefix, |this, prefix| this.child(prefix)) .child( h_flex() .flex_1() - .h(inner_height) + .h(px(30.)) .line_height(relative(1.)) .whitespace_nowrap() .items_center() .justify_center() .overflow_hidden() - .margins(inner_margins) .flex_shrink_0() + .px_3() .map(|this| match self.icon { - Some(icon) => { - this.w(inner_height * 1.25) - .child(icon.map(|this| match self.size { - Size::XSmall => this.size_2p5(), - Size::Small => this.size_3p5(), - Size::Large => this.size_4(), - _ => this.size_4(), - })) - } + Some(icon) => this.w(px(38.)).child(icon.size_4()), None => this - .paddings(inner_paddings) .map(|this| match self.label { Some(label) => this.child(label), None => this, }) .children(self.children), - }) - .bg(tab_style.inner_bg) - .rounded(inner_radius) - .when(tab_style.shadow, |this| this.shadow_xs()) - .hover(|this| this.bg(hover_style.inner_bg).rounded(inner_radius)), + }), ) .when_some(self.suffix, |this, suffix| { this.child(div().pr_2().child(suffix)) }) - .on_mouse_down(MouseButton::Left, |_, _, cx| { - // Stop propagation behavior, for works on TitleBar. - // https://github.com/longbridge/gpui-component/issues/1836 + .on_mouse_down(MouseButton::Left, |_ev, _window, cx| { cx.stop_propagation(); }) .when(!self.disabled, |this| { diff --git a/crates/ui/src/tab/tab_bar.rs b/crates/ui/src/tab/tab_bar.rs index 71e6036..11a6463 100644 --- a/crates/ui/src/tab/tab_bar.rs +++ b/crates/ui/src/tab/tab_bar.rs @@ -2,19 +2,17 @@ use std::rc::Rc; use gpui::prelude::FluentBuilder as _; use gpui::{ - Anchor, AnyElement, App, Div, Edges, ElementId, InteractiveElement, IntoElement, ParentElement, + Anchor, AnyElement, App, Div, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, ScrollHandle, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, div, px, }; use smallvec::SmallVec; -use theme::ActiveTheme; -use super::{Tab, TabVariant}; +use super::Tab; use crate::button::{Button, ButtonVariants as _}; use crate::menu::{DropdownMenu as _, PopupMenuItem}; -use crate::{IconName, Selectable, Sizable, Size, StyledExt, h_flex}; +use crate::{IconName, Selectable, Sizable, StyledExt, h_flex}; -#[allow(clippy::type_complexity)] /// A TabBar element that contains multiple [`Tab`] items. #[derive(IntoElement)] pub struct TabBar { @@ -26,9 +24,8 @@ pub struct TabBar { children: SmallVec<[Tab; 2]>, last_empty_space: AnyElement, selected_index: Option, - variant: TabVariant, - size: Size, menu: bool, + #[allow(clippy::type_complexity)] on_click: Option>, } @@ -42,8 +39,6 @@ impl TabBar { scroll_handle: None, prefix: None, suffix: None, - variant: TabVariant::default(), - size: Size::default(), last_empty_space: div().w_3().into_any_element(), selected_index: None, on_click: None, @@ -51,36 +46,6 @@ impl TabBar { } } - /// Set the Tab variant, all children will inherit the variant. - pub fn with_variant(mut self, variant: TabVariant) -> Self { - self.variant = variant; - self - } - - /// Set the Tab variant to Pill, all children will inherit the variant. - pub fn pill(mut self) -> Self { - self.variant = TabVariant::Pill; - self - } - - /// Set the Tab variant to Outline, all children will inherit the variant. - pub fn outline(mut self) -> Self { - self.variant = TabVariant::Outline; - self - } - - /// Set the Tab variant to Segmented, all children will inherit the variant. - pub fn segmented(mut self) -> Self { - self.variant = TabVariant::Segmented; - self - } - - /// Set the Tab variant to Underline, all children will inherit the variant. - pub fn underline(mut self) -> Self { - self.variant = TabVariant::Underline; - self - } - /// Set whether to show the menu button when tabs overflow, default is false. pub fn menu(mut self, menu: bool) -> Self { self.menu = menu; @@ -105,13 +70,13 @@ impl TabBar { self } - /// Add children of the TabBar, all children will inherit the variant. + /// Add children of the TabBar. pub fn children(mut self, children: impl IntoIterator>) -> Self { self.children.extend(children.into_iter().map(Into::into)); self } - /// Add child of the TabBar, tab will inherit the variant. + /// Add child of the TabBar. pub fn child(mut self, child: impl Into) -> Self { self.children.push(child.into()); self @@ -147,60 +112,8 @@ impl Styled for TabBar { } } -impl Sizable for TabBar { - fn with_size(mut self, size: impl Into) -> Self { - self.size = size.into(); - self - } -} - impl RenderOnce for TabBar { - fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { - let default_gap = match self.size { - Size::Small | Size::XSmall => px(8.), - Size::Large => px(16.), - _ => px(12.), - }; - let (bg, paddings, gap) = match self.variant { - TabVariant::Tab => { - let padding = Edges::all(px(0.)); - (cx.theme().tab_background, padding, px(0.)) - } - TabVariant::Outline => { - let padding = Edges::all(px(0.)); - (gpui::transparent_black(), padding, default_gap) - } - TabVariant::Pill => { - let padding = Edges::all(px(0.)); - (gpui::transparent_black(), padding, px(4.)) - } - TabVariant::Segmented => { - let padding_x = match self.size { - Size::XSmall => px(2.), - Size::Small => px(3.), - _ => px(4.), - }; - let padding = Edges { - left: padding_x, - right: padding_x, - ..Default::default() - }; - - (cx.theme().tab_background, padding, px(2.)) - } - TabVariant::Underline => { - // This gap is same as the tab inner_paddings - let gap = match self.size { - Size::XSmall => px(10.), - Size::Small => px(12.), - Size::Large => px(20.), - _ => px(16.), - }; - - (gpui::transparent_black(), Edges::all(px(0.)), gap) - } - }; - + fn render(self, _: &mut Window, _cx: &mut App) -> impl IntoElement { let mut item_labels = Vec::new(); let selected_index = self.selected_index; let on_click = self.on_click.clone(); @@ -210,25 +123,6 @@ impl RenderOnce for TabBar { .relative() .flex() .items_center() - .bg(bg) - .text_color(cx.theme().tab_foreground) - .when( - self.variant == TabVariant::Underline || self.variant == TabVariant::Tab, - |this| { - this.child( - div() - .id("border-b") - .absolute() - .left_0() - .bottom_0() - .size_full() - .border_b_1() - .border_color(cx.theme().border), - ) - }, - ) - .rounded(self.variant.tab_bar_radius(self.size, cx)) - .paddings(paddings) .refine_style(&self.style) .when_some(self.prefix, |this, prefix| this.child(prefix)) .child( @@ -239,15 +133,13 @@ impl RenderOnce for TabBar { .when_some(self.scroll_handle, |this, scroll_handle| { this.track_scroll(&scroll_handle) }) - .gap(gap) + .gap(px(0.)) .children(self.children.into_iter().enumerate().map(|(ix, child)| { item_labels.push((child.label.clone(), child.disabled)); let tab_bar_prefix = child.tab_bar_prefix.unwrap_or(true); child .ix(ix) .tab_bar_prefix(tab_bar_prefix) - .with_variant(self.variant) - .with_size(self.size) .when_some(self.selected_index, |this, selected_ix| { this.selected(selected_ix == ix) }) diff --git a/crates/ui/src/title_bar.rs b/crates/ui/src/title_bar.rs index 8ffc8bd..83783f5 100644 --- a/crates/ui/src/title_bar.rs +++ b/crates/ui/src/title_bar.rs @@ -283,8 +283,6 @@ impl RenderOnce for TitleBar { this.px_2() } }) - .border_b_1() - .border_color(cx.theme().border) .bg(cx.theme().title_bar) .refine_style(&self.style) .when(is_linux, |this| { diff --git a/crates/workspace/src/dialogs/import.rs b/crates/workspace/src/dialogs/import.rs index 3ff1189..06c64e2 100644 --- a/crates/workspace/src/dialogs/import.rs +++ b/crates/workspace/src/dialogs/import.rs @@ -1,11 +1,10 @@ -use instant::Duration; - use anyhow::{Error, anyhow}; use gpui::prelude::FluentBuilder; use gpui::{ AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, Window, div, }; +use instant::Duration; use nostr_connect::prelude::*; use state::{CoopAuthUrlHandler, NostrRegistry, USER_KEYRING}; use theme::ActiveTheme; @@ -36,7 +35,7 @@ pub struct ImportIdentity { impl ImportIdentity { pub fn new(window: &mut Window, cx: &mut Context) -> Self { - let key_input = cx.new(|cx| InputState::new(window, cx)); + let key_input = cx.new(|cx| InputState::new(window, cx).placeholder("nsec or bunker://")); let pass_input = cx.new(|cx| InputState::new(window, cx).masked(true)); let error = cx.new(|_| None); @@ -135,7 +134,6 @@ impl ImportIdentity { })?; } } - Ok(()) })); } @@ -208,40 +206,45 @@ impl Render for ImportIdentity { v_flex() .size_full() - .gap_2() + .gap_4() + .text_sm() .child( v_flex() - .gap_1() - .text_color(cx.theme().text_muted) - .child("Continue with existing key or bunker connection") - .child(Input::new(&self.key_input)), + .gap_2() + .child( + v_flex() + .gap_1() + .text_color(cx.theme().text_muted) + .child("Continue with existing key or bunker connection") + .child(Input::new(&self.key_input)), + ) + .when(require_password, |this| { + this.child( + v_flex() + .gap_1() + .text_color(cx.theme().text_muted) + .child("Decrypt Password:") + .child(Input::new(&self.pass_input)), + ) + }) + .when(key_warning, |this| { + this.child( + div() + .text_xs() + .text_color(cx.theme().text_warning) + .child(div().font_semibold().child("Warning")) + .child(div().child(MSG)), + ) + }), ) - .when(require_password, |this| { - this.child( - v_flex() - .gap_1() - .text_color(cx.theme().text_muted) - .child("Decrypt Password:") - .child(Input::new(&self.pass_input)), - ) - }) - .when(key_warning, |this| { - this.child( - div() - .v_flex() - .text_xs() - .text_color(cx.theme().text_warning) - .child(div().font_semibold().child("Warning")) - .child(div().child(MSG)), - ) - }) .child( Button::new("login") .label("Continue") .primary() + .font_semibold() .loading(self.loading) .disabled(self.loading) - .on_click(cx.listener(move |this, _, window, cx| { + .on_click(cx.listener(move |this, _ev, window, cx| { this.login(window, cx); })), ) diff --git a/crates/workspace/src/lib.rs b/crates/workspace/src/lib.rs index 143185e..4276a86 100644 --- a/crates/workspace/src/lib.rs +++ b/crates/workspace/src/lib.rs @@ -28,6 +28,7 @@ use crate::dialogs::import::ImportIdentity; use crate::dialogs::restore::RestoreEncryption; use crate::dialogs::settings; use crate::panels::{backup, contact_list, greeter, messaging_relays, profile, relay_list, trash}; +use crate::sidebar::Sidebar; mod dialogs; mod panels; @@ -60,6 +61,7 @@ enum Command { } pub struct Workspace { + sidebar: Entity, /// App's Dock Area dock: Entity, @@ -79,14 +81,10 @@ impl Workspace { let device = DeviceRegistry::global(cx); let nostr = NostrRegistry::global(cx); - let dock = cx.new(|cx| { - let mut this = DockArea::new(window, cx); - let left = DockItem::panel(Arc::new(sidebar::init(window, cx))); - this.set_left_dock(left, Some(SIDEBAR_WIDTH), true, window, cx); - this - }); - + let sidebar = cx.new(|cx| Sidebar::new(window, cx)); + let dock = cx.new(|cx| DockArea::new(window, cx)); let image_cache = CoopImageCache::new(IMAGE_CACHE_SIZE, cx); + let mut subscriptions = smallvec![]; subscriptions.push( @@ -245,6 +243,7 @@ impl Workspace { }); Self { + sidebar, dock, image_cache, tasks: vec![], @@ -826,8 +825,19 @@ impl Render for Workspace { .child(self.titlebar_left(cx)) .child(self.titlebar_right(cx)), ) - // Dock - .child(self.dock.clone()), + // Main + .child( + h_flex() + .size_full() + .child( + div() + .flex_shrink_0() + .h_full() + .w(SIDEBAR_WIDTH) + .child(self.sidebar.clone()), + ) + .child(self.dock.clone()), + ), ), ) // Notifications diff --git a/crates/workspace/src/panels/backup.rs b/crates/workspace/src/panels/backup.rs index df130cd..51dd1ca 100644 --- a/crates/workspace/src/panels/backup.rs +++ b/crates/workspace/src/panels/backup.rs @@ -1,10 +1,9 @@ -use instant::Duration; - use anyhow::Error; use gpui::{ AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Task, Window, div, }; +use instant::Duration; use nostr_sdk::prelude::*; use state::USER_KEYRING; use theme::ActiveTheme; @@ -155,12 +154,7 @@ impl Render for BackupPanel { .text_color(cx.theme().text_muted) .child(SharedString::from("Public Key:")), ) - .child( - Input::new(&self.npub_input) - .small() - .bordered(false) - .disabled(true), - ), + .child(Input::new(&self.npub_input).small().disabled(true)), ) .child( v_flex() @@ -173,12 +167,7 @@ impl Render for BackupPanel { .text_color(cx.theme().text_muted) .child(SharedString::from("Secret Key:")), ) - .child( - Input::new(&self.nsec_input) - .small() - .bordered(false) - .disabled(true), - ), + .child(Input::new(&self.nsec_input).small().disabled(true)), ) .child( Button::new("copy") diff --git a/crates/workspace/src/panels/contact_list.rs b/crates/workspace/src/panels/contact_list.rs index 0e035fb..6136af5 100644 --- a/crates/workspace/src/panels/contact_list.rs +++ b/crates/workspace/src/panels/contact_list.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use instant::Duration; use anyhow::Error; use gpui::prelude::FluentBuilder; @@ -8,6 +7,7 @@ use gpui::{ InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, TextAlign, Window, div, rems, }; +use instant::Duration; use nostr_sdk::prelude::*; use person::PersonRegistry; use smallvec::{SmallVec, smallvec}; @@ -303,12 +303,7 @@ impl Render for ContactListPanel { h_flex() .gap_1() .w_full() - .child( - Input::new(&self.input) - .small() - .bordered(false) - .cleanable(true), - ) + .child(Input::new(&self.input).small().cleanable(true)) .child( Button::new("add") .icon(IconName::Plus) diff --git a/crates/workspace/src/panels/messaging_relays.rs b/crates/workspace/src/panels/messaging_relays.rs index f95af38..91f5249 100644 --- a/crates/workspace/src/panels/messaging_relays.rs +++ b/crates/workspace/src/panels/messaging_relays.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use instant::Duration; use anyhow::{Error, anyhow}; use gpui::prelude::FluentBuilder; @@ -8,6 +7,7 @@ use gpui::{ InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, TextAlign, Window, div, rems, }; +use instant::Duration; use nostr_sdk::prelude::*; use smallvec::{SmallVec, smallvec}; use state::NostrRegistry; @@ -320,12 +320,7 @@ impl Render for MessagingRelayPanel { h_flex() .gap_1() .w_full() - .child( - Input::new(&self.input) - .small() - .bordered(false) - .cleanable(true), - ) + .child(Input::new(&self.input).small().cleanable(true)) .child( Button::new("add") .icon(IconName::Plus) diff --git a/crates/workspace/src/panels/profile.rs b/crates/workspace/src/panels/profile.rs index 39c8e91..f8dfb52 100644 --- a/crates/workspace/src/panels/profile.rs +++ b/crates/workspace/src/panels/profile.rs @@ -353,7 +353,7 @@ impl Render for ProfilePanel { .text_color(cx.theme().text_muted) .child(SharedString::from("What should people call you?")), ) - .child(Input::new(&self.name_input).bordered(false).small()), + .child(Input::new(&self.name_input).small()), ) .child( v_flex() @@ -364,7 +364,7 @@ impl Render for ProfilePanel { .text_color(cx.theme().text_muted) .child(SharedString::from("A short introduction about you:")), ) - .child(Input::new(&self.bio_input).bordered(false).small()), + .child(Input::new(&self.bio_input).small()), ) .child( v_flex() @@ -375,7 +375,7 @@ impl Render for ProfilePanel { .text_color(cx.theme().text_muted) .child(SharedString::from("Website:")), ) - .child(Input::new(&self.website_input).bordered(false).small()), + .child(Input::new(&self.website_input).small()), ) .child( v_flex() diff --git a/crates/workspace/src/panels/relay_list.rs b/crates/workspace/src/panels/relay_list.rs index f82e8f6..d066771 100644 --- a/crates/workspace/src/panels/relay_list.rs +++ b/crates/workspace/src/panels/relay_list.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use instant::Duration; use anyhow::{Error, anyhow}; use gpui::prelude::FluentBuilder; @@ -8,6 +7,7 @@ use gpui::{ InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, TextAlign, Window, div, px, rems, }; +use instant::Duration; use nostr_sdk::prelude::*; use serde::Deserialize; use smallvec::{SmallVec, smallvec}; @@ -371,12 +371,7 @@ impl Render for RelayListPanel { h_flex() .gap_1() .w_full() - .child( - Input::new(&self.input) - .small() - .bordered(false) - .cleanable(true), - ) + .child(Input::new(&self.input).small().cleanable(true)) .child( Button::new("metadata") .map(|this| { diff --git a/crates/workspace/src/sidebar/mod.rs b/crates/workspace/src/sidebar/mod.rs index ec61c89..18aaf45 100644 --- a/crates/workspace/src/sidebar/mod.rs +++ b/crates/workspace/src/sidebar/mod.rs @@ -1,6 +1,5 @@ use std::collections::HashSet; use std::ops::Range; -use instant::Duration; use anyhow::Error; use chat::{ChatEvent, ChatRegistry, Room, RoomKind}; @@ -12,11 +11,12 @@ use gpui::{ ParentElement, Render, SharedString, Styled, Subscription, Task, UniformListScrollHandle, Window, div, uniform_list, }; +use instant::Duration; use nostr_sdk::prelude::*; use person::PersonRegistry; use smallvec::{SmallVec, smallvec}; use state::{FIND_DELAY, IMAGE_CACHE_SIZE, NostrRegistry}; -use theme::{ActiveTheme, SIDEBAR_WIDTH, TABBAR_HEIGHT}; +use theme::{ActiveTheme, SIDEBAR_WIDTH}; use ui::button::{Button, ButtonVariants}; use ui::dock::{Panel, PanelEvent}; use ui::indicator::Indicator; @@ -29,13 +29,8 @@ mod entry; const INPUT_PLACEHOLDER: &str = "Find or start a conversation"; -pub fn init(window: &mut Window, cx: &mut App) -> Entity { - cx.new(|cx| Sidebar::new(window, cx)) -} - /// Sidebar. pub struct Sidebar { - name: SharedString, focus_handle: FocusHandle, scroll_handle: UniformListScrollHandle, @@ -80,7 +75,7 @@ pub struct Sidebar { } impl Sidebar { - fn new(window: &mut Window, cx: &mut Context) -> Self { + pub fn new(window: &mut Window, cx: &mut Context) -> Self { let chat = ChatRegistry::global(cx); let filter = cx.new(|_| RoomKind::Ongoing); let contact_list = cx.new(|_| None); @@ -135,7 +130,6 @@ impl Sidebar { ); Self { - name: "Sidebar".into(), focus_handle: cx.focus_handle(), scroll_handle: UniformListScrollHandle::new(), find_input, @@ -474,7 +468,7 @@ impl Sidebar { impl Panel for Sidebar { fn panel_id(&self) -> SharedString { - self.name.clone() + "Sidebar".into() } } @@ -488,8 +482,11 @@ impl Focusable for Sidebar { impl Render for Sidebar { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let nostr = NostrRegistry::global(cx); let chat = ChatRegistry::global(cx); - let loading = chat.read(cx).loading(); + let logged_in = nostr.read(cx).current_user().is_some(); + let loading = chat.read(cx).loading() && logged_in; + let total_rooms = chat.read(cx).count(self.filter.read(cx), cx); // Whether the find panel should be shown @@ -507,27 +504,21 @@ impl Render for Sidebar { .size_full() .gap_2() .child( - h_flex() - .h(TABBAR_HEIGHT) - .border_b_1() - .border_color(cx.theme().border) - .bg(cx.theme().tab_background) - .child( - Input::new(&self.find_input) - .appearance(false) - .bordered(false) - .small() - .text_xs() - .when(!self.find_input.read(cx).loading, |this| { - this.suffix( - Button::new("find-icon") - .icon(IconName::Search) - .tooltip("Press Enter to search") - .transparent() - .small(), - ) - }), - ), + h_flex().px_2().child( + Input::new(&self.find_input) + .small() + .text_xs() + .disabled(loading) + .when(!self.find_input.read(cx).loading, |this| { + this.suffix( + Button::new("find-icon") + .icon(IconName::Search) + .tooltip("Press Enter to search") + .transparent() + .small(), + ) + }), + ), ) .child( h_flex() @@ -608,17 +599,13 @@ impl Render for Sidebar { .items_center() .justify_center() .text_center() + .child(div().text_sm().font_semibold().child("No conversations")) .child( div() - .text_sm() - .font_semibold() - .child(SharedString::from("No conversations")), - ) - .child(div().text_xs().text_color(cx.theme().text_muted).child( - SharedString::from( - "Start a conversation with someone to get started.", - ), - )), + .text_xs() + .text_color(cx.theme().text_muted) + .child("Start a conversation with someone to get started."), + ), ), ) }) @@ -643,7 +630,7 @@ impl Render for Sidebar { .font_semibold() .text_color(cx.theme().text_muted) .child(Icon::new(IconName::ChevronDown)) - .child(SharedString::from("Results")), + .child("Results"), ) .child( uniform_list( @@ -748,7 +735,7 @@ impl Render for Sidebar { .font_semibold() .text_color(cx.theme().text_muted) .child(Indicator::new().small().color(cx.theme().icon_accent)) - .child(SharedString::from("Getting messages...")), + .child("Getting messages..."), ), ) }) diff --git a/web/Cargo.toml b/web/Cargo.toml index 9f43ef9..6959776 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -24,6 +24,7 @@ gpui_platform.workspace = true gpui_web = { git = "https://github.com/zed-industries/zed" } log.workspace = true +instant = { workspace = true, features = ["wasm-bindgen"] } console_error_panic_hook = "0.1" tracing-wasm = "0.2" diff --git a/web/src/lib.rs b/web/src/lib.rs index 71850e3..1398480 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -1,21 +1,19 @@ use gpui::*; use ui::Root; -use universal_time::{ - Duration, Instant, MonotonicClock, SystemTime, WallClock, define_time_provider, -}; +use universal_time::{Instant, MonotonicClock, SystemTime, WallClock, define_time_provider}; use wasm_bindgen::prelude::*; struct CustomTimeProvider; impl WallClock for CustomTimeProvider { fn system_time(&self) -> SystemTime { - SystemTime::from_unix_duration(Duration::from_secs(0)) + SystemTime::from_unix_duration(instant::Duration::from_secs(0)) } } impl MonotonicClock for CustomTimeProvider { fn instant(&self) -> Instant { - Instant::from_ticks(Duration::from_secs(0)) + Instant::from_ticks(instant::Duration::from_secs(0)) } } @@ -34,21 +32,7 @@ pub fn run() -> Result<(), JsValue> { #[cfg(target_family = "wasm")] gpui_platform::web_init(); - #[cfg(not(target_family = "wasm"))] - let app = gpui_platform::application(); - - #[cfg(target_family = "wasm")] - let app = { - let app = gpui_platform::single_threaded_web(); - - // Workaround: intentionally leak the `Rc` to keep the application alive - struct WasmApplication(std::rc::Rc); - let wasm_app = unsafe { std::mem::transmute::(app) }; - std::mem::forget(wasm_app.0.clone()); - unsafe { std::mem::transmute::(wasm_app) } - }; - - app.run(|cx| { + gpui_platform::application().run(|cx| { // Open the root window cx.open_window(WindowOptions::default(), |window, cx| { // Initialize components