tela-engine 0.1.0

Runtime engine for Tela — React Native for terminals. QuickJS bridge, native APIs, and ratatui renderer.
Documentation
use rquickjs::Ctx;

const CLIPBOARD_BOOTSTRAP: &str = r#"
globalThis.clipboard = {
    write: function(text) {
        __tela_clipboard_write__(String(text));
    },
    read: function() {
        return __tela_clipboard_read__();
    },
};
"#;

pub fn register_clipboard(ctx: &Ctx<'_>) -> anyhow::Result<()> {
    let write = |text: String| {
        set_clipboard(&text);
    };

    let read = || -> Option<String> { get_clipboard() };

    ctx.globals().set(
        "__tela_clipboard_write__",
        rquickjs::Function::new(ctx.clone(), write)?,
    )?;
    ctx.globals().set(
        "__tela_clipboard_read__",
        rquickjs::Function::new(ctx.clone(), read)?,
    )?;

    ctx.eval::<(), _>(CLIPBOARD_BOOTSTRAP)
        .map_err(|e| anyhow::anyhow!("failed to register clipboard bootstrap: {e}"))?;

    Ok(())
}

fn set_clipboard(text: &str) {
    #[cfg(target_os = "linux")]
    {
        use std::io::Write;
        if let Ok(mut child) = std::process::Command::new("xclip")
            .args(["-selection", "clipboard"])
            .stdin(std::process::Stdio::piped())
            .spawn()
        {
            if let Some(ref mut stdin) = child.stdin {
                let _ = stdin.write_all(text.as_bytes());
            }
            let _ = child.wait();
        }
    }
    #[cfg(target_os = "macos")]
    {
        use std::io::Write;
        if let Ok(mut child) = std::process::Command::new("pbcopy")
            .stdin(std::process::Stdio::piped())
            .spawn()
        {
            if let Some(ref mut stdin) = child.stdin {
                let _ = stdin.write_all(text.as_bytes());
            }
            let _ = child.wait();
        }
    }
}

fn get_clipboard() -> Option<String> {
    #[cfg(target_os = "linux")]
    {
        std::process::Command::new("xclip")
            .args(["-selection", "clipboard", "-o"])
            .output()
            .ok()
            .and_then(|o| String::from_utf8(o.stdout).ok())
    }
    #[cfg(target_os = "macos")]
    {
        std::process::Command::new("pbpaste")
            .output()
            .ok()
            .and_then(|o| String::from_utf8(o.stdout).ok())
    }
    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    {
        None
    }
}