Refine GPUI shell UX surfaces

This commit is contained in:
ZacharyZhang-NY committed 2026-04-19 13:32:58 -04:00
1 parent 22ba7a9004
commit 6809df15de
7 files changed
+617 -122

No files matched your search

@@ -1,4 +1,10 @@
use super::*;
use super::{
messages_parts::{
compact_message_card, composer_chip, composer_subject, filter_shell, message_section_label,
search_shell, secondary_message, selected_message_card, selected_recipient,
},
*,
};
impl AppRoot {
pub(super) fn render_messages(&self, cx: &mut Context<Self>) -> impl IntoElement + '_ {
@@ -8,36 +14,60 @@ impl AppRoot {
.iter()
.find(|item| &item.id == id)
});
let selected_subject = selected
.map(|item| item.subject.clone())
.unwrap_or_else(|| "Select a thread".to_string());
let selected_body = selected
.map(|item| item.body.clone())
.unwrap_or_else(|| "No message selected.".to_string());
let company_wide = self
.snapshot
.agent_messages
.iter()
.filter(|item| item.channel == "company")
.collect::<Vec<_>>();
let department = self
.snapshot
.agent_messages
.iter()
.filter(|item| item.channel == "department")
.collect::<Vec<_>>();
let direct = self
.snapshot
.agent_messages
.iter()
.filter(|item| item.channel == "direct")
.collect::<Vec<_>>();
let task_threads = self
.snapshot
.agent_messages
.iter()
.filter(|item| {
item.channel != "company"
&& item.channel != "department"
&& item.channel != "direct"
})
.collect::<Vec<_>>();
div()
.flex()
.gap_4()
.child(
div()
.id("messages-list")
.w(px(320.0))
.p_4()
.gap_2()
.w(px(356.0))
.p_5()
.gap_4()
.border_1()
.rounded_md()
.rounded_lg()
.border_color(theme::panel_border())
.bg(theme::panel())
.overflow_scroll()
.child(div().text_lg().child("Channels"))
.children(self.snapshot.agent_messages.iter().map(|item| {
.child(message_section_label("COMPANY-WIDE"))
.children(company_wide.iter().map(|message| {
let active =
self.state.selected_message_id.as_deref() == Some(item.id.as_str());
let message_id = item.id.clone();
self.state.selected_message_id.as_deref() == Some(message.id.as_str());
let message_id = message.id.clone();
div()
.id(SharedString::from(format!("message-{}", item.id)))
.px_3()
.py_2()
.rounded_md()
.id(SharedString::from(format!("message-{}", message.id)))
.flex()
.justify_between()
.items_center()
.px_4()
.py_3()
.rounded_lg()
.cursor_pointer()
.bg(if active {
theme::selection_fill()
@@ -49,46 +79,247 @@ impl AppRoot {
this.state.selected_message_id = Some(message_id.clone());
cx.notify();
}))
.child(format!("{} · {}", item.subject, item.priority))
})),
.child(
div()
.text_sm()
.text_color(if active {
theme::sidebar_text_active()
} else {
theme::text_secondary()
})
.truncate()
.child(message.subject.clone()),
)
.when(message.priority == "urgent", |container| {
container.child(
div()
.w(px(10.0))
.h(px(10.0))
.rounded_full()
.bg(theme::danger()),
)
})
}))
.child(message_section_label("DEPARTMENT"))
.children(department.iter().map(|message| {
let active =
self.state.selected_message_id.as_deref() == Some(message.id.as_str());
let message_id = message.id.clone();
div()
.id(SharedString::from(format!("message-{}", message.id)))
.flex()
.justify_between()
.items_center()
.px_4()
.py_3()
.rounded_lg()
.cursor_pointer()
.bg(if active {
theme::selection_fill()
} else {
theme::panel()
})
.hover(|style| style.bg(theme::panel_hover()))
.on_click(cx.listener(move |this, _, _, cx| {
this.state.selected_message_id = Some(message_id.clone());
cx.notify();
}))
.child(
div()
.text_sm()
.text_color(if active {
theme::sidebar_text_active()
} else {
theme::text_secondary()
})
.truncate()
.child(message.subject.clone()),
)
}))
.child(message_section_label("DIRECT MESSAGES"))
.children(direct.iter().map(|message| {
let active =
self.state.selected_message_id.as_deref() == Some(message.id.as_str());
let message_id = message.id.clone();
div()
.id(SharedString::from(format!("message-{}", message.id)))
.flex()
.justify_between()
.items_center()
.px_4()
.py_3()
.rounded_lg()
.cursor_pointer()
.bg(if active {
theme::selection_fill()
} else {
theme::panel()
})
.hover(|style| style.bg(theme::panel_hover()))
.on_click(cx.listener(move |this, _, _, cx| {
this.state.selected_message_id = Some(message_id.clone());
cx.notify();
}))
.child(
div()
.text_sm()
.text_color(if active {
theme::sidebar_text_active()
} else {
theme::text_secondary()
})
.truncate()
.child(message.subject.clone()),
)
.when(message.read_at.is_none(), |container| {
container.child(
div()
.w(px(10.0))
.h(px(10.0))
.rounded_full()
.bg(theme::danger()),
)
})
}))
.child(message_section_label("TASK DISCUSSIONS"))
.children(task_threads.iter().map(|message| {
let active =
self.state.selected_message_id.as_deref() == Some(message.id.as_str());
let message_id = message.id.clone();
div()
.id(SharedString::from(format!("message-{}", message.id)))
.flex()
.justify_between()
.items_center()
.px_4()
.py_3()
.rounded_lg()
.cursor_pointer()
.bg(if active {
theme::selection_fill()
} else {
theme::panel()
})
.hover(|style| style.bg(theme::panel_hover()))
.on_click(cx.listener(move |this, _, _, cx| {
this.state.selected_message_id = Some(message_id.clone());
cx.notify();
}))
.child(
div()
.text_sm()
.text_color(if active {
theme::sidebar_text_active()
} else {
theme::text_secondary()
})
.truncate()
.child(message.subject.clone()),
)
}))
.child(div().flex_1())
.child(
div()
.flex()
.justify_between()
.text_xs()
.text_color(theme::text_muted())
.child(format!("{} channels", self.snapshot.agent_messages.len()))
.child(format!("{} agents", self.snapshot.agents.len())),
),
)
.child(
div()
.flex_1()
.p_4()
.gap_3()
.border_1()
.rounded_md()
.rounded_lg()
.border_color(theme::panel_border())
.bg(theme::panel())
.child(div().text_lg().child("Conversation"))
.child(
div()
.text_sm()
.text_color(theme::text_muted())
.child(selected_subject),
)
.child(
div()
.min_h(px(220.0))
.p_3()
.border_1()
.border_color(theme::panel_inset_border())
.rounded_md()
.bg(theme::panel_inset())
.child(selected_body),
)
.child(self.message_input.clone())
.child(
div()
.flex()
.gap_2()
.child(primary_button("Send update", cx, |this, cx| {
this.send_message(cx)
}))
.child(secondary_button("Mark read", cx, |this, cx| {
this.mark_selected_message_read(cx)
})),
.justify_between()
.items_center()
.px_5()
.py_4()
.border_b_1()
.border_color(theme::panel_border())
.child(
div().text_lg().text_color(theme::text_primary()).child(
selected
.map(|message| message.subject.clone())
.unwrap_or_else(|| "Select a conversation".to_string()),
),
)
.child(
div()
.flex()
.gap_3()
.child(search_shell("Search messages"))
.child(filter_shell()),
),
)
.child(
div()
.id("messages-stream")
.flex_1()
.p_4()
.overflow_scroll()
.child(selected_message_card(selected))
.when_some(secondary_message(selected), |container, message| {
container.child(compact_message_card(message))
}),
)
.child(
div()
.px_4()
.py_3()
.border_t_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.child(
div()
.flex()
.gap_3()
.child(composer_chip(selected_recipient(selected).as_str()))
.child(composer_subject(selected)),
)
.child(
div()
.mt(px(12.0))
.p_3()
.rounded_lg()
.bg(theme::panel_inset())
.border_1()
.border_color(theme::panel_inset_border())
.child(self.message_input.clone()),
)
.child(
div()
.mt(px(10.0))
.flex()
.justify_between()
.items_center()
.child(
div()
.text_xs()
.text_color(theme::text_muted())
.child("Press Cmd+Enter to send"),
)
.child(
div()
.flex()
.gap_2()
.child(primary_button("Send update", cx, |this, cx| {
this.send_message(cx)
}))
.child(secondary_button(
"Mark read",
cx,
|this, cx| this.mark_selected_message_read(cx),
)),
),
),
),
)
}