mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
copy details window
This commit is contained in:
Generated
+119
-7
@@ -190,6 +190,26 @@ dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"image 0.25.5",
|
||||
"log",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.59.0",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arg_enum_proc_macro"
|
||||
version = "0.3.4"
|
||||
@@ -2188,6 +2208,16 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
@@ -2713,6 +2743,16 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
|
||||
dependencies = [
|
||||
"rustix 1.0.7",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
@@ -3108,6 +3148,7 @@ name = "goose-tui"
|
||||
version = "1.14.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
"axum 0.8.1",
|
||||
"chrono",
|
||||
"clap",
|
||||
@@ -4737,13 +4778,49 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.1"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
|
||||
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-app-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"dispatch2",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
@@ -4752,12 +4829,24 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8589,6 +8678,12 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.2.0"
|
||||
@@ -8624,7 +8719,7 @@ version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8643,7 +8738,7 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8734,7 +8829,7 @@ version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.3",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
@@ -9005,6 +9100,23 @@ version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"rustix 1.0.7",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.5.0"
|
||||
|
||||
@@ -40,6 +40,7 @@ syntect = "5.2"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
termimad = "0.34.0"
|
||||
unicode-width = "0.2.0"
|
||||
arboard = "3.6.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.43", features = ["full"] }
|
||||
|
||||
@@ -49,6 +49,7 @@ impl Component for HelpPopup {
|
||||
Line::from(" Esc Switch to Normal mode / Close popups"),
|
||||
Line::from(" i Switch to Editing mode"),
|
||||
Line::from(" j / k Scroll / Navigate"),
|
||||
Line::from(" Ctrl+S Toggle copy mode (disable mouse capture)"),
|
||||
Line::from(" Ctrl+T Toggle Todo List"),
|
||||
Line::from(" Ctrl+C Interrupt / Clear / Quit"),
|
||||
Line::from(""),
|
||||
@@ -57,6 +58,7 @@ impl Component for HelpPopup {
|
||||
Style::default().add_modifier(Modifier::UNDERLINED),
|
||||
)),
|
||||
Line::from(" /help Show this help"),
|
||||
Line::from(" /copy Toggle copy mode"),
|
||||
Line::from(" /config Open configuration"),
|
||||
Line::from(" /session Open session picker"),
|
||||
Line::from(" /theme Change theme (e.g. /theme light)"),
|
||||
|
||||
@@ -21,6 +21,8 @@ pub struct MessagePopup {
|
||||
last_scroll_time: Option<Instant>,
|
||||
content_height: u16,
|
||||
viewport_height: u16,
|
||||
cached_message_idx: Option<usize>,
|
||||
cached_plain_text: String,
|
||||
}
|
||||
|
||||
impl Default for MessagePopup {
|
||||
@@ -36,6 +38,8 @@ impl MessagePopup {
|
||||
last_scroll_time: None,
|
||||
content_height: 0,
|
||||
viewport_height: 0,
|
||||
cached_message_idx: None,
|
||||
cached_plain_text: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +51,55 @@ impl MessagePopup {
|
||||
}
|
||||
}
|
||||
|
||||
fn content_as_plain_text(message: &goose::conversation::message::Message) -> String {
|
||||
let mut lines = Vec::new();
|
||||
|
||||
for content in &message.content {
|
||||
match content {
|
||||
MessageContent::Text(t) => {
|
||||
lines.push(t.text.clone());
|
||||
}
|
||||
MessageContent::ToolRequest(req) => {
|
||||
lines.push("Tool Request:".to_string());
|
||||
if let Ok(call) = &req.tool_call {
|
||||
lines.push(format!("Name: {}", call.name));
|
||||
if let Some(args) = &call.arguments {
|
||||
if let Ok(json_str) = serde_json::to_string_pretty(args) {
|
||||
lines.push(json_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageContent::ToolResponse(resp) => {
|
||||
lines.push("Tool Output:".to_string());
|
||||
if let Ok(contents) = &resp.tool_result {
|
||||
for content in contents {
|
||||
if let rmcp::model::Content {
|
||||
raw: rmcp::model::RawContent::Text(text_content),
|
||||
..
|
||||
} = content
|
||||
{
|
||||
let text = &text_content.text;
|
||||
let display_string = if let Ok(v) =
|
||||
serde_json::from_str::<serde_json::Value>(text)
|
||||
{
|
||||
serde_json::to_string_pretty(&v)
|
||||
.unwrap_or_else(|_| text.to_string())
|
||||
} else {
|
||||
text.to_string()
|
||||
};
|
||||
lines.push(display_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
lines.push(String::new());
|
||||
}
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
fn render_content(
|
||||
&self,
|
||||
message: &goose::conversation::message::Message,
|
||||
@@ -113,6 +166,14 @@ impl MessagePopup {
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
fn copy_to_clipboard(&self) -> Result<(), String> {
|
||||
let mut clipboard =
|
||||
arboard::Clipboard::new().map_err(|e| format!("Clipboard error: {e}"))?;
|
||||
clipboard
|
||||
.set_text(&self.cached_plain_text)
|
||||
.map_err(|e| format!("Clipboard error: {e}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for MessagePopup {
|
||||
@@ -120,6 +181,12 @@ impl Component for MessagePopup {
|
||||
match event {
|
||||
Event::Input(key) => match key.code {
|
||||
KeyCode::Esc | KeyCode::Char('q') => return Ok(Some(Action::ClosePopup)),
|
||||
KeyCode::Char('c') => {
|
||||
return match self.copy_to_clipboard() {
|
||||
Ok(()) => Ok(Some(Action::ShowFlash("Copied to clipboard".to_string()))),
|
||||
Err(e) => Ok(Some(Action::ShowFlash(e))),
|
||||
};
|
||||
}
|
||||
KeyCode::Char('j') | KeyCode::Down => {
|
||||
self.scroll = self.scroll.saturating_add(1).min(self.max_scroll());
|
||||
self.last_scroll_time = Some(Instant::now());
|
||||
@@ -157,18 +224,23 @@ impl Component for MessagePopup {
|
||||
None => return,
|
||||
};
|
||||
|
||||
if self.cached_message_idx != Some(msg_idx) {
|
||||
self.cached_message_idx = Some(msg_idx);
|
||||
self.cached_plain_text = Self::content_as_plain_text(message);
|
||||
self.scroll = 0;
|
||||
}
|
||||
|
||||
let area = centered_rect(80, 80, area);
|
||||
f.render_widget(Clear, area);
|
||||
|
||||
let lines = self.render_content(message);
|
||||
|
||||
let block = Block::default()
|
||||
.title("Message Details (Esc to Close)")
|
||||
.title("Message Details (Esc to close, c to copy)")
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(state.config.theme.base.background));
|
||||
|
||||
// Calculate height with wrapping estimation
|
||||
let inner_width = area.width.saturating_sub(2).max(1);
|
||||
let mut wrapped_height = 0;
|
||||
for line in &lines {
|
||||
|
||||
Reference in New Issue
Block a user