summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgcv <vetoshev@gmail.com>2025-05-08 17:17:24 -0700
committerGitHub <noreply@github.com>2025-05-09 08:17:24 +0800
commite3e6388ac3bc027407ca4e72da35f9a80656121e (patch)
tree80523fa54899297e07d2b04e35995d620cb4df90
parent4c110b0429fe8f84e34abe8991af1b52d35bd4cf (diff)
downloadaichat-e3e6388ac3bc027407ca4e72da35f9a80656121e.tar.gz
feat: add support for OSC52 clipboard codes (#1280)
-rw-r--r--src/utils/clipboard.rs33
1 files changed, 28 insertions, 5 deletions
diff --git a/src/utils/clipboard.rs b/src/utils/clipboard.rs
index 12e1655..cb92bce 100644
--- a/src/utils/clipboard.rs
+++ b/src/utils/clipboard.rs
@@ -1,19 +1,42 @@
+use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
+
#[cfg(not(any(target_os = "android", target_os = "emscripten")))]
static CLIPBOARD: std::sync::LazyLock<std::sync::Mutex<Option<arboard::Clipboard>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(arboard::Clipboard::new().ok()));
+/// Attempts to set text to clipboard with OSC52 escape sequence
+/// Works in many modern terminals, including over SSH.
+fn set_text_osc52(text: &str) -> anyhow::Result<()> {
+ let encoded = BASE64.encode(text);
+ let seq = format!("\x1b]52;c;{}\x07", encoded);
+ // Write to stdout:
+ if let Err(e) = std::io::Write::write_all(&mut std::io::stdout(), seq.as_bytes()) {
+ return Err(anyhow::anyhow!("Failed to send OSC52 sequence").context(e));
+ }
+ // Flush stdout:
+ if let Err(e) = std::io::Write::flush(&mut std::io::stdout()) {
+ return Err(anyhow::anyhow!("Failed to flush OSC52 sequence").context(e));
+ }
+ Ok(())
+}
+
#[cfg(not(any(target_os = "android", target_os = "emscripten")))]
pub fn set_text(text: &str) -> anyhow::Result<()> {
+ // First try arboard:
let mut clipboard = CLIPBOARD.lock().unwrap();
- match clipboard.as_mut() {
- Some(clipboard) => {
- clipboard.set_text(text)?;
+ if let Some(clipboard) = clipboard.as_mut() {
+ if let Ok(()) = clipboard.set_text(text) {
#[cfg(target_os = "linux")]
std::thread::sleep(std::time::Duration::from_millis(50));
+ return Ok(());
}
- None => return Err(anyhow::anyhow!("No clipboard available").context("Failed to copy")),
}
- Ok(())
+ // If arboard failed, try OSC52:
+ match set_text_osc52(text) {
+ Ok(()) => Ok(()),
+ Err(osc_err) => Err(anyhow::anyhow!("No clipboard available")
+ .context(format!("Failed to copy (OSC52 error: {})", osc_err))),
+ }
}
#[cfg(any(target_os = "android", target_os = "emscripten"))]