From 71b1613d8467cfcb2383b3fce153a25c7883ac3d Mon Sep 17 00:00:00 2001 From: asepharyana Date: Wed, 15 Jul 2026 06:09:36 +0700 Subject: [PATCH] feat: Tambah write_osc52 dan salin ke clipboard di mode single-process --- src/main.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main.rs b/src/main.rs index 19551a5..be77b8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -627,6 +627,21 @@ fn run_loop( result } +/// Write text to the system clipboard via an OSC52 terminal escape sequence. +/// +/// Flow: base64-encode `text` -> wrap in `\x1b]52;c;\x07` -> write and +/// flush to `stdout`. +/// +/// Why: OSC52 asks the terminal emulator itself to set the clipboard, so no +/// OS-level clipboard library (X11/Wayland/win32) is needed. Terminals that +/// don't support it silently ignore the sequence. +fn write_osc52(stdout: &mut impl Write, text: &str) -> io::Result<()> { + use base64::Engine as _; + let b64 = base64::engine::general_purpose::STANDARD.encode(text); + write!(stdout, "\x1b]52;c;{b64}\x07")?; + stdout.flush() +} + /// The core single-process render/input loop. /// /// Flow: until `state.quit` → drain expired toasts → draw the frame → @@ -665,6 +680,13 @@ fn run_loop_inner( for action in actions { apply_action(state, action); } + if let Some(text) = state.misc.pending_clipboard_copy.take() { + let _ = write_osc52(&mut io::stdout(), &text); + state.push_toast(app::state::types::Toast::new( + app::state::types::ToastKind::Success, + "Copied to clipboard".to_string(), + )); + } } } Event::Paste(text) => { @@ -699,3 +721,18 @@ fn run_loop_inner( terminal.clear()?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::write_osc52; + + #[test] + fn write_osc52_formats_the_escape_sequence() { + let mut buf: Vec = Vec::new(); + write_osc52(&mut buf, "hello").unwrap(); + use base64::Engine as _; + let b64 = base64::engine::general_purpose::STANDARD.encode("hello"); + let expected = format!("\x1b]52;c;{b64}\x07"); + assert_eq!(String::from_utf8(buf).unwrap(), expected); + } +}