magi-code 0.78.0

Repository-aware CLI coding agent for terminal work
Documentation
//! Kitty APC commands are written only by the terminal owner, never widgets.
use super::super::images::{ImagePlacement, PreparedImage};
use std::{
    cell::RefCell,
    io::{self, IsTerminal, Write},
    sync::Arc,
};

const FIRST_IMAGE_ID: usize = 1_900_000_000;
const IMAGE_SLOTS: usize = 16;
static GRAPHICS_ACTIVE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
thread_local! {
    static UPLOADED: RefCell<Vec<Arc<PreparedImage>>> = const { RefCell::new(Vec::new()) };
}

pub(in crate::tui) fn detect_cell_pixels() -> Option<(u16, u16)> {
    let term = std::env::var("TERM").unwrap_or_default();
    let program = std::env::var("TERM_PROGRAM").unwrap_or_default();
    if !io::stdout().is_terminal()
        || !io::stdin().is_terminal()
        || !supported_terminal(
            &term,
            &program,
            std::env::var_os("TMUX").is_some(),
            std::env::var_os("STY").is_some(),
        )
    {
        return None;
    }
    current_cell_pixels()
}

pub(in crate::tui) fn current_cell_pixels() -> Option<(u16, u16)> {
    // ioctl, not a stdin query. Missing pixel dimensions fail closed.
    let size = crossterm::terminal::window_size().ok()?;
    let width = size.width.checked_div(size.columns)?;
    let height = size.height.checked_div(size.rows)?;
    (width > 0 && height > 0).then_some((width, height))
}

fn supported_terminal(term: &str, program: &str, tmux: bool, screen: bool) -> bool {
    !tmux && !screen && (term == "xterm-kitty" || program == "ghostty")
}

pub(in crate::tui) fn clear_placements() -> io::Result<()> {
    UPLOADED.with_borrow(|images| {
        let mut out = io::stdout().lock();
        for index in 0..images.len() {
            write!(out, "\x1b_Ga=d,d=i,i={},q=2;\x1b\\", FIRST_IMAGE_ID + index)?;
        }
        out.flush()
    })
}

pub(in crate::tui) fn draw_images(placements: &[ImagePlacement]) -> io::Result<()> {
    if placements.is_empty() {
        return Ok(());
    }
    GRAPHICS_ACTIVE.store(true, std::sync::atomic::Ordering::Relaxed);
    UPLOADED.with_borrow_mut(|images| {
        let mut out = io::stdout().lock();
        crossterm::queue!(out, crossterm::cursor::SavePosition)?;
        for (placement_index, placement) in placements.iter().enumerate() {
            let slot = if let Some(index) = images
                .iter()
                .position(|image| Arc::ptr_eq(image, &placement.image))
            {
                index
            } else {
                // At most 16 distinct images can be visible in one frame. If all
                // slots are still visible, retain text for further images.
                let index = if images.len() < IMAGE_SLOTS {
                    images.len()
                } else {
                    let Some(index) = images
                        .iter()
                        .position(|image| !placements.iter().any(|p| Arc::ptr_eq(image, &p.image)))
                    else {
                        continue;
                    };
                    write!(out, "\x1b_Ga=d,d=I,i={},q=2;\x1b\\", FIRST_IMAGE_ID + index)?;
                    index
                };
                upload(&mut out, FIRST_IMAGE_ID + index, &placement.image)?;
                if index == images.len() {
                    images.push(Arc::clone(&placement.image));
                } else {
                    images[index] = Arc::clone(&placement.image);
                }
                index
            };
            place(
                &mut out,
                FIRST_IMAGE_ID + slot,
                placement_index + 1,
                placement,
            )?;
        }
        crossterm::queue!(out, crossterm::cursor::RestorePosition)?;
        out.flush()
    })
}

fn upload(out: &mut impl Write, id: usize, image: &PreparedImage) -> io::Result<()> {
    let mut chunks = image.encoded.as_bytes().chunks(4096).peekable();
    let mut first = true;
    while let Some(chunk) = chunks.next() {
        if first {
            write!(
                out,
                "\x1b_Ga=t,t=d,f=32,i={id},s={},v={},q=2,m={};",
                image.width,
                image.height,
                usize::from(chunks.peek().is_some())
            )?;
            first = false;
        } else {
            write!(out, "\x1b_Gq=2,m={};", usize::from(chunks.peek().is_some()))?;
        }
        out.write_all(chunk)?;
        out.write_all(b"\x1b\\")?;
    }
    Ok(())
}

