This commit is contained in:
2026-11-11 18:46:45 +07:00
parent 2f9559228f
commit 9a7afbe3e2
26 changed files with 212 additions and 193 deletions
+2 -2
View File
@@ -155,7 +155,7 @@ impl AutoUpdater {
pub fn status(&self) -> SharedString { pub fn status(&self) -> SharedString {
match &self.status { match &self.status {
UpdateStatus::Idle | UpdateStatus::UpToDate => "Up to date".into(), UpdateStatus::Idle | UpdateStatus::UpToDate => "Up to date".into(),
UpdateStatus::Checking => "Checking for updates…".into(), UpdateStatus::Checking => "Checking for updates".into(),
UpdateStatus::Available(version) => format!("Version {version} available").into(), UpdateStatus::Available(version) => format!("Version {version} available").into(),
UpdateStatus::Downloading { downloaded, total } => { UpdateStatus::Downloading { downloaded, total } => {
let total_mb = total.map(|t| t as f64 / 1_048_576.0); let total_mb = total.map(|t| t as f64 / 1_048_576.0);
@@ -165,7 +165,7 @@ impl AutoUpdater {
None => format!("Downloading {downloaded_mb:.1} MB").into(), None => format!("Downloading {downloaded_mb:.1} MB").into(),
} }
} }
UpdateStatus::Installing => "Installing update…".into(), UpdateStatus::Installing => "Installing update".into(),
UpdateStatus::Staged(version) => { UpdateStatus::Staged(version) => {
format!("Version {version} ready — restart to apply").into() format!("Version {version} ready — restart to apply").into()
} }
+1 -1
View File
@@ -172,7 +172,7 @@
<div id="nip07-status" class="status status--checking"> <div id="nip07-status" class="status status--checking">
<div class="status__dot"></div> <div class="status__dot"></div>
<span id="nip07-status-text">Checking extension…</span> <span id="nip07-status-text">Checking extension</span>
</div> </div>
<div class="hint"> <div class="hint">
+29 -26
View File
@@ -12,7 +12,7 @@ use gpui::{
Focusable, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton, Focusable, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton,
ObjectFit, ParentElement, Render, SharedString, SharedUri, StatefulInteractiveElement, Styled, ObjectFit, ParentElement, Render, SharedString, SharedUri, StatefulInteractiveElement, Styled,
StyledImage, Subscription, SystemNotification, SystemNotificationAction, Task, WeakEntity, StyledImage, Subscription, SystemNotification, SystemNotificationAction, Task, WeakEntity,
Window, div, img, list, px, relative, retain_all, svg, Window, div, img, list, px, relative, rems, retain_all, svg,
}; };
use gpui_kit::assets::IconName; use gpui_kit::assets::IconName;
use gpui_kit::base::StyledExt as _; use gpui_kit::base::StyledExt as _;
@@ -1077,7 +1077,7 @@ impl ChatPanel {
window.open_dialog(cx, move |this, _window, cx| { window.open_dialog(cx, move |this, _window, cx| {
let relays = profile.messaging_relays(); let relays = profile.messaging_relays();
this.title("Messaging Relays") this.title("Messaging relays")
.close_button(true) .close_button(true)
.child(v_flex().gap_1().children({ .child(v_flex().gap_1().children({
let mut items = vec![]; let mut items = vec![];
@@ -1134,7 +1134,7 @@ impl ChatPanel {
svg() svg()
.path("brand/coop.svg") .path("brand/coop.svg")
.size_12() .size_12()
.text_color(cx.theme().list_active), .text_color(cx.theme().accent),
) )
.child(PRIVATE_NOTICE) .child(PRIVATE_NOTICE)
.into_any_element() .into_any_element()
@@ -1228,7 +1228,11 @@ impl ChatPanel {
cx, cx,
) )
}) })
.element(ix.into(), window, cx); .element(
SharedString::from(format!("text-{}", message.id.to_hex())).into(),
window,
cx,
);
self.render_text_message(ix, message, text, show_author, cx) self.render_text_message(ix, message, text, show_author, cx)
} else { } else {
@@ -1238,7 +1242,7 @@ impl ChatPanel {
fn render_text_message( fn render_text_message(
&self, &self,
ix: usize, _ix: usize,
message: &Message, message: &Message,
rendered_text: AnyElement, rendered_text: AnyElement,
show_author: bool, show_author: bool,
@@ -1270,7 +1274,7 @@ impl ChatPanel {
None => rendered_text, None => rendered_text,
}; };
let row = MessageRow::new(ix) let row = MessageRow::new(SharedString::from(format!("message-{}", id.to_hex())))
.show_author(show_author) .show_author(show_author)
.hide_avatar(hide_avatar) .hide_avatar(hide_avatar)
.avatar( .avatar(
@@ -1300,16 +1304,6 @@ impl ChatPanel {
this.child(self.render_message_file(&id, file, cx)) this.child(self.render_message_file(&id, file, cx))
}) })
.when(has_reactions, |this| this.child(reactions)) .when(has_reactions, |this| this.child(reactions))
.overlay(
div()
.group_hover("", |this| this.bg(cx.theme().list_active))
.absolute()
.left_0()
.top_0()
.w(px(2.))
.h_full()
.bg(gpui::transparent_black()),
)
.overlay(self.render_actions(&id, &pk, cx)) .overlay(self.render_actions(&id, &pk, cx))
.on_mouse_down( .on_mouse_down(
MouseButton::Middle, MouseButton::Middle,
@@ -1399,14 +1393,19 @@ impl ChatPanel {
) -> impl IntoIterator<Item = impl IntoElement> { ) -> impl IntoIterator<Item = impl IntoElement> {
let mut items = Vec::with_capacity(replies.len()); let mut items = Vec::with_capacity(replies.len());
for (ix, id) in replies.iter().enumerate() { for id in replies.iter() {
let Some(message) = self.message(id) else { let Some(message) = self.message(id) else {
continue; continue;
}; };
let author = self.profile(&message.author, cx); let author = self.profile(&message.author, cx);
items.push( items.push(
ReplyPreview::new(ix, author.name(), message.preview()).on_click({ ReplyPreview::new(
SharedString::from(format!("reply-{}", id.to_hex())),
author.name(),
message.preview(),
)
.on_click({
let id = *id; let id = *id;
cx.listener(move |this, _event, _window, _cx| { cx.listener(move |this, _event, _window, _cx| {
this.scroll_to(&id); this.scroll_to(&id);
@@ -1437,7 +1436,7 @@ impl ChatPanel {
} else if failed { } else if failed {
SharedString::from("• Error") SharedString::from("• Error")
} else if pending { } else if pending {
SharedString::from("• Sending...") SharedString::from("• Sending")
} else { } else {
SharedString::from("• Unknown") SharedString::from("• Unknown")
}; };
@@ -1462,7 +1461,7 @@ impl ChatPanel {
}) })
.unwrap_or_default(); .unwrap_or_default();
this.title(SharedString::from("Sent Reports")) this.title(SharedString::from("Sent reports"))
.close_button(true) .close_button(true)
.child(v_flex().gap_4().children(items)) .child(v_flex().gap_4().children(items))
}); });
@@ -1699,7 +1698,7 @@ impl ChatPanel {
img(local.image_source()) img(local.image_source())
.border_1() .border_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.h(px(250.)) .h(rems(16.))
.object_fit(ObjectFit::Cover) .object_fit(ObjectFit::Cover)
.rounded(cx.theme().radius_lg), .rounded(cx.theme().radius_lg),
) )
@@ -1734,7 +1733,7 @@ impl ChatPanel {
.border_1() .border_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.child(Icon::new(IconName::Lock).text_color(cx.theme().primary)) .child(Icon::new(IconName::Lock).text_color(cx.theme().accent))
.child( .child(
v_flex() v_flex()
.min_w_0() .min_w_0()
@@ -1835,11 +1834,15 @@ impl ChatPanel {
items items
} }
fn render_reply(&self, ix: usize, id: &EventId, cx: &Context<Self>) -> impl IntoElement { fn render_reply(&self, id: &EventId, cx: &Context<Self>) -> impl IntoElement {
if let Some(text) = self.message(id) { if let Some(text) = self.message(id) {
let profile = self.directory.read(cx).person(&text.author, cx); let profile = self.directory.read(cx).person(&text.author, cx);
ReplyPreview::new(ix, profile.name(), text.preview()) ReplyPreview::new(
SharedString::from(format!("reply-{}", id.to_hex())),
profile.name(),
text.preview(),
)
.on_remove({ .on_remove({
let id = text.id; let id = text.id;
cx.listener(move |this, _, _, cx| { cx.listener(move |this, _, _, cx| {
@@ -1859,8 +1862,8 @@ impl ChatPanel {
) -> impl IntoIterator<Item = impl IntoElement> { ) -> impl IntoIterator<Item = impl IntoElement> {
let mut items = vec![]; let mut items = vec![];
for (ix, id) in self.replies_to.read(cx).iter().enumerate() { for id in self.replies_to.read(cx).iter() {
items.push(self.render_reply(ix, id, cx)); items.push(self.render_reply(id, cx));
} }
items items
+4 -3
View File
@@ -1,5 +1,6 @@
use gpui::{ use gpui::{
AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div, px, AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div,
rems,
}; };
use gpui_kit::base::StyledExt as _; use gpui_kit::base::StyledExt as _;
use gpui_kit::component::description_list::{DescriptionItem, DescriptionList}; use gpui_kit::component::description_list::{DescriptionItem, DescriptionList};
@@ -10,9 +11,9 @@ use ui::Avatar;
pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) { pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) {
window.open_dialog(cx, move |this, _window, cx| { window.open_dialog(cx, move |this, _window, cx| {
this.max_h(px(600.)) this.max_h(rems(37.5))
.close_button(true) .close_button(true)
.title("About Group") .title("About group")
.child(body(&group, cx)) .child(body(&group, cx))
}); });
} }
+2 -2
View File
@@ -3,7 +3,7 @@ use common::TimestampExt;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
App, AppContext, ClipboardItem, Entity, IntoElement, ParentElement, SharedString, Styled, App, AppContext, ClipboardItem, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px, WeakEntity, Window, div, px, rems,
}; };
use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants}; use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants};
use gpui_kit::component::dialog::DialogButtonProps; use gpui_kit::component::dialog::DialogButtonProps;
@@ -25,7 +25,7 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
}); });
window.open_dialog(cx, move |this, _window, cx| { window.open_dialog(cx, move |this, _window, cx| {
this.max_h(px(600.)) this.max_h(rems(37.5))
.close_button(true) .close_button(true)
.title("Invites") .title("Invites")
.child(body(group.clone(), input.clone(), cx)) .child(body(group.clone(), input.clone(), cx))
+18 -20
View File
@@ -8,7 +8,7 @@ use gpui::{
Action, Anchor, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, Action, Anchor, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter,
FocusHandle, Focusable, FollowMode, InteractiveElement, IntoElement, ListAlignment, FocusHandle, Focusable, FollowMode, InteractiveElement, IntoElement, ListAlignment,
ListScrollEvent, ListState, ParentElement, Render, SharedString, Styled, Subscription, Task, ListScrollEvent, ListState, ParentElement, Render, SharedString, Styled, Subscription, Task,
WeakEntity, Window, div, list, px, WeakEntity, Window, div, list, px, rems,
}; };
use gpui_kit::assets::IconName; use gpui_kit::assets::IconName;
use gpui_kit::base::StyledExt as _; use gpui_kit::base::StyledExt as _;
@@ -167,7 +167,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
) )
.child( .child(
Button::new("group-delete") Button::new("group-delete")
.icon(IconName::CircleX) .icon(IconName::Trash)
.tooltip("Delete group") .tooltip("Delete group")
.ghost() .ghost()
.small() .small()
@@ -965,16 +965,11 @@ impl GroupPanel {
.py_1p5() .py_1p5()
.border_b_1() .border_b_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.children(pins.iter().map(|pin| { .children(
div() pins.iter().map(|pin| {
.px_1p5() gpui_kit::component::tag::Tag::secondary().child(pin_label(group, pin))
.py_0p5() }),
.rounded(cx.theme().radius) )
.bg(cx.theme().primary)
.text_xs()
.text_color(cx.theme().muted_foreground)
.child(pin_label(group, pin))
}))
.into_any_element() .into_any_element()
} }
@@ -1025,7 +1020,7 @@ impl GroupPanel {
.child( .child(
Button::new("load-older") Button::new("load-older")
.label(if loading { .label(if loading {
"Loading earlier messages…" "Loading earlier messages"
} else { } else {
"Load earlier messages" "Load earlier messages"
}) })
@@ -1083,7 +1078,7 @@ impl GroupPanel {
row.child("You're in preview mode. Join this group to talk.") row.child("You're in preview mode. Join this group to talk.")
.child( .child(
Button::new("join") Button::new("join")
.label("Request to Join") .label("Request to join")
.small() .small()
.ghost() .ghost()
.on_click(cx.listener(move |this, _event, _window, cx| { .on_click(cx.listener(move |this, _event, _window, cx| {
@@ -1152,7 +1147,11 @@ impl GroupPanel {
cx, cx,
) )
}) })
.element(ix.into(), window, cx), .element(
SharedString::from(format!("text-{}", message.id.to_hex())).into(),
window,
cx,
),
}; };
let admin = nostr let admin = nostr
@@ -1378,7 +1377,7 @@ impl GroupPanel {
.child( .child(
div() div()
.flex_shrink_0() .flex_shrink_0()
.max_w(px(140.)) .max_w(rems(8.75))
.truncate() .truncate()
.font_semibold() .font_semibold()
.text_color(cx.theme().foreground) .text_color(cx.theme().foreground)
@@ -1408,8 +1407,7 @@ impl GroupPanel {
fn render_attachments(&self, cx: &mut Context<Self>) -> Vec<AnyElement> { fn render_attachments(&self, cx: &mut Context<Self>) -> Vec<AnyElement> {
self.attachments self.attachments
.iter() .iter()
.enumerate() .map(|url| {
.map(|(ix, url)| {
let url = url.clone(); let url = url.clone();
h_flex() h_flex()
@@ -1425,12 +1423,12 @@ impl GroupPanel {
.child(Icon::new(IconName::Link).xsmall()) .child(Icon::new(IconName::Link).xsmall())
.child( .child(
div() div()
.max_w(px(200.)) .max_w(rems(12.5))
.truncate() .truncate()
.child(attachment_name(&url)), .child(attachment_name(&url)),
) )
.child( .child(
Button::new(SharedString::from(format!("remove-attachment-{ix}"))) Button::new(SharedString::from(format!("remove-attachment-{}", url)))
.icon(IconName::Close) .icon(IconName::Close)
.tooltip("Remove") .tooltip("Remove")
.ghost() .ghost()
+16 -7
View File
@@ -33,7 +33,7 @@ pub(crate) struct Actions {
} }
pub(crate) fn render( pub(crate) fn render(
ix: usize, _ix: usize,
message: &Message, message: &Message,
content: AnyElement, content: AnyElement,
show_author: bool, show_author: bool,
@@ -58,7 +58,10 @@ pub(crate) fn render(
nostr.read(cx).current_user(), nostr.read(cx).current_user(),
); );
let row = MessageRow::new(ix) let row = MessageRow::new(SharedString::from(format!(
"message-{}",
message.id.to_hex()
)))
.show_author(show_author) .show_author(show_author)
.hide_avatar(hide_avatar) .hide_avatar(hide_avatar)
.avatar( .avatar(
@@ -249,7 +252,7 @@ pub(crate) fn render(
} }
pub(crate) fn render_activity( pub(crate) fn render_activity(
ix: usize, _ix: usize,
activity: &Activity, activity: &Activity,
persons: &Entity<Directory>, persons: &Entity<Directory>,
cx: &App, cx: &App,
@@ -258,12 +261,14 @@ pub(crate) fn render_activity(
let hide_avatar = AppSettings::get_hide_avatar(cx); let hide_avatar = AppSettings::get_hide_avatar(cx);
h_flex() h_flex()
.id(ix) .id(SharedString::from(format!(
"activity-{}",
activity.id.to_hex()
)))
.w_full() .w_full()
.gap_2() .gap_2()
.py_1() .py_1()
.px_3() .px_3()
.hover(|this| this.bg(cx.theme().list_hover))
.child(badge(activity, cx)) .child(badge(activity, cx))
.when(!hide_avatar, |this| { .when(!hide_avatar, |this| {
this.child( this.child(
@@ -340,7 +345,7 @@ fn replies(
let mut items = Vec::with_capacity(targets.len()); let mut items = Vec::with_capacity(targets.len());
for (ix, id) in targets.iter().enumerate() { for id in targets.iter() {
let context = entity.read_with(cx, |group, cx| { let context = entity.read_with(cx, |group, cx| {
let message = group.message(*id)?; let message = group.message(*id)?;
let author = persons.read(cx).person(&message.author, cx); let author = persons.read(cx).person(&message.author, cx);
@@ -359,7 +364,11 @@ fn replies(
let panel = panel.clone(); let panel = panel.clone();
items.push( items.push(
ReplyPreview::new(ix, author, preview) ReplyPreview::new(
SharedString::from(format!("reply-{}", id.to_hex())),
author,
preview,
)
.on_click(move |_event, _window, cx| { .on_click(move |_event, _window, cx| {
if let Err(error) = panel.update(cx, |panel, cx| { if let Err(error) = panel.update(cx, |panel, cx| {
panel.scroll_to(&id, cx); panel.scroll_to(&id, cx);
+11 -7
View File
@@ -11,7 +11,7 @@ use gpui_kit::component::dialog::DialogButtonProps;
use gpui_kit::component::input::InputState; use gpui_kit::component::input::InputState;
use gpui_kit::component::notification::Notification; use gpui_kit::component::notification::Notification;
use gpui_kit::component::switch::Switch; use gpui_kit::component::switch::Switch;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex}; use gpui_kit::component::{ActiveTheme, Disableable, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupId, GroupMetadata}; use nip29::{Group, GroupId, GroupMetadata};
use nostr_sdk::prelude::RelayUrl; use nostr_sdk::prelude::RelayUrl;
use ui::settings_row; use ui::settings_row;
@@ -100,7 +100,7 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
let supported = *livekit.read(cx) != Some(false); let supported = *livekit.read(cx) != Some(false);
this.close_button(true) this.close_button(true)
.title("Edit Group") .title("Edit group")
.button_props( .button_props(
DialogButtonProps::default() DialogButtonProps::default()
.cancel_text("Cancel") .cancel_text("Cancel")
@@ -161,11 +161,14 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
.child(settings_row( .child(settings_row(
"LiveKit", "LiveKit",
if supported { if supported {
"Announce audio and video support." "Announce audio and video support"
} else { } else {
"The relay doesn't announce LiveKit support." "The relay doesn't announce LiveKit support."
}, },
Switch::new("livekit").checked(current.livekit).on_click({ Switch::new("livekit")
.checked(current.livekit)
.disabled(!supported)
.on_click({
let flags = flags.clone(); let flags = flags.clone();
let livekit = livekit.clone(); let livekit = livekit.clone();
@@ -293,10 +296,10 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
.flex_shrink_0() .flex_shrink_0()
.gap_1() .gap_1()
.when(ix > 0, |this| { .when(ix > 0, |this| {
this.child(move_button(children.clone(), ix, ix - 1, "up")) this.child(move_button(children.clone(), child, ix, ix - 1, "up"))
}) })
.when(ix + 1 < list.len(), |this| { .when(ix + 1 < list.len(), |this| {
this.child(move_button(children.clone(), ix, ix + 1, "down")) this.child(move_button(children.clone(), child, ix, ix + 1, "down"))
}); });
section = section.child( section = section.child(
@@ -319,11 +322,12 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
fn move_button( fn move_button(
children: Entity<Vec<GroupId>>, children: Entity<Vec<GroupId>>,
child: &GroupId,
ix: usize, ix: usize,
target: usize, target: usize,
direction: &'static str, direction: &'static str,
) -> Button { ) -> Button {
Button::new(SharedString::from(format!("child-{direction}-{ix}"))) Button::new(SharedString::from(format!("child-{direction}-{child}")))
.label(if direction == "up" { "Up" } else { "Down" }) .label(if direction == "up" { "Up" } else { "Down" })
.ghost() .ghost()
.small() .small()
+1 -1
View File
@@ -74,7 +74,7 @@ pub fn open_nutzap(
window.open_dialog(cx, move |this, _window, _cx| { window.open_dialog(cx, move |this, _window, _cx| {
this.width(px(380.)) this.width(px(380.))
.title("Send Nutzap") .title("Send eCash")
.on_ok({ .on_ok({
let send = send.clone(); let send = send.clone();
move |event, window, cx| send(event, window, cx) move |event, window, cx| send(event, window, cx)
+2 -2
View File
@@ -126,7 +126,7 @@ impl WalletPanel {
.border_dashed() .border_dashed()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.rounded(cx.theme().radius_lg) .rounded(cx.theme().radius_lg)
.child("No transactions yet.") .child("No transactions yet")
.into_any_element(); .into_any_element();
} }
@@ -646,7 +646,7 @@ fn open_receive_dialog(window: &mut Window, cx: &mut App) {
.step(1.) .step(1.)
.min(1.) .min(1.)
}); });
let token_input = cx.new(|cx| InputState::new(window, cx).placeholder("cashu…")); let token_input = cx.new(|cx| InputState::new(window, cx).placeholder("cashu"));
let create_invoice: SubmitFn = { let create_invoice: SubmitFn = {
let wallet = WalletRegistry::global(cx); let wallet = WalletRegistry::global(cx);
+10 -9
View File
@@ -1,6 +1,6 @@
use gpui::{ use gpui::{
App, Div, Hsla, InteractiveElement, IntoElement, ParentElement, Pixels, RenderOnce, App, Div, Hsla, InteractiveElement, IntoElement, ParentElement, Rems, RenderOnce, SharedString,
SharedString, StyleRefinement, Styled, Window, div, px, StyleRefinement, Styled, Window, div, rems,
}; };
use gpui_base::AvatarImage; use gpui_base::AvatarImage;
use gpui_kit::base::StyledExt as _; use gpui_kit::base::StyledExt as _;
@@ -115,7 +115,7 @@ impl RenderOnce for Avatar {
#[derive(IntoElement)] #[derive(IntoElement)]
struct PixelAvatar { struct PixelAvatar {
seed: u64, seed: u64,
side: Pixels, side: Rems,
style: StyleRefinement, style: StyleRefinement,
} }
@@ -187,13 +187,14 @@ impl RenderOnce for PixelAvatar {
} }
/// The rendered side length of an avatar at `size`. /// The rendered side length of an avatar at `size`.
fn side_length(size: Size) -> Pixels { fn side_length(size: Size) -> Rems {
match size { match size {
Size::XSmall => px(16.), Size::XSmall => rems(1.),
Size::Small => px(24.), Size::Small => rems(1.5),
Size::Medium => px(48.), Size::Medium => rems(3.),
Size::Large => px(80.), Size::Large => rems(5.),
Size::Size(size) => size, // Custom sizes arrive in px; assume the 16px default rem.
Size::Size(size) => rems(size.as_f32() / 16.),
} }
} }
+11 -8
View File
@@ -7,7 +7,7 @@ use gpui::{
Animation, AnimationExt as _, AnyElement, App, ClickEvent, Context, ElementId, Animation, AnimationExt as _, AnyElement, App, ClickEvent, Context, ElementId,
InteractiveElement as _, IntoElement, MouseButton, MouseDownEvent, ObjectFit, InteractiveElement as _, IntoElement, MouseButton, MouseDownEvent, ObjectFit,
ParentElement as _, RenderOnce, SharedString, SharedUri, StatefulInteractiveElement as _, ParentElement as _, RenderOnce, SharedString, SharedUri, StatefulInteractiveElement as _,
StyleRefinement, Styled, StyledImage, Window, div, img, px, StyleRefinement, Styled, StyledImage, Window, div, img, rems,
}; };
use gpui_kit::assets::IconName; use gpui_kit::assets::IconName;
use gpui_kit::component::button::{Button, ButtonVariants as _}; use gpui_kit::component::button::{Button, ButtonVariants as _};
@@ -199,10 +199,10 @@ impl RenderOnce for MessageRow {
if show_author { if show_author {
match avatar { match avatar {
Some(avatar) => this.child(avatar), Some(avatar) => this.child(avatar),
None => this.child(div().flex_shrink_0().w(px(24.))), None => this.child(div().flex_shrink_0().w_6()),
} }
} else { } else {
this.child(div().flex_shrink_0().w(px(24.))) this.child(div().flex_shrink_0().w_6())
} }
}) })
.child( .child(
@@ -212,7 +212,7 @@ impl RenderOnce for MessageRow {
.min_w_0() .min_w_0()
.flex_initial() .flex_initial()
.overflow_hidden() .overflow_hidden()
.text_size(px(15.)) .text_base()
.when(show_author, |this| { .when(show_author, |this| {
this.child( this.child(
h_flex() h_flex()
@@ -383,7 +383,7 @@ impl RenderOnce for MessageMedia {
img(only.clone()) img(only.clone())
.border_1() .border_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.h(px(250.)) .h(rems(16.))
.object_fit(ObjectFit::Cover) .object_fit(ObjectFit::Cover)
.rounded(cx.theme().radius_lg), .rounded(cx.theme().radius_lg),
) )
@@ -398,8 +398,11 @@ impl RenderOnce for MessageMedia {
.flex_wrap() .flex_wrap()
.gap_2() .gap_2()
.refine_style(&style) .refine_style(&style)
.children(media.into_iter().enumerate().map(|(ix, item)| { .children(media.into_iter().map(|item| {
div().id(format!("media-{ix}")).flex_shrink_0().child( div()
.id(SharedString::from(format!("media-{}", item)))
.flex_shrink_0()
.child(
img(item) img(item)
.h_32() .h_32()
.border_1() .border_1()
@@ -494,7 +497,7 @@ impl RenderOnce for ReactionBar {
.when(!group.mine, |this| this.border_color(cx.theme().border)) .when(!group.mine, |this| this.border_color(cx.theme().border))
.when(group.image.is_none(), |this| this.child(group.emoji)) .when(group.image.is_none(), |this| this.child(group.emoji))
.when_some(group.image, |this, image| { .when_some(group.image, |this, image| {
this.child(img(image).size(px(16.)).flex_shrink_0()) this.child(img(image).size_4().flex_shrink_0())
}) })
.child(SharedString::from(group.count.to_string())) .child(SharedString::from(group.count.to_string()))
})) }))
+1 -1
View File
@@ -11,7 +11,7 @@ use nip29::{GroupReference, GroupsRegistry};
type SubmitFn = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool>; type SubmitFn = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool>;
pub fn open(window: &mut Window, cx: &mut App) { pub fn open(window: &mut Window, cx: &mut App) {
let input = cx.new(|cx| InputState::new(window, cx).placeholder("naddr1…")); let input = cx.new(|cx| InputState::new(window, cx).placeholder("naddr1"));
let join: SubmitFn = { let join: SubmitFn = {
let input = input.clone(); let input = input.clone();
+2 -2
View File
@@ -3,7 +3,7 @@ use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
AnyElement, App, AppContext, Context, Div, Entity, InteractiveElement as _, IntoElement, AnyElement, App, AppContext, Context, Div, Entity, InteractiveElement as _, IntoElement,
ParentElement, Render, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled, ParentElement, Render, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled,
Subscription, Task, WeakEntity, Window, div, px, Subscription, Task, WeakEntity, Window, div, px, rems,
}; };
use gpui_kit::base::StyledExt as _; use gpui_kit::base::StyledExt as _;
use gpui_kit::component::button::{Button, ButtonVariants}; use gpui_kit::component::button::{Button, ButtonVariants};
@@ -491,7 +491,7 @@ impl Render for NewGroup {
.child( .child(
v_flex() v_flex()
.id("group-dialog-content") .id("group-dialog-content")
.h(px(500.)) .h(rems(31.25))
.gap_3() .gap_3()
.when_some(self.error.clone(), |this, error| { .when_some(self.error.clone(), |this, error| {
this.child(self.render_error(&error, web_url.as_deref(), cx)) this.child(self.render_error(&error, web_url.as_deref(), cx))
+3 -3
View File
@@ -72,7 +72,7 @@ impl ProfileDialog {
let bio_input = cx.new(|cx| { let bio_input = cx.new(|cx| {
TextareaState::new(window, cx) TextareaState::new(window, cx)
.auto_grow(3, 8) .auto_grow(3, 8)
.placeholder("A short introduce about you.") .placeholder("A short introduction about you")
}); });
let directory = Directory::new(cx); let directory = Directory::new(cx);
@@ -438,7 +438,7 @@ impl Render for ProfileDialog {
Button::new("update") Button::new("update")
.icon(IconName::SquarePen) .icon(IconName::SquarePen)
.label("Update") .label("Update")
.primary() .outline()
.on_click(cx.listener(move |this, _ev, window, cx| { .on_click(cx.listener(move |this, _ev, window, cx| {
this.toggle_edit(window, cx); this.toggle_edit(window, cx);
})), })),
@@ -449,7 +449,7 @@ impl Render for ProfileDialog {
Button::new("message") Button::new("message")
.icon(IconName::MessageSquare) .icon(IconName::MessageSquare)
.label("Message") .label("Message")
.primary() .outline()
.on_click(cx.listener(move |this, _ev, window, cx| { .on_click(cx.listener(move |this, _ev, window, cx| {
this.message(window, cx); this.message(window, cx);
})), })),
+5 -5
View File
@@ -352,7 +352,7 @@ impl GossipRelays {
let metadata = self.metadata; let metadata = self.metadata;
if !value.starts_with("ws") { if !value.starts_with("ws") {
self.set_error("Relay URl is invalid", window, cx); self.set_error("Relay URL is invalid", window, cx);
return; return;
} }
@@ -364,7 +364,7 @@ impl GossipRelays {
cx.notify(); cx.notify();
} }
} else { } else {
self.set_error("Relay URl is invalid", window, cx); self.set_error("Relay URL is invalid", window, cx);
} }
} }
@@ -496,7 +496,7 @@ impl InboxRelays {
let value = self.input.read(cx).value().to_string(); let value = self.input.read(cx).value().to_string();
if !value.starts_with("ws") { if !value.starts_with("ws") {
self.set_error("Relay URl is invalid", window, cx); self.set_error("Relay URL is invalid", window, cx);
return; return;
} }
@@ -508,7 +508,7 @@ impl InboxRelays {
cx.notify(); cx.notify();
} }
} else { } else {
self.set_error("Relay URl is invalid", window, cx); self.set_error("Relay URL is invalid", window, cx);
} }
} }
@@ -646,7 +646,7 @@ fn relay_row(
.px_2() .px_2()
.justify_between() .justify_between()
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.bg(cx.theme().sidebar) .bg(cx.theme().secondary)
.context_menu({ .context_menu({
let on_remove = on_remove.clone(); let on_remove = on_remove.clone();
+3 -3
View File
@@ -5,7 +5,7 @@ use common::TimestampExt;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
App, AppContext, Context, Div, Entity, IntoElement, ParentElement, Render, SharedString, App, AppContext, Context, Div, Entity, IntoElement, ParentElement, Render, SharedString,
Styled, Subscription, Task, Window, div, px, relative, uniform_list, Styled, Subscription, Task, Window, div, relative, rems, uniform_list,
}; };
use gpui_kit::assets::IconName; use gpui_kit::assets::IconName;
use gpui_kit::base::StyledExt as _; use gpui_kit::base::StyledExt as _;
@@ -324,7 +324,7 @@ impl Screening {
items items
}) })
.h(px(300.)), .h(rems(18.75)),
), ),
) )
}); });
@@ -571,7 +571,7 @@ fn status_badge(status: Option<bool>, cx: &App) -> Div {
if let Some(status) = status { if let Some(status) = status {
this.child(Icon::new(IconName::CircleCheck).small().text_color({ this.child(Icon::new(IconName::CircleCheck).small().text_color({
if status { if status {
cx.theme().primary cx.theme().success
} else { } else {
cx.theme().muted_foreground cx.theme().muted_foreground
} }
+13 -13
View File
@@ -93,7 +93,7 @@ impl Preferences {
}); });
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
let mint_input = cx.new(|cx| InputState::new(window, cx).placeholder("https://mint…")); let mint_input = cx.new(|cx| InputState::new(window, cx).placeholder("https://mint"));
let subscriptions = vec![ let subscriptions = vec![
Self::wire_number_input(&font_size_input, FONT_SIZE_RANGE, cx, |value, cx| { Self::wire_number_input(&font_size_input, FONT_SIZE_RANGE, cx, |value, cx| {
@@ -446,7 +446,7 @@ impl Preferences {
impl Render for Preferences { impl Render for Preferences {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
const SCREENING: &str = "Show an screening dialog to verify the unknown sender."; const SCREENING: &str = "Show a screening dialog to verify the unknown sender.";
const WOT: &str = "Treat people followed by your contacts as known senders."; const WOT: &str = "Treat people followed by your contacts as known senders.";
const AVATAR: &str = "Hide all avatar pictures to improve performance."; const AVATAR: &str = "Hide all avatar pictures to improve performance.";
const APPEARANCE: &str = const APPEARANCE: &str =
@@ -475,7 +475,7 @@ impl Render for Preferences {
let background_label = match &panel_background_path { let background_label = match &panel_background_path {
Some(path) => path.display().to_string(), Some(path) => path.display().to_string(),
None => String::from("Default Background"), None => String::from("Default background"),
}; };
v_flex() v_flex()
@@ -497,7 +497,7 @@ impl Render for Preferences {
cx, cx,
)) ))
.child(settings_row( .child(settings_row(
"Web of Trust", "Web of trust",
WOT, WOT,
Switch::new("wot-auto-trust") Switch::new("wot-auto-trust")
.checked(wot_auto_trust) .checked(wot_auto_trust)
@@ -536,33 +536,33 @@ impl Render for Preferences {
}), }),
cx, cx,
)) ))
.child(self.render_theme_choice("theme-light", "Light Theme", LIGHT_THEME, false, cx)) .child(self.render_theme_choice("theme-light", "Light theme", LIGHT_THEME, false, cx))
.child(self.render_theme_choice("theme-dark", "Dark Theme", DARK_THEME, true, cx)) .child(self.render_theme_choice("theme-dark", "Dark theme", DARK_THEME, true, cx))
.child(Separator::horizontal()) .child(Separator::horizontal())
.child( .child(
v_flex() v_flex()
.w_full() .w_full()
.gap_3() .gap_3()
.child(settings_row( .child(settings_row(
"Font Size", "Font size",
FONT_SIZE, FONT_SIZE,
NumberInput::new(&self.font_size_input).w_24(), NumberInput::new(&self.font_size_input).w_24(),
cx, cx,
)) ))
.child(settings_row( .child(settings_row(
"Corner Radius", "Corner radius",
RADIUS, RADIUS,
NumberInput::new(&self.radius_input).w_24(), NumberInput::new(&self.radius_input).w_24(),
cx, cx,
)) ))
.child(settings_row( .child(settings_row(
"Large Corner Radius", "Large corner radius",
RADIUS_LG, RADIUS_LG,
NumberInput::new(&self.radius_lg_input).w_24(), NumberInput::new(&self.radius_lg_input).w_24(),
cx, cx,
)) ))
.child(settings_row( .child(settings_row(
"Focus Ring", "Focus ring",
FOCUS_RING, FOCUS_RING,
Switch::new("focus-ring") Switch::new("focus-ring")
.checked(theme.focus_ring) .checked(theme.focus_ring)
@@ -624,7 +624,7 @@ impl Render for Preferences {
) )
.child(Separator::horizontal()) .child(Separator::horizontal())
.child(settings_row( .child(settings_row(
"Decoupling Encryption Key", "Decoupling encryption key",
NIP4E, NIP4E,
Switch::new("nip4e") Switch::new("nip4e")
.checked(nip4e) .checked(nip4e)
@@ -634,7 +634,7 @@ impl Render for Preferences {
cx, cx,
)) ))
.child(settings_row( .child(settings_row(
"Rotate Encryption Key", "Rotate encryption key",
NIP4E_ROTATION, NIP4E_ROTATION,
Switch::new("nip4e_rotation") Switch::new("nip4e_rotation")
.checked(nip4e_rotation_days.is_some()) .checked(nip4e_rotation_days.is_some())
@@ -654,7 +654,7 @@ impl Render for Preferences {
.child(self.render_dictation_shortcut(window, cx)) .child(self.render_dictation_shortcut(window, cx))
.child(Separator::horizontal()) .child(Separator::horizontal())
.child(settings_block( .child(settings_block(
"Media Upload Service", "Media upload service",
MEDIA, MEDIA,
h_flex() h_flex()
.gap_1() .gap_1()
+1 -1
View File
@@ -678,7 +678,7 @@ impl TabGroupRenderer for CoopTabGroupSkin {
h_flex() h_flex()
.id("tabs-inner") .id("tabs-inner")
.relative() .relative()
.gap(px(4.)) .gap_1()
.overflow_x_scroll() .overflow_x_scroll()
.lock_scroll_axis() .lock_scroll_axis()
.track_scroll(&self.scroll_handle) .track_scroll(&self.scroll_handle)
+2 -2
View File
@@ -11,7 +11,7 @@ use chat::{ChatEvent, ChatRegistry};
use common::download_dir; use common::download_dir;
use gpui::{ use gpui::{
Action, App, AppContext, Context, Entity, Focusable, InteractiveElement, IntoElement, Action, App, AppContext, Context, Entity, Focusable, InteractiveElement, IntoElement,
ParentElement, Pixels, Render, SharedString, Styled, Subscription, Task, Window, div, px, ParentElement, Pixels, Render, SharedString, Styled, Subscription, Task, Window, div, px, rems,
}; };
use gpui_kit::component::button::{Button, ButtonVariant}; use gpui_kit::component::button::{Button, ButtonVariant};
use gpui_kit::component::command::{ use gpui_kit::component::command::{
@@ -480,7 +480,7 @@ impl Workspace {
.bordered(false) .bordered(false)
.rounded(cx.theme().radius_lg) .rounded(cx.theme().radius_lg)
.placeholder("Search themes...") .placeholder("Search themes...")
.max_h(px(420.)) .max_h(rems(26.25))
.on_confirm(move |index, _, cx| { .on_confirm(move |index, _, cx| {
if let Some(name) = Self::theme_name_at(index, cx) { if let Some(name) = Self::theme_name_at(index, cx) {
AppSettings::select_theme(&name, cx); AppSettings::select_theme(&name, cx);
+4 -4
View File
@@ -296,7 +296,7 @@ impl BrowsePanel {
.max_w_64() .max_w_64()
.px_1() .px_1()
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.bg(cx.theme().sidebar) .bg(cx.theme().secondary)
.child(Input::new(&self.input).appearance(false).flex_1()) .child(Input::new(&self.input).appearance(false).flex_1())
.child( .child(
Button::new("relay-browse") Button::new("relay-browse")
@@ -376,7 +376,7 @@ impl BrowsePanel {
.cursor_pointer() .cursor_pointer()
.border_1() .border_1()
.border_color(transparent_black()) .border_color(transparent_black())
.bg(cx.theme().sidebar) .bg(cx.theme().group_box)
.hover(|this| { .hover(|this| {
this.bg(cx.theme().secondary) this.bg(cx.theme().secondary)
.border_color(cx.theme().border) .border_color(cx.theme().border)
@@ -510,12 +510,12 @@ impl Render for BrowsePanel {
} else { } else {
let text = if discover { let text = if discover {
if self.discovering { if self.discovering {
"Discovering groups…" "Discovering groups"
} else { } else {
"No groups found." "No groups found."
} }
} else if self.loading { } else if self.loading {
"Loading groups…" "Loading groups"
} else { } else {
"No groups found." "No groups found."
}; };
+1 -1
View File
@@ -307,7 +307,7 @@ impl ContactListPanel {
.rounded(cx.theme().radius_lg) .rounded(cx.theme().radius_lg)
.text_xs() .text_xs()
.text_align(TextAlign::Center) .text_align(TextAlign::Center)
.child(SharedString::from("Please add some relays.")) .child(SharedString::from("No contacts yet"))
} }
} }
+9 -9
View File
@@ -213,17 +213,17 @@ impl GreeterPanel {
} else { } else {
requests requests
.iter() .iter()
.enumerate() .map(|(room, preview)| {
.map(|(index, (room, preview))| {
let room = room.clone(); let room = room.clone();
let dock = dock.clone(); let dock = dock.clone();
let name = room.read(cx).display_name(cx); let name = room.read(cx).display_name(cx);
let seed = room.read(cx).display_image_seed(cx); let seed = room.read(cx).display_image_seed(cx);
let created_at = room.read(cx).created_at.to_ago(); let created_at = room.read(cx).created_at.to_ago();
let id = room.read(cx).id;
let preview = preview.clone(); let preview = preview.clone();
h_flex() h_flex()
.id(format!("request-{index}")) .id(format!("request-{id}"))
.group("") .group("")
.w_full() .w_full()
.h_9() .h_9()
@@ -280,8 +280,8 @@ impl GreeterPanel {
), ),
) )
.child( .child(
Button::new(format!("response-{index}")) Button::new(format!("response-{id}"))
.label("Response") .label("Respond")
.xsmall() .xsmall()
.ghost() .ghost()
.invisible() .invisible()
@@ -332,17 +332,17 @@ impl GreeterPanel {
let rows: Vec<AnyElement> = self let rows: Vec<AnyElement> = self
.follow_ups .follow_ups
.iter() .iter()
.enumerate() .map(|(room, preview)| {
.map(|(index, (room, preview))| {
let room = room.clone(); let room = room.clone();
let dock = dock.clone(); let dock = dock.clone();
let name = room.read(cx).display_name(cx); let name = room.read(cx).display_name(cx);
let seed = room.read(cx).display_image_seed(cx); let seed = room.read(cx).display_image_seed(cx);
let created_at = room.read(cx).created_at.to_ago(); let created_at = room.read(cx).created_at.to_ago();
let id = room.read(cx).id;
let preview = preview.clone(); let preview = preview.clone();
h_flex() h_flex()
.id(format!("follow-up-{index}")) .id(format!("follow-up-{id}"))
.group("") .group("")
.w_full() .w_full()
.h_9() .h_9()
@@ -399,7 +399,7 @@ impl GreeterPanel {
), ),
) )
.child( .child(
Button::new(format!("reply-{index}")) Button::new(format!("reply-{id}"))
.label("Reply") .label("Reply")
.xsmall() .xsmall()
.ghost() .ghost()
+1 -1
View File
@@ -284,7 +284,7 @@ fn open_logout_dialog(window: &mut Window, cx: &mut App) {
window.open_alert_dialog(cx, |alert, _, _| { window.open_alert_dialog(cx, |alert, _, _| {
alert alert
.title("Log Out?") .title("Log out")
.description(DESCRIPTION) .description(DESCRIPTION)
.button_props( .button_props(
DialogButtonProps::default() DialogButtonProps::default()
+2 -2
View File
@@ -51,7 +51,7 @@ pub(crate) fn open_room(
.button_props( .button_props(
DialogButtonProps::default() DialogButtonProps::default()
.cancel_text("Ignore") .cancel_text("Ignore")
.ok_text("Response"), .ok_text("Respond"),
) )
.on_cancel(move |_event, window, cx| { .on_cancel(move |_event, window, cx| {
window.dispatch_action(Box::new(ClosePanel), cx); window.dispatch_action(Box::new(ClosePanel), cx);
@@ -200,7 +200,7 @@ impl Sidebar {
h_flex() h_flex()
.gap_1() .gap_1()
.when(spam, |this| { .when(spam, |this| {
this.child(Tag::danger().xsmall().child("SPAM")) this.child(Tag::danger().xsmall().child("Spam"))
}) })
.when(!spam, |this| { .when(!spam, |this| {
this.child( this.child(
+1 -1
View File
@@ -309,7 +309,7 @@ impl Sidebar {
public_key: *public_key, public_key: *public_key,
})); }));
} }
None => rows.push(SidebarRow::Empty("Searching...")), None => rows.push(SidebarRow::Empty("Searching")),
} }
} }