diff --git a/Cargo.lock b/Cargo.lock index 077bdb5..ae935a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,7 @@ name = "aichat" version = "0.8.0" dependencies = [ "anyhow", + "arboard", "atty", "base64", "bincode", @@ -29,7 +30,6 @@ dependencies = [ "bytes", "chrono", "clap", - "copypasta", "crossbeam", "crossterm 0.26.1", "ctrlc", @@ -117,6 +117,24 @@ version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +[[package]] +name = "arboard" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" +dependencies = [ + "clipboard-win", + "log", + "objc", + "objc-foundation", + "objc_id", + "once_cell", + "parking_lot", + "thiserror", + "winapi", + "x11rb", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -277,11 +295,12 @@ checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "clipboard-win" -version = "3.1.1" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" dependencies = [ - "lazy-bytes-cast", + "error-code", + "str-buf", "winapi", ] @@ -301,20 +320,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "copypasta" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133fc8675ee3a4ec9aa513584deda9aa0faeda3586b87f7f0f2ba082c66fb172" -dependencies = [ - "clipboard-win", - "objc", - "objc-foundation", - "objc_id", - "smithay-clipboard", - "x11-clipboard", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -523,21 +528,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading", -] - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - [[package]] name = "dyn-clone" version = "1.0.11" @@ -580,6 +570,16 @@ dependencies = [ "libc", ] +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + [[package]] name = "eventsource-stream" version = "0.2.3" @@ -958,12 +958,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy-bytes-cast" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" - [[package]] name = "lazy_static" version = "1.4.0" @@ -976,16 +970,6 @@ version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "link-cplusplus" version = "1.0.8" @@ -1035,15 +1019,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.6.5" @@ -1513,12 +1488,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1665,34 +1634,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" -[[package]] -name = "smithay-client-toolkit" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" -dependencies = [ - "bitflags", - "dlib", - "lazy_static", - "log", - "memmap2", - "nix 0.24.3", - "pkg-config", - "wayland-client", - "wayland-cursor", - "wayland-protocols", -] - -[[package]] -name = "smithay-clipboard" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" -dependencies = [ - "smithay-client-toolkit", - "wayland-client", -] - [[package]] name = "socket2" version = "0.4.9" @@ -1715,6 +1656,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + [[package]] name = "strip-ansi-escapes" version = "0.1.1" @@ -2139,79 +2086,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wayland-client" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.24.3", - "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys", -] - -[[package]] -name = "wayland-commons" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" -dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-cursor" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" -dependencies = [ - "nix 0.24.3", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-protocols" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" -dependencies = [ - "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", -] - -[[package]] -name = "wayland-sys" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" -dependencies = [ - "dlib", - "lazy_static", - "pkg-config", -] - [[package]] name = "web-sys" version = "0.3.61" @@ -2446,15 +2320,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "x11-clipboard" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980b9aa9226c3b7de8e2adb11bf20124327c054e0e5812d2aac0b5b5a87e7464" -dependencies = [ - "x11rb", -] - [[package]] name = "x11rb" version = "0.10.1" @@ -2476,18 +2341,3 @@ checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" dependencies = [ "nix 0.24.3", ] - -[[package]] -name = "xcursor" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" -dependencies = [ - "nom", -] - -[[package]] -name = "xml-rs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374b609fb36c36ce3501094dc0548f7df5d8d102224b65bc59812e4a5425d571" diff --git a/Cargo.toml b/Cargo.toml index 834b485..7c1ee7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ base64 = "0.21.0" rustc-hash = "1.1.0" bstr = "1.3.0" nu-ansi-term = "0.47.0" -copypasta = "0.8.2" +arboard = { version = "3.2.0", default-features = false } [dependencies.reqwest] version = "0.11.14" diff --git a/src/config/mod.rs b/src/config/mod.rs index 021d2c1..900f129 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -10,7 +10,6 @@ use crate::config::message::num_tokens_from_messages; use crate::utils::{mask_text, now}; use anyhow::{anyhow, bail, Context, Result}; -use copypasta::{ClipboardContext, ClipboardProvider}; use inquire::{Confirm, Text}; use parking_lot::RwLock; use serde::Deserialize; @@ -71,7 +70,7 @@ pub struct Config { pub light_theme: bool, /// Set a timeout in seconds for connect to gpt pub connect_timeout: usize, - /// Automatically copy the last message output to the clipboard + /// Automatically copy the last output to the clipboard pub auto_copy: bool, /// Predefined roles #[serde(skip)] @@ -173,20 +172,6 @@ impl Config { Ok(path) } - pub fn copy_message(&self, output: &str) -> Result<()> { - if self.auto_copy { - match ClipboardContext::new() { - Ok(mut ctx) => { - ctx.set_contents(output.to_string()).map_err(|err| { - anyhow!("Error: Unable to the last message output to clipboard, {err}") - })?; - } - Err(_) => bail!("Error: Unable to access clipboard context."), - } - } - Ok(()) - } - pub fn save_message(&self, input: &str, output: &str) -> Result<()> { if !self.save { return Ok(()); diff --git a/src/repl/handler.rs b/src/repl/handler.rs index c38f607..495413c 100644 --- a/src/repl/handler.rs +++ b/src/repl/handler.rs @@ -2,6 +2,7 @@ use crate::client::ChatGptClient; use crate::config::SharedConfig; use crate::print_now; use crate::render::render_stream; +use crate::utils::copy; use super::abort::SharedAbortSignal; @@ -20,6 +21,7 @@ pub enum ReplCmd { ViewInfo, StartConversation, EndConversatoin, + Copy, } pub struct ReplCmdHandler { @@ -64,7 +66,9 @@ impl ReplCmdHandler { wg.wait(); let buffer = ret?; self.config.read().save_message(&input, &buffer)?; - let _ = self.config.read().copy_message(&buffer); + if self.config.read().auto_copy { + let _ = copy(&buffer); + } self.config.write().save_conversation(&input, &buffer)?; *self.reply.borrow_mut() = buffer; } @@ -100,6 +104,10 @@ impl ReplCmdHandler { self.config.write().end_conversation(); print_now!("\n"); } + ReplCmd::Copy => { + copy(&self.reply.borrow()).with_context(|| "Failed to copy the last output")?; + print_now!("\n"); + } } Ok(()) } diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 6bedfa3..8f3d184 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -19,7 +19,7 @@ use reedline::Signal; use std::borrow::Cow; use std::sync::Arc; -pub const REPL_COMMANDS: [(&str, &str); 12] = [ +pub const REPL_COMMANDS: [(&str, &str); 13] = [ (".info", "Print system-wide information"), (".set", "Modify the configuration temporarily"), (".model", "Choose a model"), @@ -28,6 +28,7 @@ pub const REPL_COMMANDS: [(&str, &str); 12] = [ (".clear role", "Clear the currently selected role"), (".conversation", "Start a conversation."), (".clear conversation", "End current conversation."), + (".copy", "Copy the last output to the clipboard"), (".history", "Print the history"), (".clear history", "Clear the history"), (".help", "Print this help message"), @@ -138,6 +139,9 @@ impl Repl { ".conversation" => { handler.handle(ReplCmd::StartConversation)?; } + ".copy" => { + handler.handle(ReplCmd::Copy)?; + } _ => dump_unknown_command(), }, None => { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 64fc01c..ec57fed 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,6 +2,7 @@ mod tiktoken; pub use self::tiktoken::{cl100k_base_singleton, count_tokens, text_to_tokens, tokens_to_text}; +use arboard::Clipboard; use chrono::prelude::*; use crossterm::style::{Color, Stylize}; use std::io::{stdout, Write}; @@ -35,6 +36,11 @@ pub fn mask_text(text: &str, head: usize, tail: usize) -> String { format!("{}...{}", &text[0..head], &text[text.len() - tail..]) } +pub fn copy(src: &str) -> Result<(), arboard::Error> { + let mut clipboard = Clipboard::new()?; + clipboard.set_text(src) +} + #[cfg(test)] mod tests { use super::*;