supercode-frontend-tui 0.4.4

Attachable terminal frontend primitives for Supercode SDK runtimes.
Documentation
//! Crossterm input adapter over the donor-neutral composer model.

use std::ops::{Deref, DerefMut};

use crossterm::event::{KeyCode, KeyEvent};
use supercode::frontend::FrontendRuntimeDescriptor;
use supercode_frontend_model::{
    ComposerAction, ComposerInputEvent, ComposerKey, ComposerKeyCode,
    ComposerModel as NeutralComposerModel,
};

use crate::terminal::event_stream::TerminalEvent;

#[derive(Clone, Debug)]
pub struct ComposerModel(NeutralComposerModel);

impl ComposerModel {
    pub fn new(descriptor: &FrontendRuntimeDescriptor) -> Self {
        Self(NeutralComposerModel::new(descriptor))
    }

    pub fn handle_terminal_event(&mut self, event: TerminalEvent) -> Option<ComposerAction> {
        self.0.handle_input(map_terminal_event(event))
    }

    pub fn handle_key(&mut self, key: KeyEvent) -> Option<ComposerAction> {
        self.0.handle_key(map_key(key))
    }
}

impl Deref for ComposerModel {
    type Target = NeutralComposerModel;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for ComposerModel {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

fn map_terminal_event(event: TerminalEvent) -> ComposerInputEvent {
    match event {
        TerminalEvent::Key(key) => ComposerInputEvent::Key(map_key(key)),
        TerminalEvent::Paste(text) => ComposerInputEvent::Paste(text),
        TerminalEvent::Resize { width, height } => ComposerInputEvent::Resize { width, height },
        TerminalEvent::FocusChanged(focused) => ComposerInputEvent::FocusChanged(focused),
    }
}

fn map_key(key: KeyEvent) -> ComposerKey {
    let code = match key.code {
        KeyCode::Char(character) => ComposerKeyCode::Char(character),
        KeyCode::Enter => ComposerKeyCode::Enter,
        KeyCode::Backspace => ComposerKeyCode::Backspace,
        KeyCode::Delete => ComposerKeyCode::Delete,
        KeyCode::Left => ComposerKeyCode::Left,
        KeyCode::Right => ComposerKeyCode::Right,
        KeyCode::Home => ComposerKeyCode::Home,
        KeyCode::End => ComposerKeyCode::End,
        KeyCode::Up => ComposerKeyCode::Up,
        KeyCode::Down => ComposerKeyCode::Down,
        KeyCode::Esc => ComposerKeyCode::Escape,
        _ => ComposerKeyCode::Other,
    };
    ComposerKey {
        code,
        control: key
            .modifiers
            .contains(crossterm::event::KeyModifiers::CONTROL),
        shift: key
            .modifiers
            .contains(crossterm::event::KeyModifiers::SHIFT),
        alt: key.modifiers.contains(crossterm::event::KeyModifiers::ALT),
        release: key.kind == crossterm::event::KeyEventKind::Release,
    }
}