From 2c6b7e66cb5511162fe5760adc5d0f9f5042c95c Mon Sep 17 00:00:00 2001 From: csh <458761603@qq.com> Date: Wed, 22 Apr 2026 23:54:44 +0800 Subject: [PATCH 1/6] feat: add PNG format support for screen rendering - Rename render_screen_to_jpeg to render_screen_to_image - Add format parameter to support both JPEG and PNG output - Add Copy trait to ImageFormat enum - Use PNG as default format for scroll operations - PNG preserves transparency (RGBA), JPEG uses RGB only Co-Authored-By: Claude Opus 4.5 --- src/protocol.rs | 2 +- src/ws.rs | 138 ++++++++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/protocol.rs b/src/protocol.rs index 3464d66..2cc55a4 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -144,7 +144,7 @@ pub struct NotificationData { // ========== 辅助类型 ========== /// 图片格式 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ImageFormat { Png, diff --git a/src/ws.rs b/src/ws.rs index 9a26a08..fbe1a1f 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -7,6 +7,7 @@ use axum::{ }, response::IntoResponse, }; +use image::ImageEncoder; use tokio::sync::{broadcast, mpsc}; use vt100::Callbacks; @@ -152,19 +153,6 @@ impl ASRInterface { } fn send_screen(tx: &ServerTx, screen: Arc) { - // if let Ok(jpeg) = render_screen_to_jpeg(&screen) { - // let chunk_size = IMAGE_CHUNK_SIZE; - // let total_chunks = jpeg.len().div_ceil(chunk_size); - // for (i, chunk) in jpeg.chunks(chunk_size).enumerate() { - // let is_last = i == total_chunks - 1; - // let _ = tx.send(ServerMessage::screen_image_chunk( - // crate::protocol::ImageFormat::Jpeg, - // is_last, - // chunk.to_vec(), - // )); - // } - // } - let _ = tx.send(ServerMessage::Screen(screen)); } @@ -283,7 +271,12 @@ pub async fn run_command( TerminalEvent::ScreenGetter(getter) => { let screen = vt_parser.screen().clone(); let mut window_scrollback = 0; - let result = render_screen_to_jpeg(&screen, None, &mut window_scrollback); + let result = render_screen_to_image( + &screen, + None, + &mut window_scrollback, + crate::protocol::ImageFormat::Jpeg, + ); let jpeg = match result { Ok(data) => Ok(data), @@ -512,33 +505,31 @@ async fn send_screen_to_client( screen: &vt100::Screen, window_size: Option<(u16, u16)>, // (width, height) window_h_offset: &mut u16, + format: crate::protocol::ImageFormat, ) -> anyhow::Result<()> { - let jpeg = render_screen_to_jpeg(screen, window_size, window_h_offset)?; - if jpeg.is_empty() { + let image_data = render_screen_to_image(screen, window_size, window_h_offset, format)?; + if image_data.is_empty() { state .cli_tx .send(ClientMessage::ScrollDown) .await .map_err(|e| { anyhow::anyhow!( - "Failed to send ScrollDown message to cli_tx after empty JPEG: {}", + "Failed to send ScrollDown message to cli_tx after empty image: {}", e ) })?; } log::debug!( - "Sending screen JPEG to client, size: {} KB", - jpeg.len() / 1024 + "Sending screen image to client, format: {:?}, size: {} KB", + format, + image_data.len() / 1024 ); let chunk_size = IMAGE_CHUNK_SIZE; - let total_chunks = jpeg.len().div_ceil(chunk_size); - for (i, chunk) in jpeg.chunks(chunk_size).enumerate() { + let total_chunks = image_data.len().div_ceil(chunk_size); + for (i, chunk) in image_data.chunks(chunk_size).enumerate() { let is_last = i == total_chunks - 1; - let msg = ServerMessage::screen_image_chunk( - crate::protocol::ImageFormat::Jpeg, - is_last, - chunk.to_vec(), - ); + let msg = ServerMessage::screen_image_chunk(format, is_last, chunk.to_vec()); let data = msg.to_msgpack()?; socket.send(Message::Binary(data.into())).await?; } @@ -579,6 +570,7 @@ async fn handle_client_message( screen, Some((width, height)), window_h_offset, + crate::protocol::ImageFormat::Png, ) .await?; } else { @@ -601,6 +593,7 @@ async fn handle_client_message( screen, Some((width, height)), window_h_offset, + crate::protocol::ImageFormat::Png, ) .await?; } else { @@ -670,7 +663,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState, params: WsParams) }, ServerMessage::Screen(screen_) => { screen = Some(screen_.clone()); - if let Err(e) = send_screen_to_client(&state, &mut socket, screen_, Some(window_size), &mut window_h_offset).await { + if let Err(e) = send_screen_to_client(&state, &mut socket, screen_, Some(window_size), &mut window_h_offset, crate::protocol::ImageFormat::Png).await { log::error!("Failed to send screen to client: {}", e); } continue; @@ -867,11 +860,12 @@ async fn handle_web_vosk_socket(mut socket: WebSocket, vosk_tx: WebVoskTx) { } } -/// Render a vt100 screen to JPEG bytes -fn render_screen_to_jpeg( +/// Render a vt100 screen to image bytes (JPEG or PNG) +fn render_screen_to_image( screen: &vt100::Screen, window_size: Option<(u16, u16)>, // (width, height) window_h_offset: &mut u16, + format: crate::protocol::ImageFormat, ) -> anyhow::Result> { let config = crate::screenshot::ScreenshotConfig { show_decorations: false, @@ -881,12 +875,12 @@ fn render_screen_to_jpeg( let image = crate::screenshot::capture_screen(screen, &config) .map_err(|e| anyhow::anyhow!("Failed to capture screen: {}", e))?; - // Convert RGBA to RGB for JPEG - let mut rgb_image = image::DynamicImage::ImageRgba8(image).to_rgb8(); + let mut dyn_image = image::DynamicImage::ImageRgba8(image); + let is_png = matches!(format, crate::protocol::ImageFormat::Png); if let Some((width, height)) = window_size { - let orig_width = rgb_image.width(); - let orig_height = rgb_image.height(); + let orig_width = dyn_image.width(); + let orig_height = dyn_image.height(); log::debug!( "Original image size: {}x{}, requested window size: {}x{}", orig_width, @@ -897,12 +891,12 @@ fn render_screen_to_jpeg( let scale = width as f32 / orig_width as f32; let new_height = (orig_height as f32 * scale).round() as u32; - rgb_image = image::imageops::resize( - &rgb_image, + dyn_image = image::DynamicImage::ImageRgba8(image::imageops::resize( + &dyn_image, width as u32, new_height, image::imageops::FilterType::Lanczos3, - ); + )); // 根据垂直偏移截取 let crop_height = height as u32; @@ -921,14 +915,22 @@ fn render_screen_to_jpeg( crop_height - new_height ); let padding_top = crop_height - new_height; - let mut padded = image::RgbImage::new(width as u32, crop_height); - // 填充背景色(深色背景) - for pixel in padded.pixels_mut() { - *pixel = image::Rgb([30, 30, 30]); + + if is_png { + let mut padded = image::RgbaImage::new(width as u32, crop_height); + for pixel in padded.pixels_mut() { + *pixel = image::Rgba([30, 30, 30, 255]); + } + image::imageops::overlay(&mut padded, &dyn_image.to_rgba8(), 0, padding_top as i64); + dyn_image = image::DynamicImage::ImageRgba8(padded); + } else { + let mut padded = image::RgbImage::new(width as u32, crop_height); + for pixel in padded.pixels_mut() { + *pixel = image::Rgb([30, 30, 30]); + } + image::imageops::overlay(&mut padded, &dyn_image.to_rgb8(), 0, padding_top as i64); + dyn_image = image::DynamicImage::ImageRgb8(padded); } - // 把原图放在底部 - image::imageops::overlay(&mut padded, &rgb_image, 0, padding_top as i64); - rgb_image = padded; } else if y_offset + crop_height > new_height { // 偏移量过大,调整到对齐底部 log::warn!( @@ -941,24 +943,46 @@ fn render_screen_to_jpeg( return Ok(Vec::new()); } else { // 正常截取 - rgb_image = - image::imageops::crop(&mut rgb_image, 0, y_offset, width as u32, crop_height) - .to_image(); + dyn_image = + image::imageops::crop(&mut dyn_image, 0, y_offset, width as u32, crop_height) + .to_image() + .into(); } } - let mut jpeg_buf = std::io::Cursor::new(Vec::new()); - let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut jpeg_buf, 100); - encoder - .encode( - rgb_image.as_raw(), - rgb_image.width(), - rgb_image.height(), - image::ExtendedColorType::Rgb8, - ) - .map_err(|e| anyhow::anyhow!("Failed to encode JPEG: {}", e))?; + // Encode based on format + let mut buf = std::io::Cursor::new(Vec::new()); + match format { + crate::protocol::ImageFormat::Png => { + let encoder = image::codecs::png::PngEncoder::new(&mut buf); + encoder + .write_image( + dyn_image.as_bytes(), + dyn_image.width(), + dyn_image.height(), + image::ExtendedColorType::from(dyn_image.color()), + ) + .map_err(|e| anyhow::anyhow!("Failed to encode PNG: {}", e))?; + } + crate::protocol::ImageFormat::Jpeg => { + // Convert to RGB for JPEG + let rgb_image = dyn_image.to_rgb8(); + let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 100); + encoder + .encode( + rgb_image.as_raw(), + rgb_image.width(), + rgb_image.height(), + image::ExtendedColorType::Rgb8, + ) + .map_err(|e| anyhow::anyhow!("Failed to encode JPEG: {}", e))?; + } + _ => { + return Err(anyhow::anyhow!("Unsupported image format: {:?}", format)); + } + } - Ok(jpeg_buf.into_inner()) + Ok(buf.into_inner()) } /// HTTP handler for /screenshot.jpeg From 991f7b01982cf7a9eae40fad5b7efbb2ffcdf825 Mon Sep 17 00:00:00 2001 From: zzz <458761603@qq.com> Date: Thu, 23 Apr 2026 00:32:06 +0000 Subject: [PATCH 2/6] refactor: replace inline screenshot module with vibetty-screenshot crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the bundled src/screenshot/ module and depend on the standalone vibetty-screenshot library (v0.1.0) from GitHub instead. The API is unchanged — all existing callers in ws.rs work without modification. Co-Authored-By: Claude Opus 4.6 --- Cargo.toml | 6 +- src/main.rs | 2 +- src/screenshot/canvas.rs | 156 -------------------------------- src/screenshot/font.rs | 33 ------- src/screenshot/mod.rs | 189 --------------------------------------- src/screenshot/theme.rs | 96 -------------------- src/screenshot/utils.rs | 21 ----- 7 files changed, 2 insertions(+), 501 deletions(-) delete mode 100644 src/screenshot/canvas.rs delete mode 100644 src/screenshot/font.rs delete mode 100644 src/screenshot/mod.rs delete mode 100644 src/screenshot/theme.rs delete mode 100644 src/screenshot/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 86ce7b3..8c989c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,8 +43,4 @@ tui-term = "0.3" vt100 = "0.16" # Screenshot / Image rendering -ab_glyph = "0.2" -image = "0.25" -imageproc = "0.26" -tiny-skia = "0.12" -thiserror = "2.0" +vibetty-screenshot = { git = "https://github.com/second-state/vibetty-screenshot", tag = "v0.1.0" } diff --git a/src/main.rs b/src/main.rs index 9cf4465..3269267 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ mod terminal; mod ui; -pub mod screenshot; +pub use vibetty_screenshot as screenshot; use config::{Args, AsrConfig}; diff --git a/src/screenshot/canvas.rs b/src/screenshot/canvas.rs deleted file mode 100644 index c2b4c29..0000000 --- a/src/screenshot/canvas.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Canvas for rendering terminal content to images - -use ab_glyph::{Font, PxScale}; -use image::{ImageBuffer, Rgba, RgbaImage}; -use imageproc::drawing::draw_text_mut; -use tiny_skia::{Color, Paint, Pixmap, Rect, Transform}; - -/// Canvas for drawing shapes and text -pub struct Canvas { - background: Pixmap, - text_layer: ImageBuffer, Vec>, - char_width: u32, - char_height: u32, -} - -impl Canvas { - /// Create a new canvas with the given dimensions - pub fn new(width: u32, height: u32) -> Result { - let background = - Pixmap::new(width, height).ok_or_else(|| "Failed to create pixmap".to_string())?; - - let text_layer = ImageBuffer::new(width, height); - - Ok(Self { - background, - text_layer, - char_width: 8, - char_height: 16, - }) - } - - /// Set character size for text rendering - pub fn set_char_size(&mut self, width: u32, height: u32) { - self.char_width = width; - self.char_height = height; - } - - /// Get character width - #[allow(dead_code)] - pub fn char_width(&self) -> u32 { - self.char_width - } - - /// Get character height - #[allow(dead_code)] - pub fn char_height(&self) -> u32 { - self.char_height - } - - /// Fill the entire canvas with a color - pub fn fill(&mut self, color: [u8; 4]) { - let color = Color::from_rgba8(color[0], color[1], color[2], color[3]); - self.background.fill(color); - } - - /// Fill a rectangle with a color - pub fn fill_rect(&mut self, x: i32, y: i32, width: u32, height: u32, color: [u8; 4]) { - if let Some(rect) = Rect::from_xywh(x as f32, y as f32, width as f32, height as f32) { - let mut paint = Paint::default(); - paint.set_color(Color::from_rgba8(color[0], color[1], color[2], color[3])); - self.background - .fill_rect(rect, &paint, Transform::identity(), None); - } - } - - /// Draw a title bar background at the top - pub fn draw_title_bar(&mut self, _title: &str, _padding: u32) { - let height = 32; - let bg = [40, 40, 45, 255]; - self.fill_rect(0, 0, self.width(), height, bg); - self.fill_rect(0, height as i32 - 2, self.width(), 2, [60, 60, 65, 255]); - } - - /// Draw text at the specified position (simple placeholder) - #[allow(dead_code)] - pub fn draw_text( - &mut self, - text: &str, - x: i32, - y: i32, - color: [u8; 4], - _font: Option<&()>, - _font_size: f32, - ) { - self.draw_text_simple(text, x, y, color); - } - - /// Draw text at the specified position (simple version without font) - #[allow(dead_code)] - pub fn draw_text_simple(&mut self, text: &str, x: i32, y: i32, color: [u8; 4]) { - for (i, ch) in text.chars().enumerate() { - let px_x = x + i as i32 * 8; - let px_y = y; - if !ch.is_whitespace() { - self.fill_rect(px_x, px_y, 6, 10, color); - } - } - } - - /// Draw text using ab_glyph font - pub fn draw_text_with_font( - &mut self, - text: &str, - x: i32, - y: i32, - color: [u8; 4], - font: &F, - scale: PxScale, - ) { - let rgba = Rgba(color); - draw_text_mut(&mut self.text_layer, rgba, x, y, scale, font, text); - } - - /// Get the canvas width - pub fn width(&self) -> u32 { - self.background.width() - } - - /// Get the canvas height - #[allow(dead_code)] - pub fn height(&self) -> u32 { - self.background.height() - } - - /// Convert the canvas to a final image - pub fn into_image(self) -> Result { - let mut final_image = RgbaImage::from_raw( - self.background.width(), - self.background.height(), - self.background.data().to_vec(), - ) - .ok_or_else(|| "Failed to create image from raw data".to_string())?; - - // Blend text layer on top (fast integer alpha blending) - for (dst, src) in final_image.pixels_mut().zip(self.text_layer.pixels()) { - let a = src[3] as u32; - if a == 0 { - continue; - } - if a == 255 { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = 255; - } else { - let inv_a = 255 - a; - dst[0] = ((src[0] as u32 * a + dst[0] as u32 * inv_a + 128) >> 8) as u8; - dst[1] = ((src[1] as u32 * a + dst[1] as u32 * inv_a + 128) >> 8) as u8; - dst[2] = ((src[2] as u32 * a + dst[2] as u32 * inv_a + 128) >> 8) as u8; - dst[3] = 255; - } - } - - Ok(final_image) - } -} diff --git a/src/screenshot/font.rs b/src/screenshot/font.rs deleted file mode 100644 index d00238c..0000000 --- a/src/screenshot/font.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Font loading utilities - -use ab_glyph::{FontArc, PxScale}; -use std::sync::LazyLock; - -/// Global cached font, parsed only once -static FONT: LazyLock = LazyLock::new(|| { - FontArc::try_from_slice(include_bytes!("../../assets/SarasaMonoSC-Light.ttf")) - .expect("Embedded font is valid") -}); - -/// Font data container with ab_glyph FontArc -pub struct FontData { - /// Regular font - pub font: FontArc, - /// Font scale for rendering - pub scale: PxScale, -} - -impl FontData { - /// Create a new FontData with the given font size - pub fn new(font_size: f32) -> Self { - Self { - font: FONT.clone(), - scale: PxScale::from(font_size), - } - } -} - -/// Load font with specific size -pub fn load_font_with_size(size: f32) -> Result { - Ok(FontData::new(size)) -} diff --git a/src/screenshot/mod.rs b/src/screenshot/mod.rs deleted file mode 100644 index 09a30b6..0000000 --- a/src/screenshot/mod.rs +++ /dev/null @@ -1,189 +0,0 @@ -//! Terminal screenshot functionality -//! -//! Renders a vt100::Screen to an image file. - -mod canvas; -mod font; -mod theme; -mod utils; - -pub use canvas::Canvas; -pub use font::{FontData, load_font_with_size}; -pub use theme::Theme; - -use ab_glyph::Font; -use image::ImageError; -use thiserror::Error; - -#[derive(Error, Debug)] -#[allow(clippy::enum_variant_names)] -pub enum ScreenshotError { - #[error("Failed to load font: {0}")] - #[allow(dead_code)] - FontLoadError(String), - - #[error("Canvas error: {0}")] - CanvasError(String), - - #[error("Image error: {0}")] - ImageError(#[from] ImageError), - - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), -} - -/// Configuration for screenshot generation -pub struct ScreenshotConfig { - /// Font size in points - pub font_size: f32, - - /// Padding around the content (in pixels) - pub padding: u32, - - /// Background color (R, G, B, A) - pub background_color: [u8; 4], - - /// Whether to show window decorations - pub show_decorations: bool, - - /// Window title - pub title: Option, -} - -impl Default for ScreenshotConfig { - fn default() -> Self { - Self { - font_size: 14.0, - padding: 16, - background_color: [30, 30, 30, 255], - show_decorations: true, - title: None, - } - } -} - -/// Capture a vt100::Screen as an image -pub fn capture_screen( - screen: &vt100::Screen, - config: &ScreenshotConfig, -) -> Result { - // Try to load font, fallback to built-in if not available - let font_data = - load_font_with_size(config.font_size).unwrap_or_else(|_| FontData::new(config.font_size)); - - let theme = Theme::default(); - - // Calculate character dimensions from the font - let units_per_em = font_data.font.units_per_em().unwrap_or(2048.0); - let space_id = font_data.font.glyph_id(' '); - let advance_unscaled = font_data.font.h_advance_unscaled(space_id); - let char_width = ((advance_unscaled / units_per_em) * font_data.scale.x).round() as u32; - - // Use proper line height = ascent + descent - let ascent = - (font_data.font.ascent_unscaled() / units_per_em * font_data.scale.y).round() as u32; - let descent = - (font_data.font.descent_unscaled() / units_per_em * font_data.scale.y).round() as u32; - let char_height = ascent + descent; - - let (rows, cols) = screen.size(); - let padding = config.padding; - let title_height = if config.show_decorations { 32 } else { 0 }; - - // Find the last row with actual content (skip trailing empty rows) - let mut last_content_row = 0; - 'b: for row in (0..rows).rev() { - for col in 0..cols { - if let Some(cell) = screen.cell(row, col) - && cell.has_contents() - && cell.contents() != " " - { - last_content_row = row; - break 'b; - } - } - } - - // Only render rows up to the last one with content - let actual_rows = last_content_row + 1; - - let image_width = cols as u32 * char_width + padding * 2; - let image_height = actual_rows as u32 * char_height + title_height + padding * 2; - - let mut canvas = Canvas::new(image_width, image_height) - .map_err(|e| ScreenshotError::CanvasError(e.to_string()))?; - - canvas.set_char_size(char_width, char_height); - - // Fill background - canvas.fill(config.background_color); - - // Draw title bar if decorations are enabled - if config.show_decorations { - let title = config.title.as_deref().unwrap_or("Terminal"); - canvas.draw_title_bar(title, config.padding); - - // Draw title with proper font - let title_x = (padding + 8) as i32; - let title_y = 10; - canvas.draw_text_with_font( - title, - title_x, - title_y, - [220, 220, 220, 255], - &font_data.font, - font_data.scale, - ); - } - - // Draw terminal content (only up to last_content_row) - for row in 0..actual_rows { - for col in 0..cols { - if let Some(cell) = screen.cell(row, col) { - let x = padding + col as u32 * char_width; - let y = title_height + padding + row as u32 * char_height; - - // Draw background color - let bg = cell.bgcolor(); - if bg != vt100::Color::Default { - let color = theme.color_to_rgba(bg); - let w = if cell.is_wide() { - char_width * 2 - } else { - char_width - }; - canvas.fill_rect(x as i32, y as i32, w, char_height, color); - } - - // Draw text - imageproc's draw_text_mut y param is the top of the text - if cell.has_contents() && !cell.is_wide_continuation() { - let fg = cell.fgcolor(); - let fg_color = theme.get_foreground(fg, cell.bold(), cell.dim()); - canvas.draw_text_with_font( - cell.contents(), - x as i32, - y as i32, - fg_color, - &font_data.font, - font_data.scale, - ); - } - } - } - } - - canvas - .into_image() - .map_err(|e| ScreenshotError::CanvasError(e.to_string())) -} - -/// Save a vt100::Screen to a PNG file -pub fn save_screen_png( - screen: &vt100::Screen, - path: &str, - config: &ScreenshotConfig, -) -> Result<(), ScreenshotError> { - let image = capture_screen(screen, config)?; - image.save(path)?; - Ok(()) -} diff --git a/src/screenshot/theme.rs b/src/screenshot/theme.rs deleted file mode 100644 index c7e7280..0000000 --- a/src/screenshot/theme.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! Color theme for terminal rendering - -/// Theme for terminal colors -#[derive(Clone, Debug)] -pub struct Theme { - /// Standard ANSI 16 colors - pub colors: [[u8; 4]; 16], -} - -impl Default for Theme { - fn default() -> Self { - Self { - colors: [ - // Normal colors - [0, 0, 0, 255], // Black - [194, 54, 33, 255], // Red - [37, 188, 36, 255], // Green - [173, 173, 39, 255], // Yellow - [73, 46, 225, 255], // Blue - [211, 56, 211, 255], // Magenta - [51, 187, 200, 255], // Cyan - [203, 204, 205, 255], // White - // Bright colors - [128, 128, 128, 255], // Bright Black (Gray) - [255, 85, 85, 255], // Bright Red - [85, 255, 85, 255], // Bright Green - [255, 255, 85, 255], // Bright Yellow - [85, 85, 255, 255], // Bright Blue - [255, 85, 255, 255], // Bright Magenta - [85, 255, 255, 255], // Bright Cyan - [255, 255, 255, 255], // Bright White - ], - } - } -} - -impl Theme { - /// Convert a vt100 Color to RGBA - pub fn color_to_rgba(&self, color: vt100::Color) -> [u8; 4] { - match color { - vt100::Color::Default => [229, 229, 229, 255], - vt100::Color::Idx(i) => { - if i < 16 { - self.colors[i as usize] - } else if i >= 232 { - // Grayscale - let shade = (i - 232) * 10 + 8; - [shade, shade, shade, 255] - } else { - // 216-color cube - let i = i - 16; - let r = (i / 36) * 51; - let g = ((i / 6) % 6) * 51; - let b = (i % 6) * 51; - [r, g, b, 255] - } - } - vt100::Color::Rgb(r, g, b) => [r, g, b, 255], - } - } - - /// Get foreground color with bold and dim attributes - pub fn get_foreground(&self, color: vt100::Color, bold: bool, dim: bool) -> [u8; 4] { - let rgba = if bold { - match color { - vt100::Color::Default => self.colors[15], // bright white - vt100::Color::Idx(i) if i < 8 => { - // Map normal color to bright variant (index + 8) - self.colors[i as usize + 8] - } - _ => self.color_to_rgba(color), - } - } else { - self.color_to_rgba(color) - }; - - if dim { - // Dim: blend 50% toward background (30,30,30) - let bg = [30u8, 30, 30]; - [ - (rgba[0] as u16 / 2 + bg[0] as u16 / 2) as u8, - (rgba[1] as u16 / 2 + bg[1] as u16 / 2) as u8, - (rgba[2] as u16 / 2 + bg[2] as u16 / 2) as u8, - rgba[3], - ] - } else { - rgba - } - } - - /// Get background color - #[allow(dead_code)] - pub fn get_background(&self, color: vt100::Color) -> [u8; 4] { - self.color_to_rgba(color) - } -} diff --git a/src/screenshot/utils.rs b/src/screenshot/utils.rs deleted file mode 100644 index e6656d2..0000000 --- a/src/screenshot/utils.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Utility functions for screenshot rendering - -/// Calculate character size based on font size -#[allow(dead_code)] -pub fn calculate_char_size(font_size: f32) -> (u32, u32) { - let width = (font_size * 0.6) as u32; - let height = (font_size * 1.2) as u32; - (width, height) -} - -/// Clamp a value between min and max -#[allow(dead_code)] -pub fn clamp(value: T, min: T, max: T) -> T { - if value < min { - min - } else if value > max { - max - } else { - value - } -} From a4fb297f64e871d951f8bc82642b429b86ccf914 Mon Sep 17 00:00:00 2001 From: zzz <458761603@qq.com> Date: Thu, 23 Apr 2026 00:59:07 +0000 Subject: [PATCH 3/6] feat: add --image-format CLI parameter (-f) Add configurable image format (png/jpeg) via CLI flag, defaulting to jpeg. Propagates through AppState to all rendering call sites: HTTP screenshot endpoint, WebSocket screen sync, and scroll operations. Route changed from /screenshot.jpeg to /screenshot. Co-Authored-By: Claude Opus 4.6 --- src/config.rs | 11 +++++++++++ src/main.rs | 4 +++- src/ws.rs | 10 ++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/config.rs b/src/config.rs index db242a0..bfe6096 100644 --- a/src/config.rs +++ b/src/config.rs @@ -39,9 +39,20 @@ pub struct Args { /// Command to execute on PTY start (e.g., -- bash -l) #[arg(last = true)] pub command: Vec, + + /// Image format for screen rendering (png or jpeg) + #[arg(short = 'f', long, default_value = "jpeg", value_name = "FORMAT")] + pub image_format: String, } impl Args { + pub fn image_format(&self) -> crate::protocol::ImageFormat { + match self.image_format.to_lowercase().as_str() { + "jpeg" | "jpg" => crate::protocol::ImageFormat::Jpeg, + _ => crate::protocol::ImageFormat::Png, + } + } + pub fn asr_config(&self) -> AsrConfig { // 如果指定了配置文件,从文件读取 if let Some(path) = &self.asr_config_path { diff --git a/src/main.rs b/src/main.rs index 3269267..99fd3c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -150,6 +150,7 @@ async fn main() { cli_tx, web_vosk_tx, screenshot_tx: screenshot_tx.clone(), + image_format: args.image_format(), }; let listener = tokio::net::TcpListener::bind(&args.bind_addr) @@ -165,7 +166,7 @@ async fn main() { .route("/setup", get(static_page::setup_handler)) .route("/vosk", get(static_page::vosk_handler)) .route("/ws", get(ws::ws_handler)) - .route("/screenshot.jpeg", get(ws::screenshot_handler)) + .route("/screenshot", get(ws::screenshot_handler)) .route("/api/change-dir", post(static_page::change_dir_handler)) .route("/vosk_ws", get(ws::web_vosk_ws_handler)) .nest_service( @@ -225,6 +226,7 @@ async fn main() { &mut tui, &mut ui_title, &server_url, + state.image_format, ) .await; match r { diff --git a/src/ws.rs b/src/ws.rs index fbe1a1f..c60ea0b 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -66,6 +66,7 @@ pub struct AppState { pub cli_tx: ClientTx, pub web_vosk_tx: Option, pub screenshot_tx: ScreenshotTx, + pub image_format: crate::protocol::ImageFormat, } fn t2s>(s: S) -> String { @@ -178,6 +179,7 @@ pub async fn run_command( tui: &mut crate::ui::TuiTerminal, ui_title: &mut String, ui_footer: &str, + image_format: crate::protocol::ImageFormat, ) -> anyhow::Result { let dir_path = current_dir .as_ref() @@ -275,7 +277,7 @@ pub async fn run_command( &screen, None, &mut window_scrollback, - crate::protocol::ImageFormat::Jpeg, + image_format, ); let jpeg = match result { @@ -570,7 +572,7 @@ async fn handle_client_message( screen, Some((width, height)), window_h_offset, - crate::protocol::ImageFormat::Png, + state.image_format, ) .await?; } else { @@ -593,7 +595,7 @@ async fn handle_client_message( screen, Some((width, height)), window_h_offset, - crate::protocol::ImageFormat::Png, + state.image_format, ) .await?; } else { @@ -663,7 +665,7 @@ async fn handle_socket(mut socket: WebSocket, state: AppState, params: WsParams) }, ServerMessage::Screen(screen_) => { screen = Some(screen_.clone()); - if let Err(e) = send_screen_to_client(&state, &mut socket, screen_, Some(window_size), &mut window_h_offset, crate::protocol::ImageFormat::Png).await { + if let Err(e) = send_screen_to_client(&state, &mut socket, screen_, Some(window_size), &mut window_h_offset, state.image_format).await { log::error!("Failed to send screen to client: {}", e); } continue; From 1886c1b761f814a842bbc7f1a6fe0eac3064c2df Mon Sep 17 00:00:00 2001 From: zzz <458761603@qq.com> Date: Thu, 23 Apr 2026 01:17:23 +0000 Subject: [PATCH 4/6] fix: add image dependency back, remove obsolete bin test, fix state move - Add image crate back to Cargo.toml (still used by ws.rs for resize/crop/encode) - Remove src/bin/font_render_test.rs (references deleted screenshot module) - Fix state move-after-use by extracting image_format before AppState construction Co-Authored-By: Claude Opus 4.6 --- Cargo.lock | 403 +++++------------------------------- Cargo.toml | 1 + src/bin/font_render_test.rs | 32 --- src/main.rs | 6 +- 4 files changed, 61 insertions(+), 381 deletions(-) delete mode 100644 src/bin/font_render_test.rs diff --git a/Cargo.lock b/Cargo.lock index 388a1ca..9190c84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,22 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ab_glyph" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" - [[package]] name = "adler2" version = "2.0.1" @@ -135,15 +119,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "arbitrary" version = "1.4.2" @@ -1075,6 +1050,28 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "freetype-rs" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d228d6de56c90dd7585341f341849441b3490180c62d27133e525eb726809b4" +dependencies = [ + "bitflags 2.11.0", + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab537ce43cab850c64b4cdc390ce7e4f47f877485ddc323208e268280c308ae" +dependencies = [ + "cc", + "libz-sys", + "pkg-config", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1201,114 +1198,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "glam" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" - -[[package]] -name = "glam" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" - -[[package]] -name = "glam" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" - -[[package]] -name = "glam" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" - -[[package]] -name = "glam" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" - -[[package]] -name = "glam" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" - -[[package]] -name = "glam" -version = "0.20.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" - -[[package]] -name = "glam" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" - -[[package]] -name = "glam" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" - -[[package]] -name = "glam" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" - -[[package]] -name = "glam" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" - -[[package]] -name = "glam" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" - -[[package]] -name = "glam" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" - -[[package]] -name = "glam" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" - -[[package]] -name = "glam" -version = "0.29.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" - -[[package]] -name = "glam" -version = "0.30.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" - -[[package]] -name = "glam" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556f6b2ea90b8d15a74e0e7bb41671c9bdf38cd9f78c284d750b9ce58a2b5be7" - -[[package]] -name = "glam" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0" - [[package]] name = "h2" version = "0.4.13" @@ -1691,25 +1580,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "imageproc" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8046da590889acc65f5880004580ebb269bbef84d6c0f5c543ec2dece46638" -dependencies = [ - "ab_glyph", - "approx", - "getrandom 0.3.4", - "image", - "itertools", - "nalgebra", - "num", - "rand 0.9.2", - "rand_distr", - "rayon", - "rustdct", -] - [[package]] name = "imgref" version = "1.12.0" @@ -1916,10 +1786,16 @@ dependencies = [ ] [[package]] -name = "libm" -version = "0.2.16" +name = "libz-sys" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] [[package]] name = "line-clipping" @@ -2009,16 +1885,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" -[[package]] -name = "matrixmultiply" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" -dependencies = [ - "autocfg", - "rawpointer", -] - [[package]] name = "maybe-rayon" version = "0.1.1" @@ -2104,39 +1970,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "nalgebra" -version = "0.34.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df76ea0ff5c7e6b88689085804d6132ded0ddb9de5ca5b8aeb9eeadc0508a70a" -dependencies = [ - "approx", - "glam 0.14.0", - "glam 0.15.2", - "glam 0.16.0", - "glam 0.17.3", - "glam 0.18.0", - "glam 0.19.0", - "glam 0.20.5", - "glam 0.21.3", - "glam 0.22.0", - "glam 0.23.0", - "glam 0.24.2", - "glam 0.25.0", - "glam 0.27.0", - "glam 0.28.0", - "glam 0.29.3", - "glam 0.30.10", - "glam 0.31.1", - "glam 0.32.1", - "matrixmultiply", - "num-complex", - "num-rational", - "num-traits", - "simba", - "typenum", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2190,19 +2023,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -2213,15 +2033,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-conv" version = "0.2.0" @@ -2248,17 +2059,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-rational" version = "0.4.2" @@ -2277,7 +2077,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -2316,15 +2115,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "owned_ttf_parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" -dependencies = [ - "ttf-parser", -] - [[package]] name = "parking_lot" version = "0.12.5" @@ -2473,6 +2263,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + [[package]] name = "png" version = "0.18.1" @@ -2535,15 +2331,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "primal-check" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" -dependencies = [ - "num-integer", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -2718,16 +2505,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rand_distr" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" -dependencies = [ - "num-traits", - "rand 0.9.2", -] - [[package]] name = "ratatui" version = "0.30.0" @@ -2863,12 +2640,6 @@ dependencies = [ "rgb", ] -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - [[package]] name = "rayon" version = "1.11.0" @@ -3043,29 +2814,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustdct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551" -dependencies = [ - "rustfft", -] - -[[package]] -name = "rustfft" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" -dependencies = [ - "num-complex", - "num-integer", - "num-traits", - "primal-check", - "strength_reduce", - "transpose", -] - [[package]] name = "rustix" version = "0.38.44" @@ -3179,15 +2927,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - [[package]] name = "same-file" version = "1.0.6" @@ -3366,19 +3105,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simba" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" -dependencies = [ - "approx", - "num-complex", - "num-traits", - "paste", - "wide", -] - [[package]] name = "simd-adler32" version = "0.3.9" @@ -3434,12 +3160,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strength_reduce" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" - [[package]] name = "strict-num" version = "0.1.1" @@ -3907,28 +3627,12 @@ dependencies = [ "once_cell", ] -[[package]] -name = "transpose" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" -dependencies = [ - "num-integer", - "strength_reduce", -] - [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" - [[package]] name = "tui-term" version = "0.3.2" @@ -4069,6 +3773,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -4079,7 +3789,6 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" name = "vibetty" version = "0.3.0" dependencies = [ - "ab_glyph", "anyhow", "axum", "axum-extra", @@ -4092,7 +3801,6 @@ dependencies = [ "futures-util", "hanconv", "image", - "imageproc", "log", "pty-process", "ratatui", @@ -4102,16 +3810,27 @@ dependencies = [ "serde", "serde_json", "strip-ansi-escapes", - "thiserror 2.0.18", - "tiny-skia", "tokio", "tower-http 0.5.2", "tui-term", "uuid", + "vibetty-screenshot", "vt100", "wav_io", ] +[[package]] +name = "vibetty-screenshot" +version = "0.1.0" +source = "git+https://github.com/second-state/vibetty-screenshot?tag=v0.1.0#260b7615774601f007ed80f0ea6eaf19bc2ba9f9" +dependencies = [ + "freetype-rs", + "image", + "thiserror 2.0.18", + "tiny-skia", + "vt100", +] + [[package]] name = "vt100" version = "0.16.2" @@ -4416,16 +4135,6 @@ dependencies = [ "wezterm-dynamic", ] -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 8c989c6..f8eafe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,3 +44,4 @@ vt100 = "0.16" # Screenshot / Image rendering vibetty-screenshot = { git = "https://github.com/second-state/vibetty-screenshot", tag = "v0.1.0" } +image = "0.25" diff --git a/src/bin/font_render_test.rs b/src/bin/font_render_test.rs deleted file mode 100644 index 3b94639..0000000 --- a/src/bin/font_render_test.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Test program for font rendering - -// Reuse the main screenshot module -#[path = "../screenshot/mod.rs"] -mod screenshot; - -fn main() -> Result<(), Box> { - println!("Testing font rendering..."); - - // Create a simple terminal screen with colored text - let mut parser = vt100::Parser::new(10, 60, 8096); - - parser.process(b"\x1b[31mRed Text\x1b[0m\r\n"); - parser.process(b"\x1b[32mGreen Text\x1b[0m\r\n"); - parser.process(b"\x1b[33mYellow Text\x1b[0m\r\n"); - parser.process(b"\x1b[34mBlue Text\x1b[0m\r\n"); - parser.process(b"\x1b[1mBold Text\x1b[0m\r\n"); - parser.process(b"Normal text with some content\r\n"); - parser.process(b"The quick brown fox jumps over the lazy dog\r\n"); - - // Save screenshot using the library function - let config = screenshot::ScreenshotConfig { - title: Some("Font Render Test".to_string()), - ..Default::default() - }; - - screenshot::save_screen_png(parser.screen(), "font_render_test.png", &config)?; - - println!("✓ Screenshot saved to font_render_test.png"); - - Ok(()) -} diff --git a/src/main.rs b/src/main.rs index 99fd3c0..c053b2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,12 +145,14 @@ async fn main() { let (screenshot_tx, screenshot_rx) = tokio::sync::mpsc::channel(4); + let image_format = args.image_format(); + let state = ws::AppState { tx: tx.clone(), cli_tx, web_vosk_tx, screenshot_tx: screenshot_tx.clone(), - image_format: args.image_format(), + image_format, }; let listener = tokio::net::TcpListener::bind(&args.bind_addr) @@ -226,7 +228,7 @@ async fn main() { &mut tui, &mut ui_title, &server_url, - state.image_format, + image_format, ) .await; match r { From dd88ccd478d7c87582124e2e9d2a887c87062b94 Mon Sep 17 00:00:00 2001 From: csh <458761603@qq.com> Date: Thu, 23 Apr 2026 09:32:07 +0800 Subject: [PATCH 5/6] feat: switch vibetty-screenshot to ab_glyph rendering backend Disable default features and enable only the ab_glyph feature for vibetty-screenshot, switching the font rendering backend. Co-Authored-By: Claude Opus 4.6 --- Cargo.lock | 389 +++++++++++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 2 +- 2 files changed, 346 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9190c84..37091b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + [[package]] name = "adler2" version = "2.0.1" @@ -119,6 +135,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -1050,28 +1075,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "freetype-rs" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d228d6de56c90dd7585341f341849441b3490180c62d27133e525eb726809b4" -dependencies = [ - "bitflags 2.11.0", - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab537ce43cab850c64b4cdc390ce7e4f47f877485ddc323208e268280c308ae" -dependencies = [ - "cc", - "libz-sys", - "pkg-config", -] - [[package]] name = "fs_extra" version = "1.3.0" @@ -1198,6 +1201,114 @@ dependencies = [ "weezl", ] +[[package]] +name = "glam" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" + +[[package]] +name = "glam" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" + +[[package]] +name = "glam" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" + +[[package]] +name = "glam" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" + +[[package]] +name = "glam" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" + +[[package]] +name = "glam" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" + +[[package]] +name = "glam" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" + +[[package]] +name = "glam" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" + +[[package]] +name = "glam" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" + +[[package]] +name = "glam" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" + +[[package]] +name = "glam" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" + +[[package]] +name = "glam" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556f6b2ea90b8d15a74e0e7bb41671c9bdf38cd9f78c284d750b9ce58a2b5be7" + +[[package]] +name = "glam" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0" + [[package]] name = "h2" version = "0.4.13" @@ -1580,6 +1691,25 @@ dependencies = [ "quick-error", ] +[[package]] +name = "imageproc" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8046da590889acc65f5880004580ebb269bbef84d6c0f5c543ec2dece46638" +dependencies = [ + "ab_glyph", + "approx", + "getrandom 0.3.4", + "image", + "itertools", + "nalgebra", + "num", + "rand 0.9.2", + "rand_distr", + "rayon", + "rustdct", +] + [[package]] name = "imgref" version = "1.12.0" @@ -1786,16 +1916,10 @@ dependencies = [ ] [[package]] -name = "libz-sys" -version = "1.1.28" +name = "libm" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "line-clipping" @@ -1885,6 +2009,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -1970,6 +2104,39 @@ dependencies = [ "pxfm", ] +[[package]] +name = "nalgebra" +version = "0.34.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df76ea0ff5c7e6b88689085804d6132ded0ddb9de5ca5b8aeb9eeadc0508a70a" +dependencies = [ + "approx", + "glam 0.14.0", + "glam 0.15.2", + "glam 0.16.0", + "glam 0.17.3", + "glam 0.18.0", + "glam 0.19.0", + "glam 0.20.5", + "glam 0.21.3", + "glam 0.22.0", + "glam 0.23.0", + "glam 0.24.2", + "glam 0.25.0", + "glam 0.27.0", + "glam 0.28.0", + "glam 0.29.3", + "glam 0.30.10", + "glam 0.31.1", + "glam 0.32.1", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2023,6 +2190,19 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2033,6 +2213,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.0" @@ -2059,6 +2248,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -2077,6 +2277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2115,6 +2316,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -2263,12 +2473,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - [[package]] name = "png" version = "0.18.1" @@ -2331,6 +2535,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -2505,6 +2718,16 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_distr" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" +dependencies = [ + "num-traits", + "rand 0.9.2", +] + [[package]] name = "ratatui" version = "0.30.0" @@ -2640,6 +2863,12 @@ dependencies = [ "rgb", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.11.0" @@ -2814,6 +3043,29 @@ dependencies = [ "semver", ] +[[package]] +name = "rustdct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551" +dependencies = [ + "rustfft", +] + +[[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + [[package]] name = "rustix" version = "0.38.44" @@ -2927,6 +3179,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3105,6 +3366,19 @@ dependencies = [ "libc", ] +[[package]] +name = "simba" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -3160,6 +3434,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + [[package]] name = "strict-num" version = "0.1.1" @@ -3627,12 +3907,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + [[package]] name = "tui-term" version = "0.3.2" @@ -3773,12 +4069,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.5" @@ -3824,8 +4114,9 @@ name = "vibetty-screenshot" version = "0.1.0" source = "git+https://github.com/second-state/vibetty-screenshot?tag=v0.1.0#260b7615774601f007ed80f0ea6eaf19bc2ba9f9" dependencies = [ - "freetype-rs", + "ab_glyph", "image", + "imageproc", "thiserror 2.0.18", "tiny-skia", "vt100", @@ -4135,6 +4426,16 @@ dependencies = [ "wezterm-dynamic", ] +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index f8eafe6..7eee01d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,5 +43,5 @@ tui-term = "0.3" vt100 = "0.16" # Screenshot / Image rendering -vibetty-screenshot = { git = "https://github.com/second-state/vibetty-screenshot", tag = "v0.1.0" } +vibetty-screenshot = { git = "https://github.com/second-state/vibetty-screenshot", tag = "v0.1.0", default-features = false, features = ["ab_glyph"] } image = "0.25" From bacf45a27875ed6a9994a9c4e9032203b3dc890b Mon Sep 17 00:00:00 2001 From: csh <458761603@qq.com> Date: Sun, 26 Apr 2026 14:31:18 +0800 Subject: [PATCH 6/6] chore: upgrade vibetty-screenshot to v0.2.0 Update vibetty-screenshot dependency from v0.1.0 to v0.2.0. Co-Authored-By: Claude Opus 4.5 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/ws.rs | 8 ++------ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37091b6..6dae1f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4111,8 +4111,8 @@ dependencies = [ [[package]] name = "vibetty-screenshot" -version = "0.1.0" -source = "git+https://github.com/second-state/vibetty-screenshot?tag=v0.1.0#260b7615774601f007ed80f0ea6eaf19bc2ba9f9" +version = "0.2.0" +source = "git+https://github.com/second-state/vibetty-screenshot?tag=v0.2.0#3f6a2e69da8db29253b9f619bd71b29865d563f3" dependencies = [ "ab_glyph", "image", diff --git a/Cargo.toml b/Cargo.toml index 7eee01d..a40c31d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,5 +43,5 @@ tui-term = "0.3" vt100 = "0.16" # Screenshot / Image rendering -vibetty-screenshot = { git = "https://github.com/second-state/vibetty-screenshot", tag = "v0.1.0", default-features = false, features = ["ab_glyph"] } +vibetty-screenshot = { git = "https://github.com/second-state/vibetty-screenshot", tag = "v0.2.0" } image = "0.25" diff --git a/src/ws.rs b/src/ws.rs index f3b1ca0..9e9dd1d 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -273,12 +273,8 @@ pub async fn run_command( TerminalEvent::ScreenGetter(getter) => { let screen = vt_parser.screen().clone(); let mut window_scrollback = 0; - let result = render_screen_to_image( - &screen, - None, - &mut window_scrollback, - image_format, - ); + let result = + render_screen_to_image(&screen, None, &mut window_scrollback, image_format); let jpeg = match result { Ok(data) => Ok(data),