diff --git a/README.md b/README.md index fe64775..ee3c6ef 100644 --- a/README.md +++ b/README.md @@ -144,55 +144,61 @@ Or if you are using CLI tool, CodeSnap will generate a default config file for y ```jsonc // Both "CaskaydiaCove Nerd Font" and "Pacifico" is pre-installed in CodeSnap, you can use them out of the box { - "theme": "candy", - "window": { - "mac_window_bar": true, - "shadow": { - "radius": 20, - "color": "#00000040" - }, - "margin": { - "x": 82, - "y": 82 + "theme": "candy", + "window": { + "mac_window_bar": true, + "shadow": { + "radius": 20, + "color": "#00000040" + }, + "margin": { + "x": 82, + "y": 82 + }, + "border": { + "width": 1, + "color": "#ffffff30" + }, + "title_config": { + "color": "#ffffff", + "font_family": "Pacifico" + }, + "radius": 12 }, - "border": { - "width": 1, - "color": "#ffffff30" - } - }, - "code_config": { - "font_family": "CaskaydiaCove Nerd Font", - "breadcrumbs": { - "separator": "/", - "color": "#80848b", - "font_family": "CaskaydiaCove Nerd Font" - } - }, - "watermark": { - "content": "CodeSnap", - "font_family": "Pacifico", - "color": "#ffffff" - }, - "background": { - "start": { - "x": 0, - "y": 0 + "code_config": { + "font_family": "CaskaydiaCove Nerd Font", + "breadcrumbs": { + "enable": false, + "separator": "/", + "color": "#80848b", + "font_family": "CaskaydiaCove Nerd Font" + } }, - "end": { - "x": "max", - "y": 0 + "watermark": { + "content": "CodeSnap", + "font_family": "Pacifico", + "color": "#ffffff" }, - "stops": [ - { - "position": 0, - "color": "#6bcba5" + "background": { + "start": { + "x": 0, + "y": 0 }, - { - "position": 1, - "color": "#caf4c2" - } - ] - } + "end": { + "x": "max", + "y": 0 + }, + "stops": [ + { + "position": 0, + "color": "#6bcba5" + }, + { + "position": 1, + "color": "#caf4c2" + } + ] + } } ``` diff --git a/cli/config.json b/cli/config.json index 449ce74..35f1b00 100644 --- a/cli/config.json +++ b/cli/config.json @@ -19,7 +19,8 @@ "title_config": { "color": "#ffffff", "font_family": "Pacifico" - } + }, + "radius": 12 }, "code_config": { "font_family": "CaskaydiaCove Nerd Font", diff --git a/cli/src/config.rs b/cli/src/config.rs index b21dd06..3e9d50e 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -24,7 +24,7 @@ impl CodeSnapCLIConfig { // Get CodeSnap config, create if the config does not exists pub fn get_config_content() -> anyhow::Result { let home_dir = home::home_dir().context("Unable to get your home dir")?; - let codesnap_home_path = home_dir.join(".config").join("CodeSnap"); + let codesnap_home_path = home_dir.join(".config").join("codesnap"); let config_path = codesnap_home_path.join("config.json"); let is_config_exists = config_path.try_exists()?; diff --git a/cli/src/main.rs b/cli/src/main.rs index fd6b6f3..a7ca2dc 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -9,6 +9,8 @@ mod watermark; mod window; use std::fs::read_to_string; +use std::io; +use std::io::Write; use anyhow::bail; use clap::value_parser; @@ -16,8 +18,8 @@ use clap::Parser; use code::create_content; use code_config::create_code_config; use codesnap::config::CodeSnap; -use codesnap::config::Content; use codesnap::config::SnapshotConfig; +use codesnap::snapshot::snapshot_data::SnapshotData; use codesnap::themes::parse_code_theme; use config::CodeSnapCLIConfig; use egg::say; @@ -63,6 +65,9 @@ struct CLI { #[arg(short, long)] output: String, + #[arg(long, default_value = "false")] + silent: bool, + /// Executing a command and taking the output as the code snippet. #[arg(long, short, num_args=1..)] execute: Vec, @@ -235,6 +240,25 @@ struct CLI { } fn output_snapshot(cli: &CLI, snapshot: &SnapshotConfig) -> anyhow::Result { + if cli.output == "raw" { + // Output to stdout + match snapshot.create_snapshot()?.png_data()? { + SnapshotData::Image { + data, + width: _, + height: _, + } => { + io::stdout().write_all(&data)?; + io::stdout().flush()?; + } + SnapshotData::Text(content) => { + print!("{content}"); + } + }; + + return Ok("Output to stdout".to_string()); + } + // Save snapshot to clipboard if cli.output == "clipboard" { match cli.r#type.as_str() { @@ -282,9 +306,12 @@ async fn generate_snapshot_with_config(cli: &CLI, codesnap: CodeSnap) -> anyhow: return Ok(()); } - let message = with_spinner(|| output_snapshot(cli, &snapshot))?; - - logger::success(&message); + if !cli.silent { + let message = with_spinner(|| output_snapshot(cli, &snapshot))?; + logger::success(&message) + } else { + output_snapshot(cli, &snapshot)?; + } Ok(()) } diff --git a/core/assets/theme_configs/bamboo.json b/core/assets/theme_configs/bamboo.json index 0105291..359ba1e 100644 --- a/core/assets/theme_configs/bamboo.json +++ b/core/assets/theme_configs/bamboo.json @@ -17,7 +17,8 @@ "title_config": { "color": "#ffffff", "font_family": "Pacifico" - } + }, + "radius": 12 }, "code_config": { "font_family": "CaskaydiaCove Nerd Font", diff --git a/core/assets/theme_configs/mei.json b/core/assets/theme_configs/mei.json index ef07b0b..226b35d 100644 --- a/core/assets/theme_configs/mei.json +++ b/core/assets/theme_configs/mei.json @@ -17,7 +17,8 @@ "title_config": { "color": "#ffffff", "font_family": "Pacifico" - } + }, + "radius": 12 }, "code_config": { "font_family": "CaskaydiaCove Nerd Font", diff --git a/core/src/components/image.rs b/core/src/components/image.rs index 1768ddc..6f95712 100644 --- a/core/src/components/image.rs +++ b/core/src/components/image.rs @@ -1,4 +1,5 @@ -use tiny_skia::{Pixmap, PixmapPaint}; +use anyhow::anyhow; +use tiny_skia::{FillRule, FilterQuality, Mask, Path, PathBuilder, Pixmap, PixmapPaint, Transform}; use crate::components::interface::{ component::{Component, ComponentContext, RenderParams}, @@ -31,9 +32,10 @@ impl Component for Image { _style: &ComponentStyle, _parent_style: &Style, ) -> render_error::Result<()> { - let transform = - tiny_skia::Transform::from_scale(context.scale_factor, context.scale_factor); - let paint = PixmapPaint::default(); + let transform = Transform::from_scale(context.scale_factor, context.scale_factor); + let mut paint = PixmapPaint::default(); + + paint.quality = FilterQuality::Bilinear; pixmap.draw_pixmap( render_params.x as i32, @@ -49,12 +51,55 @@ impl Component for Image { } impl Image { - pub fn new(image_data: Vec) -> anyhow::Result { - let image_pixmap = Pixmap::decode_png(&image_data)?; + pub fn new(radius: f32, image_data: Vec) -> anyhow::Result { + let mut image_pixmap = Pixmap::decode_png(&image_data)?; + let rounded_mask = Self::build_round_mask(&image_pixmap, radius)?; + image_pixmap.apply_mask(&rounded_mask); Ok(Self { image_pixmap, children: vec![], }) } + + fn build_round_mask(pixmap: &Pixmap, radius: f32) -> anyhow::Result { + let width = pixmap.width(); + let height = pixmap.height(); + + if width == 0 || height == 0 { + return Err(anyhow!("image pixmap must have non-zero dimensions")); + } + + let path = Self::rounded_rect_path(width as f32, height as f32, radius)?; + let mut mask = Mask::new(width, height).ok_or_else(|| anyhow!("failed to create mask"))?; + mask.fill_path(&path, FillRule::Winding, true, Transform::identity()); + + Ok(mask) + } + + fn rounded_rect_path(width: f32, height: f32, radius: f32) -> anyhow::Result { + if width <= 0.0 || height <= 0.0 { + return Err(anyhow!("rounded rect requires positive size")); + } + + let mut builder = PathBuilder::new(); + let radius = radius.max(0.0).min(width / 2.0).min(height / 2.0); + let right = width; + let bottom = height; + + builder.move_to(radius, 0.0); + builder.line_to(right - radius, 0.0); + builder.quad_to(right, 0.0, right, radius); + builder.line_to(right, bottom - radius); + builder.quad_to(right, bottom, right - radius, bottom); + builder.line_to(radius, bottom); + builder.quad_to(0.0, bottom, 0.0, bottom - radius); + builder.line_to(0.0, radius); + builder.quad_to(0.0, 0.0, radius, 0.0); + builder.close(); + + builder + .finish() + .ok_or_else(|| anyhow!("failed to create rounded rectangle path")) + } } diff --git a/core/src/config.rs b/core/src/config.rs index 7e8d43c..c6febca 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -174,6 +174,9 @@ pub struct Window { #[builder(default = ShadowBuilder::default().build().unwrap())] pub shadow: Shadow, + + #[builder(default = 12.0)] + pub radius: f32, } impl WindowBuilder { @@ -184,6 +187,7 @@ impl WindowBuilder { border: Some(window.border), mac_window_bar: Some(window.mac_window_bar), shadow: Some(window.shadow), + radius: Some(window.radius), } } } diff --git a/core/src/snapshot/image_snapshot.rs b/core/src/snapshot/image_snapshot.rs index fcec142..a0562dd 100644 --- a/core/src/snapshot/image_snapshot.rs +++ b/core/src/snapshot/image_snapshot.rs @@ -44,11 +44,11 @@ pub struct ImageSnapshot { impl ImageSnapshot { pub fn raw_data(&self) -> Result { - Ok(SnapshotData::from_pixmap(&self.pixmap, false)?) + SnapshotData::from_pixmap(&self.pixmap, false) } pub fn png_data(&self) -> Result { - Ok(SnapshotData::from_pixmap(&self.pixmap, true)?) + SnapshotData::from_pixmap(&self.pixmap, true) } pub fn svg_data(&self) -> Result { @@ -202,7 +202,7 @@ impl ImageSnapshot { theme_provider_cloned.clone(), Box::new( Rect::create_with_border( - 12., + config_cloned.window.radius, editor_background_color.into(), DEFAULT_WINDOW_MIN_WIDTH, window_padding_cloned, @@ -232,12 +232,15 @@ impl ImageSnapshot { theme_provider.clone(), Box::new( Rect::new( - 12., - Color::from_rgba8(255, 255, 255, 255), + config.window.radius, + Color::from_rgba8(255, 255, 255, 0), None, Padding::default(), "ImageContainer", - vec![Box::new(Image::new(image_data.to_owned())?)], + vec![Box::new(Image::new( + config.window.radius, + image_data.to_owned(), + )?)], ) .shadow( 0.,