lv-tui 0.3.0

A reactive TUI framework for Rust, inspired by Textual and React
Documentation
//! System clipboard integration via OSC 52 escape sequences.
//!
//! Most modern terminals (Alacritty, Kitty, iTerm2, WezTerm, foot, etc.)
//! support OSC 52 for clipboard access. For terminals that don't, these
//! operations silently no-op.

/// Copies `text` to the system clipboard using OSC 52.
///
/// Returns the escape sequence. Write this to stdout (via the backend) to
/// set the clipboard. Most modern terminals support OSC 52.
pub fn copy_to_clipboard_sequence(text: &str) -> String {
    let encoded = base64_encode(text.as_bytes());
    format!("\x1b]52;c;{}\x07", encoded)
}

/// Returns the OSC 52 sequence to request clipboard contents.
pub fn request_clipboard_sequence() -> String {
    "\x1b]52;c;?\x07".to_string()
}

const BASE64_CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

fn base64_encode(data: &[u8]) -> String {
    let mut result = String::new();
    for chunk in data.chunks(3) {
        let b0 = chunk[0] as u32;
        let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
        let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
        let triple = (b0 << 16) | (b1 << 8) | b2;
        result.push(BASE64_CHARS[((triple >> 18) & 0x3F) as usize] as char);
        result.push(BASE64_CHARS[((triple >> 12) & 0x3F) as usize] as char);
        if chunk.len() > 1 {
            result.push(BASE64_CHARS[((triple >> 6) & 0x3F) as usize] as char);
        } else {
            result.push('=');
        }
        if chunk.len() > 2 {
            result.push(BASE64_CHARS[(triple & 0x3F) as usize] as char);
        } else {
            result.push('=');
        }
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_base64_encode() {
        assert_eq!(base64_encode(b"hello"), "aGVsbG8=");
        assert_eq!(base64_encode(b"ab"), "YWI=");
        assert_eq!(base64_encode(b"abc"), "YWJj");
    }

    #[test]
    fn test_copy_sequence() {
        let seq = copy_to_clipboard_sequence("hello");
        assert!(seq.starts_with("\x1b]52;c;"));
        assert!(seq.ends_with("\x07"));
        assert!(seq.contains("aGVsbG8="));
    }
}