diff --git a/assets/icons/copy.svg b/assets/icons/copy.svg
new file mode 100644
index 0000000..5af5438
--- /dev/null
+++ b/assets/icons/copy.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/coop/src/views/chat.rs b/crates/coop/src/views/chat.rs
index 80045cc..d56879a 100644
--- a/crates/coop/src/views/chat.rs
+++ b/crates/coop/src/views/chat.rs
@@ -10,10 +10,10 @@ use common::{nip96_upload, profile::RenderProfile};
use global::get_client;
use gpui::{
div, img, impl_internal_actions, list, prelude::FluentBuilder, px, red, relative, rems, svg,
- white, AnyElement, App, AppContext, Context, Div, Element, Empty, Entity, EventEmitter,
- Flatten, FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListState,
- ObjectFit, ParentElement, PathPromptOptions, Render, RetainAllImageCache, SharedString,
- StatefulInteractiveElement, Styled, StyledImage, Subscription, Window,
+ white, AnyElement, App, AppContext, ClipboardItem, Context, Div, Element, Empty, Entity,
+ EventEmitter, Flatten, FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment,
+ ListState, ObjectFit, ParentElement, PathPromptOptions, Render, RetainAllImageCache,
+ SharedString, StatefulInteractiveElement, Styled, StyledImage, Subscription, Window,
};
use itertools::Itertools;
use nostr_sdk::prelude::*;
@@ -30,7 +30,7 @@ use ui::{
notification::Notification,
popup_menu::PopupMenu,
text::RichText,
- v_flex, ContextModal, Disableable, Icon, IconName, Sizable, StyledExt,
+ v_flex, ContextModal, Disableable, Icon, IconName, InteractiveElementExt, Sizable, StyledExt,
};
use crate::views::subject;
@@ -700,28 +700,50 @@ impl Chat {
)
.child(message_border(cx))
.child(message_actions(
- vec![Button::new("reply")
- .icon(IconName::Reply)
- .tooltip("Reply")
- .small()
- .ghost()
- .on_click({
- let message = message.clone();
- cx.listener(move |this, _, _, cx| {
- this.reply(message.clone(), cx);
- })
- })],
+ vec![
+ Button::new("reply")
+ .icon(IconName::Reply)
+ .tooltip("Reply")
+ .small()
+ .ghost()
+ .on_click({
+ let message = message.clone();
+ cx.listener(move |this, _event, _window, cx| {
+ this.reply(message.clone(), cx);
+ })
+ }),
+ Button::new("copy")
+ .icon(IconName::Copy)
+ .tooltip("Copy Message")
+ .small()
+ .ghost()
+ .on_click({
+ let content = ClipboardItem::new_string(message.content.to_string());
+ cx.listener(move |_this, _event, _window, cx| {
+ #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+ cx.write_to_primary(content.clone());
+ #[cfg(any(target_os = "windows", target_os = "macos"))]
+ cx.write_to_clipboard(content.clone());
+ })
+ }),
+ ],
cx,
))
- .on_mouse_down(
- gpui::MouseButton::Middle,
- cx.listener({
- let message = message.clone();
- move |this, _, _window, cx| {
- this.reply(message.clone(), cx);
- }
- }),
- )
+ .on_mouse_down(gpui::MouseButton::Middle, {
+ let content = ClipboardItem::new_string(message.content.to_string());
+ cx.listener(move |_this, _event, _window, cx| {
+ #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+ cx.write_to_primary(content.clone());
+ #[cfg(any(target_os = "windows", target_os = "macos"))]
+ cx.write_to_clipboard(content.clone());
+ })
+ })
+ .on_double_click(cx.listener({
+ let message = message.clone();
+ move |this, _, _window, cx| {
+ this.reply(message.clone(), cx);
+ }
+ }))
.hover(|this| this.bg(cx.theme().surface_background))
}
}
@@ -885,7 +907,7 @@ fn message_errors(errors: Vec, cx: &App) -> Div {
}))
}
-fn message_actions(buttons: Vec