fn place(
    out: &mut impl Write,
    id: usize,
    placement_id: usize,
    placement: &ImagePlacement,
) -> io::Result<()> {
    crossterm::queue!(
        out,
        crossterm::cursor::MoveTo(placement.area.x, placement.area.y)
    )?;
    write!(
        out,
        "\x1b_Ga=p,i={id},p={placement_id},q=2,C=1,c={},r=1,x=0,y={},w={},h={};\x1b\\",
        placement.area.width, placement.source_y, placement.image.width, placement.source_height
    )
}

fn delete_image_data(out: &mut impl Write) {
    for index in 0..IMAGE_SLOTS {
        let _ = write!(out, "\x1b_Ga=d,d=I,i={},q=2;\x1b\\", FIRST_IMAGE_ID + index);
    }
}

pub(in crate::tui) fn cleanup() {
    // Do not borrow thread-local caches here: a panic may occur during a borrow.
    // Delete only our fixed namespace, including partially uploaded images.
    if !GRAPHICS_ACTIVE.swap(false, std::sync::atomic::Ordering::Relaxed) {
        return;
    }
    let mut out = io::stdout().lock();
    delete_image_data(&mut out);
    let _ = out.flush();
    let _ = UPLOADED.try_with(|images| {
        if let Ok(mut images) = images.try_borrow_mut() {
            images.clear();
        }
    });
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn detection_rejects_multiplexers_and_unknown_terminals() {
        assert!(supported_terminal("xterm-kitty", "", false, false));
        assert!(supported_terminal(
            "xterm-256color",
            "ghostty",
            false,
            false
        ));
        assert!(!supported_terminal("xterm-kitty", "", true, false));
        assert!(!supported_terminal("xterm-kitty", "", false, true));
        assert!(!supported_terminal(
            "xterm-256color",
            "iTerm.app",
            false,
            false
        ));
    }
    #[test]
    fn upload_chunks_are_bounded_and_do_not_request_terminal_file_access() {
        let image = PreparedImage {
            width: 20,
            height: 100,
            encoded: "A".repeat(10_668),
        };
        let mut out = Vec::new();
        upload(&mut out, FIRST_IMAGE_ID, &image).unwrap();
        let text = String::from_utf8(out).unwrap();
        assert!(text.starts_with("\x1b_Ga=t,t=d,f=32,"));
        let commands: Vec<_> = text.split("\x1b\\").filter(|s| !s.is_empty()).collect();
        assert_eq!(commands.len(), 3);
        for command in &commands {
            assert!(command.split_once(';').unwrap().1.len() <= 4096);
        }
        assert!(commands.last().unwrap().starts_with("\x1b_Gq=2,m=0;"));
    }
    #[test]
    fn scrolled_row_uses_source_crop_and_never_moves_cursor() {
        let placement = ImagePlacement {
            image: Arc::new(PreparedImage {
                width: 100,
                height: 80,
                encoded: String::new(),
            }),
            area: ratatui::layout::Rect::new(2, 3, 20, 1),
            source_y: 30,
            source_height: 10,
        };
        let mut out = Vec::new();
        place(&mut out, 1, 4, &placement).unwrap();
        assert_eq!(
            String::from_utf8(out).unwrap(),
            "\x1b[4;3H\x1b_Ga=p,i=1,p=4,q=2,C=1,c=20,r=1,x=0,y=30,w=100,h=10;\x1b\\"
        );
    }
}

#[cfg(test)]
#[test]
fn image_cleanup_deletes_owned_ids_without_clearing_other_terminal_images() {
    let mut output = Vec::new();
    delete_image_data(&mut output);
    let text = String::from_utf8(output).unwrap();
    let commands: Vec<_> = text
        .split("\x1b\\")
        .filter(|command| !command.is_empty())
        .collect();
    assert_eq!(commands.len(), IMAGE_SLOTS);
    for (index, command) in commands.iter().enumerate() {
        assert_eq!(
            *command,
            format!("\x1b_Ga=d,d=I,i={},q=2;", FIRST_IMAGE_ID + index)
        );
    }
}