From 2d281dfbbb3201056f43eb851d7cf61cf7e32688 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Sat, 15 Aug 2026 11:00:55 +0700 Subject: [PATCH] update --- .../src/views/repo_detail/browser.rs | 17 ++- crates/workspace/src/views/repo_detail/mod.rs | 141 +++++++++++++----- crates/workspace/src/views/sidebar/mod.rs | 2 +- desktop/src/main.rs | 2 +- 4 files changed, 119 insertions(+), 43 deletions(-) diff --git a/crates/workspace/src/views/repo_detail/browser.rs b/crates/workspace/src/views/repo_detail/browser.rs index 62468f0..6720ed1 100644 --- a/crates/workspace/src/views/repo_detail/browser.rs +++ b/crates/workspace/src/views/repo_detail/browser.rs @@ -6,7 +6,7 @@ use gpui::prelude::*; use gpui::{AnyElement, Context, Entity, SharedString, WeakEntity, Window, div, px}; use gpui_component::button::{Button, ButtonVariants}; -use gpui_component::input::{Editor, EditorState}; +use gpui_component::input::{Editor, EditorState, TabSize}; use gpui_component::list::ListItem; use gpui_component::spinner::Spinner; use gpui_component::text::{TextView, TextViewState}; @@ -280,7 +280,16 @@ impl RepoDetailView { cx: &mut Context, ) { let language = code_language(path.as_ref()).unwrap_or("text"); - let state = cx.new(|cx| EditorState::new(language, window, cx).default_value(text)); + let state = cx.new(|cx| { + EditorState::new(language, window, cx) + .default_value(text) + .tab_size(TabSize { + tab_size: 4, + hard_tabs: false, + }) + .line_number(true) + .folding(true) + }); self.code = Some(CodeView { path, state }); } @@ -294,14 +303,12 @@ impl RepoDetailView { return preview_spinner(); } - // Disabled: the preview is read-only. Selection, copy and the - // built-in search (Cmd/Ctrl+F) still work; editing is blocked by the - // component's disabled state. Editor::new(&code.state) .readonly(true) .bordered(false) .rounded_none() .h_full() + .text_sm() .into_any_element() } } diff --git a/crates/workspace/src/views/repo_detail/mod.rs b/crates/workspace/src/views/repo_detail/mod.rs index adf904f..120ec77 100644 --- a/crates/workspace/src/views/repo_detail/mod.rs +++ b/crates/workspace/src/views/repo_detail/mod.rs @@ -11,6 +11,7 @@ use gpui::{ AnyElement, App, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render, SharedString, Size, Subscription, Task, WeakEntity, Window, div, px, size, }; +use gpui_component::avatar::{Avatar, AvatarGroup}; use gpui_component::button::{Button, ButtonVariants, DropdownButton}; use gpui_component::combobox::{ Caret, Combobox, ComboboxEvent, ComboboxState, ComboboxTriggerContext, @@ -26,7 +27,7 @@ use gpui_component::{ }; use signed_core::Announcement; use signed_git::{CommitList, FileCommit}; -use signed_state::{GitStore, RepoStore}; +use signed_state::{GitStore, ProfileStore, RepoStore}; mod browser; mod commits; @@ -941,6 +942,54 @@ impl RepoDetailView { .clone() .unwrap_or_else(|| SharedString::from(announcement.id.clone())) } + + /// Horizontal list of everyone who maintains the repository: the owner + /// shown in full, and any additional maintainers as a compact overlapping avatar group. + fn render_maintainers(&self, cx: &mut Context) -> AnyElement { + let announcement = self.announcement(); + let profile_store = ProfileStore::global(cx); + + let mut seen = HashSet::new(); + let rest: Vec<_> = announcement + .maintainers + .iter() + .copied() + .filter(|key| *key != announcement.owner && seen.insert(*key)) + .collect(); + + let owner = profile_store.read(cx).get(&announcement.owner); + let owner_name = owner.name(); + let owner_picture = owner.picture(); + + h_flex() + .w_full() + .gap_3() + .items_center() + .flex_wrap() + .child( + h_flex() + .gap_1() + .items_center() + .child( + Avatar::new() + .name(owner_name.clone()) + .when_some(owner_picture, |this, url| this.src(url)) + .small(), + ) + .child(div().text_xs().whitespace_nowrap().child(owner_name)), + ) + .when(!rest.is_empty(), |this| { + this.child(AvatarGroup::new().small().limit(5).ellipsis().children( + rest.into_iter().map(|key| { + let profile = profile_store.read(cx).get(&key); + Avatar::new() + .name(profile.name()) + .when_some(profile.picture(), |this, url| this.src(url)) + }), + )) + }) + .into_any_element() + } } impl Panel for RepoDetailView { @@ -1064,11 +1113,25 @@ impl RepoDetailView { .child(div().font_semibold().child(name)) .child( div() + .min_w_0() .text_xs() .text_color(cx.theme().muted_foreground) - .line_clamp(3) + .line_clamp(2) .text_ellipsis() .child(description), + ) + .child( + h_flex() + .mt_2() + .gap_2() + .child( + div() + .text_xs() + .text_color(cx.theme().muted_foreground) + .font_semibold() + .child("Maintainers:"), + ) + .child(self.render_maintainers(cx)), ), ) .child( @@ -1081,6 +1144,7 @@ impl RepoDetailView { .button( Button::new("relay-trigger") .label(format!("{} relays", relays.len())) + .small() .ghost(), ) .dropdown_menu(move |menu, _window, _cx| { @@ -1107,7 +1171,12 @@ impl RepoDetailView { ) .child( DropdownButton::new("web") - .button(Button::new("web-trigger").label("Websites").ghost()) + .button( + Button::new("web-trigger") + .label("Websites") + .small() + .ghost(), + ) .dropdown_menu(move |menu, _window, _cx| { let mut menu = menu; if web.is_empty() { @@ -1167,41 +1236,9 @@ impl RepoDetailView { .flex_1() .gap_2() .justify_end() - .child( - div().w(px(120.)).child( - Combobox::new(&self.branch_select) - .placeholder("Branch") - .appearance(false) - .menu_width(px(200.)) - .disabled(worktree_empty) - .bg(cx.theme().muted) - .rounded(cx.theme().radius) - .render_trigger(|ctx, _window, cx| { - Self::render_ref_trigger( - ctx, - CustomIconName::GitBranch, - cx, - ) - }), - ), - ) - .child( - div().w(px(120.)).child( - Combobox::new(&self.tag_select) - .placeholder("Tag") - .appearance(false) - .menu_width(px(200.)) - .disabled(worktree_empty) - .bg(cx.theme().muted) - .rounded(cx.theme().radius) - .render_trigger(|ctx, _window, cx| { - Self::render_ref_trigger(ctx, CustomIconName::Tag, cx) - }), - ), - ) .child( Button::new("enc") - .secondary() + .ghost() .when_some(self.head_commit.as_ref(), |this, commit| { this.child( div() @@ -1232,6 +1269,38 @@ impl RepoDetailView { this.open_commit_diff(&id, window, cx); } })), + ) + .child( + div().w(px(120.)).child( + Combobox::new(&self.branch_select) + .placeholder("Branch") + .appearance(false) + .menu_width(px(200.)) + .disabled(worktree_empty) + .bg(cx.theme().muted) + .rounded(cx.theme().radius) + .render_trigger(|ctx, _window, cx| { + Self::render_ref_trigger( + ctx, + CustomIconName::GitBranch, + cx, + ) + }), + ), + ) + .child( + div().w(px(120.)).child( + Combobox::new(&self.tag_select) + .placeholder("Tag") + .appearance(false) + .menu_width(px(200.)) + .disabled(worktree_empty) + .bg(cx.theme().muted) + .rounded(cx.theme().radius) + .render_trigger(|ctx, _window, cx| { + Self::render_ref_trigger(ctx, CustomIconName::Tag, cx) + }), + ), ), ), ) diff --git a/crates/workspace/src/views/sidebar/mod.rs b/crates/workspace/src/views/sidebar/mod.rs index 72dec76..62a1575 100644 --- a/crates/workspace/src/views/sidebar/mod.rs +++ b/crates/workspace/src/views/sidebar/mod.rs @@ -210,7 +210,7 @@ impl Render for SidebarPanel { .child(NavItem::new("explore", "Browse", IconName::Globe).on_click( cx.listener(|this, _ev, window, cx| this.open_explore(window, cx)), )) - .child(NavItem::new("search", "Saerch", IconName::Search).on_click( + .child(NavItem::new("search", "Search", IconName::Search).on_click( cx.listener(|this, _ev, window, cx| this.open_explore(window, cx)), )) .child( diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 2bc535b..d7f1aae 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -42,7 +42,7 @@ fn main() { kind: WindowKind::Normal, app_id: Some("Signed".to_owned()), titlebar: Some(TitlebarOptions { - title: Some(SharedString::new_static("Signed Platform")), + title: Some(SharedString::new_static("Signed")), traffic_light_position: Some(point(px(9.0), px(9.0))), appears_transparent: true, }),