beam-worker 0.6.4

Per-session worker process for beam that owns terminal backends and CLI adapters
Documentation
use super::*;

pub(crate) fn term_action_keys(key: TermActionKey) -> Vec<String> {
    match key {
        TermActionKey::Esc => vec!["Escape".to_string()],
        TermActionKey::CtrlC => vec!["C-c".to_string()],
        TermActionKey::Tab => vec!["Tab".to_string()],
        TermActionKey::Enter => vec!["Enter".to_string()],
        TermActionKey::Space => vec!["Space".to_string()],
        TermActionKey::Up => vec!["Up".to_string()],
        TermActionKey::Down => vec!["Down".to_string()],
        TermActionKey::Left => vec!["Left".to_string()],
        TermActionKey::Right => vec!["Right".to_string()],
        TermActionKey::HalfPageUp => vec!["PageUp".to_string()],
        TermActionKey::HalfPageDown => vec!["PageDown".to_string()],
    }
}

pub(crate) fn shell_quote(input: &str) -> String {
    if input.is_empty() {
        return "''".to_string();
    }
    if !input.bytes().any(|b| {
        matches!(
            b,
            b' ' | b'\t'
                | b'\n'
                | b'\''
                | b'"'
                | b'\\'
                | b'$'
                | b'`'
                | b'!'
                | b'&'
                | b'|'
                | b';'
                | b'<'
                | b'>'
                | b'('
                | b')'
                | b'['
                | b']'
                | b'{'
                | b'}'
                | b'*'
                | b'?'
                | b'#'
        )
    }) {
        return input.to_string();
    }
    format!("'{}'", input.replace('\'', "'\"'\"'"))
}

pub(crate) async fn handle_tui_keys(
    backend: &Arc<Mutex<Box<dyn SessionBackend>>>,
    analyzer_runtime: &Arc<RwLock<AnalyzerRuntime>>,
    keys: &[String],
    is_final: bool,
) -> Result<()> {
    if keys.is_empty() {
        return Ok(());
    }
    let guard = backend.lock().await;
    for key in keys {
        guard.send_special_keys(std::slice::from_ref(key)).await?;
        tokio::time::sleep(Duration::from_millis(100)).await;
    }
    drop(guard);
    if is_final {
        analyzer_runtime.write().await.prompt_active = false;
    }
    Ok(())
}

pub(crate) async fn handle_tui_text_input(
    backend: &Arc<Mutex<Box<dyn SessionBackend>>>,
    adapter: &Arc<Mutex<CliAdapter>>,
    analyzer_runtime: &Arc<RwLock<AnalyzerRuntime>>,
    keys: &[String],
    text: &str,
) -> Result<()> {
    let nav_keys = if keys.last().map(String::as_str) == Some("Enter") {
        &keys[..keys.len().saturating_sub(1)]
    } else {
        keys
    };
    if !nav_keys.is_empty() {
        let guard = backend.lock().await;
        for key in nav_keys {
            guard.send_special_keys(std::slice::from_ref(key)).await?;
            tokio::time::sleep(Duration::from_millis(100)).await;
        }
    }
    analyzer_runtime.write().await.prompt_active = false;
    tokio::time::sleep(Duration::from_millis(200)).await;
    let guard = backend.lock().await;
    let _ = adapter
        .lock()
        .await
        .write_input(guard.as_ref(), text)
        .await?;
    Ok(())
}

pub(crate) async fn handle_tui_prompt_override(
    stdout: &Arc<Mutex<tokio::io::Stdout>>,
    analyzer_runtime: &Arc<RwLock<AnalyzerRuntime>>,
) {
    let was_active = {
        let mut runtime = analyzer_runtime.write().await;
        if runtime.prompt_active {
            runtime.prompt_active = false;
            true
        } else {
            false
        }
    };
    if was_active {
        let _ = send_message(
            stdout,
            &WorkerToDaemon::TuiPromptResolved {
                selected_text: Some("user-override".to_string()),
            },
        )
        .await;
    }
}