commit d944ad79196dfd53ef453ace3b598511fa1728d6
parent ee630392db5de72799353b730efa28ee685b8b29
Author: William Casarin <jb55@jb55.com>
Date: Fri, 20 Feb 2026 12:48:50 -0800
messages: add copy context menus for chat bubbles
Long-press (mobile) or right-click (desktop) on sent and received
messages shows a Copy option, matching the pattern used in dave.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
1 file changed, 54 insertions(+), 35 deletions(-)
diff --git a/crates/notedeck_messages/src/ui/convo.rs b/crates/notedeck_messages/src/ui/convo.rs
@@ -428,7 +428,7 @@ fn self_chat_bubble(
timestamp: u64,
) -> egui::Response {
let bubble_fill = ui.visuals().selection.bg_fill;
- ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
+ let r = ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
chat_bubble(ui, msg_type, true, bubble_fill, |ui| {
ui.with_layout(Layout::top_down(Align::Max), |ui| {
ui.add(
@@ -448,8 +448,14 @@ fn self_chat_bubble(
})
})
.inner
- })
- .response
+ });
+ r.response.context_menu(|ui| {
+ if ui.button("Copy").clicked() {
+ ui.ctx().copy_text(message.to_owned());
+ ui.close_menu();
+ }
+ });
+ r.response
}
fn other_chat_bubble(
@@ -459,46 +465,59 @@ fn other_chat_bubble(
msg_type: MessageType,
) -> egui::Response {
let message = chat_msg.message;
+ let message_owned = message.to_owned();
let bubble_fill = ui.visuals().extreme_bg_color;
let text_color = ui.visuals().text_color();
let secondary_color = ui.visuals().weak_text_color();
- chat_bubble(ui, msg_type, false, bubble_fill, |ui| {
- ui.vertical(|ui| {
- if msg_type == MessageType::FirstInSeries || msg_type == MessageType::Standalone {
- ui.label(
- RichText::new(sender_name.name())
- .strong()
- .color(secondary_color),
- );
- ui.add_space(2.0);
- }
-
- ui.with_layout(
- Layout::left_to_right(Align::Max).with_main_wrap(true),
- |ui| {
- ui.add(
- egui::Label::new(RichText::new(message).color(text_color)).selectable(true),
+ let r = ui.scope(|ui| {
+ chat_bubble(ui, msg_type, false, bubble_fill, |ui| {
+ ui.vertical(|ui| {
+ if msg_type == MessageType::FirstInSeries || msg_type == MessageType::Standalone {
+ ui.label(
+ RichText::new(sender_name.name())
+ .strong()
+ .color(secondary_color),
);
- if msg_type == MessageType::Standalone || msg_type == MessageType::LastInSeries
- {
- ui.add_space(6.0);
- let timestamp_label =
- format_timestamp_label(&local_datetime_from_nostr(chat_msg.created_at));
+ ui.add_space(2.0);
+ }
+
+ ui.with_layout(
+ Layout::left_to_right(Align::Max).with_main_wrap(true),
+ |ui| {
ui.add(
- egui::Label::new(
- RichText::new(timestamp_label)
- .small()
- .color(secondary_color),
- )
- .wrap_mode(egui::TextWrapMode::Extend),
+ egui::Label::new(RichText::new(message).color(text_color))
+ .selectable(true),
);
- }
- },
- );
+ if msg_type == MessageType::Standalone
+ || msg_type == MessageType::LastInSeries
+ {
+ ui.add_space(6.0);
+ let timestamp_label = format_timestamp_label(
+ &local_datetime_from_nostr(chat_msg.created_at),
+ );
+ ui.add(
+ egui::Label::new(
+ RichText::new(timestamp_label)
+ .small()
+ .color(secondary_color),
+ )
+ .wrap_mode(egui::TextWrapMode::Extend),
+ );
+ }
+ },
+ );
+ })
+ .response
})
- .response
- })
+ });
+ r.response.context_menu(|ui| {
+ if ui.button("Copy").clicked() {
+ ui.ctx().copy_text(message_owned.clone());
+ ui.close_menu();
+ }
+ });
+ r.response
}
/// An unfortunate hack to change the corner radius of a TextEdit...