From 5b384487331eaf08031dfe438bb2affa31aafcbb Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Mon, 8 Jul 2024 11:17:47 +0100 Subject: feat(gui): runbooks that run (#2233) * add initial runbooks frontend * fix buttons, scroll, add shell support to editor * work * some tweaks * wip - run crate * functioning executable blocks * handle resizing, killing ptys * clear properly on stop * move terminal to its own component, handle lifecycle better * fix all build issues * ffs codespelll * update lockfile * clippy is needy once more * only build pty stuff on mac/linux * vendor pty handling into desktop * update lockfile --- ui/backend/src/run/mod.rs | 1 + ui/backend/src/run/pty.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 ui/backend/src/run/mod.rs create mode 100644 ui/backend/src/run/pty.rs (limited to 'ui/backend/src/run') diff --git a/ui/backend/src/run/mod.rs b/ui/backend/src/run/mod.rs new file mode 100644 index 00000000..5ece0912 --- /dev/null +++ b/ui/backend/src/run/mod.rs @@ -0,0 +1 @@ +pub mod pty; diff --git a/ui/backend/src/run/pty.rs b/ui/backend/src/run/pty.rs new file mode 100644 index 00000000..382b45dd --- /dev/null +++ b/ui/backend/src/run/pty.rs @@ -0,0 +1,93 @@ +use eyre::{Result, WrapErr}; +use std::io::BufRead; +use std::path::PathBuf; + +use crate::state::AtuinState; +use tauri::{Manager, State}; + +use atuin_client::{database::Sqlite, record::sqlite_store::SqliteStore, settings::Settings}; + +#[tauri::command] +pub async fn pty_open<'a>( + app: tauri::AppHandle, + state: State<'a, AtuinState>, +) -> Result { + let id = uuid::Uuid::new_v4(); + let pty = crate::pty::Pty::open(24, 80).await.unwrap(); + + let reader = pty.reader.clone(); + + tauri::async_runtime::spawn_blocking(move || loop { + let mut buf = [0u8; 512]; + + match reader.lock().unwrap().read(&mut buf) { + // EOF + Ok(0) => { + println!("reader loop hit eof"); + break; + } + + Ok(n) => { + println!("read {n} bytes"); + + // TODO: sort inevitable encoding issues + let out = String::from_utf8_lossy(&buf).to_string(); + let out = out.trim_matches(char::from(0)); + let channel = format!("pty-{id}"); + + app.emit(channel.as_str(), out).unwrap(); + } + + Err(e) => { + println!("failed to read: {e}"); + break; + } + } + }); + + state.pty_sessions.write().await.insert(id, pty); + + Ok(id) +} + +#[tauri::command] +pub(crate) async fn pty_write( + pid: uuid::Uuid, + data: String, + state: tauri::State<'_, AtuinState>, +) -> Result<(), String> { + let sessions = state.pty_sessions.read().await; + let pty = sessions.get(&pid).ok_or("Pty not found")?; + + let bytes = data.as_bytes().to_vec(); + pty.send_bytes(bytes.into()) + .await + .map_err(|e| e.to_string())?; + Ok(()) +} + +#[tauri::command] +pub(crate) async fn pty_resize( + pid: uuid::Uuid, + rows: u16, + cols: u16, + state: tauri::State<'_, AtuinState>, +) -> Result<(), String> { + let sessions = state.pty_sessions.read().await; + let pty = sessions.get(&pid).ok_or("Pty not found")?; + + pty.resize(rows, cols).await.map_err(|e| e.to_string())?; + + Ok(()) +} + +#[tauri::command] +pub(crate) async fn pty_kill( + pid: uuid::Uuid, + state: tauri::State<'_, AtuinState>, +) -> Result<(), String> { + let pty = state.pty_sessions.write().await.remove(&pid).unwrap(); + println!("RIP {pid:?}"); + + Ok(()) +} -- cgit v1.3.1