use std::io::Write;
use crate::render::TokenRenderer;
use crate::viz::RenderSink;
const ANSI_RESET: &str = "\x1b[0m";
#[derive(Debug)]
pub struct TerminalSink<W: Write> {
renderer: TokenRenderer<W>,
}
impl<W: Write> TerminalSink<W> {
pub fn new(renderer: TokenRenderer<W>) -> Self {
Self { renderer }
}
pub fn renderer_mut(&mut self) -> &mut TokenRenderer<W> {
&mut self.renderer
}
pub fn into_renderer(self) -> TokenRenderer<W> {
self.renderer
}
}
impl<W: Write> RenderSink for TerminalSink<W> {
fn visible_text(&mut self, text: &str) {
self.renderer.set_in_think(false);
self.renderer.write(text);
}
fn think_text(&mut self, text: &str) {
self.renderer.set_in_think(true);
self.renderer.write(text);
}
fn tool_text(&mut self, text: &str) {
self.renderer.set_in_think(false);
self.renderer.plain(text);
}
fn error_text(&mut self, text: &str) {
self.renderer.set_in_think(false);
self.renderer.color("\x1b[1;31m");
self.renderer.plain(text);
self.renderer.color(ANSI_RESET);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::render::RenderOptions;
#[test]
fn tool_text_is_not_markdown_processed() {
let opts = RenderOptions {
use_color: false,
format_thinking: true,
format_markdown: true,
};
let mut sink = TerminalSink::new(TokenRenderer::new(Vec::new(), opts));
sink.tool_text("pattern=**/mod.rs");
let out = String::from_utf8(sink.into_renderer().into_sink()).unwrap();
assert!(
out.contains("**/mod.rs"),
"tool banners must render verbatim, got {out:?}"
);
}
}