Skip to content

Commit 84ba273

Browse files
authored
[Feat] support users can output image to stdout and customize window radius (#114)
* [Feat] support users can output image to stdout and customize window radius * [Update] add radius config
1 parent 31f156a commit 84ba273

File tree

9 files changed

+153
-65
lines changed

9 files changed

+153
-65
lines changed

README.md

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -144,55 +144,61 @@ Or if you are using CLI tool, CodeSnap will generate a default config file for y
144144
```jsonc
145145
// Both "CaskaydiaCove Nerd Font" and "Pacifico" is pre-installed in CodeSnap, you can use them out of the box
146146
{
147-
"theme": "candy",
148-
"window": {
149-
"mac_window_bar": true,
150-
"shadow": {
151-
"radius": 20,
152-
"color": "#00000040"
153-
},
154-
"margin": {
155-
"x": 82,
156-
"y": 82
147+
"theme": "candy",
148+
"window": {
149+
"mac_window_bar": true,
150+
"shadow": {
151+
"radius": 20,
152+
"color": "#00000040"
153+
},
154+
"margin": {
155+
"x": 82,
156+
"y": 82
157+
},
158+
"border": {
159+
"width": 1,
160+
"color": "#ffffff30"
161+
},
162+
"title_config": {
163+
"color": "#ffffff",
164+
"font_family": "Pacifico"
165+
},
166+
"radius": 12
157167
},
158-
"border": {
159-
"width": 1,
160-
"color": "#ffffff30"
161-
}
162-
},
163-
"code_config": {
164-
"font_family": "CaskaydiaCove Nerd Font",
165-
"breadcrumbs": {
166-
"separator": "/",
167-
"color": "#80848b",
168-
"font_family": "CaskaydiaCove Nerd Font"
169-
}
170-
},
171-
"watermark": {
172-
"content": "CodeSnap",
173-
"font_family": "Pacifico",
174-
"color": "#ffffff"
175-
},
176-
"background": {
177-
"start": {
178-
"x": 0,
179-
"y": 0
168+
"code_config": {
169+
"font_family": "CaskaydiaCove Nerd Font",
170+
"breadcrumbs": {
171+
"enable": false,
172+
"separator": "/",
173+
"color": "#80848b",
174+
"font_family": "CaskaydiaCove Nerd Font"
175+
}
180176
},
181-
"end": {
182-
"x": "max",
183-
"y": 0
177+
"watermark": {
178+
"content": "CodeSnap",
179+
"font_family": "Pacifico",
180+
"color": "#ffffff"
184181
},
185-
"stops": [
186-
{
187-
"position": 0,
188-
"color": "#6bcba5"
182+
"background": {
183+
"start": {
184+
"x": 0,
185+
"y": 0
189186
},
190-
{
191-
"position": 1,
192-
"color": "#caf4c2"
193-
}
194-
]
195-
}
187+
"end": {
188+
"x": "max",
189+
"y": 0
190+
},
191+
"stops": [
192+
{
193+
"position": 0,
194+
"color": "#6bcba5"
195+
},
196+
{
197+
"position": 1,
198+
"color": "#caf4c2"
199+
}
200+
]
201+
}
196202
}
197203
```
198204

cli/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"title_config": {
2020
"color": "#ffffff",
2121
"font_family": "Pacifico"
22-
}
22+
},
23+
"radius": 12
2324
},
2425
"code_config": {
2526
"font_family": "CaskaydiaCove Nerd Font",

cli/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl CodeSnapCLIConfig {
2424
// Get CodeSnap config, create if the config does not exists
2525
pub fn get_config_content() -> anyhow::Result<String> {
2626
let home_dir = home::home_dir().context("Unable to get your home dir")?;
27-
let codesnap_home_path = home_dir.join(".config").join("CodeSnap");
27+
let codesnap_home_path = home_dir.join(".config").join("codesnap");
2828
let config_path = codesnap_home_path.join("config.json");
2929
let is_config_exists = config_path.try_exists()?;
3030

cli/src/main.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ mod watermark;
99
mod window;
1010

1111
use std::fs::read_to_string;
12+
use std::io;
13+
use std::io::Write;
1214

1315
use anyhow::bail;
1416
use clap::value_parser;
1517
use clap::Parser;
1618
use code::create_content;
1719
use code_config::create_code_config;
1820
use codesnap::config::CodeSnap;
19-
use codesnap::config::Content;
2021
use codesnap::config::SnapshotConfig;
22+
use codesnap::snapshot::snapshot_data::SnapshotData;
2123
use codesnap::themes::parse_code_theme;
2224
use config::CodeSnapCLIConfig;
2325
use egg::say;
@@ -63,6 +65,9 @@ struct CLI {
6365
#[arg(short, long)]
6466
output: String,
6567

68+
#[arg(long, default_value = "false")]
69+
silent: bool,
70+
6671
/// Executing a command and taking the output as the code snippet.
6772
#[arg(long, short, num_args=1..)]
6873
execute: Vec<String>,
@@ -235,6 +240,25 @@ struct CLI {
235240
}
236241

237242
fn output_snapshot(cli: &CLI, snapshot: &SnapshotConfig) -> anyhow::Result<String> {
243+
if cli.output == "raw" {
244+
// Output to stdout
245+
match snapshot.create_snapshot()?.png_data()? {
246+
SnapshotData::Image {
247+
data,
248+
width: _,
249+
height: _,
250+
} => {
251+
io::stdout().write_all(&data)?;
252+
io::stdout().flush()?;
253+
}
254+
SnapshotData::Text(content) => {
255+
print!("{content}");
256+
}
257+
};
258+
259+
return Ok("Output to stdout".to_string());
260+
}
261+
238262
// Save snapshot to clipboard
239263
if cli.output == "clipboard" {
240264
match cli.r#type.as_str() {
@@ -282,9 +306,12 @@ async fn generate_snapshot_with_config(cli: &CLI, codesnap: CodeSnap) -> anyhow:
282306
return Ok(());
283307
}
284308

285-
let message = with_spinner(|| output_snapshot(cli, &snapshot))?;
286-
287-
logger::success(&message);
309+
if !cli.silent {
310+
let message = with_spinner(|| output_snapshot(cli, &snapshot))?;
311+
logger::success(&message)
312+
} else {
313+
output_snapshot(cli, &snapshot)?;
314+
}
288315

289316
Ok(())
290317
}

core/assets/theme_configs/bamboo.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"title_config": {
1818
"color": "#ffffff",
1919
"font_family": "Pacifico"
20-
}
20+
},
21+
"radius": 12
2122
},
2223
"code_config": {
2324
"font_family": "CaskaydiaCove Nerd Font",

core/assets/theme_configs/mei.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"title_config": {
1818
"color": "#ffffff",
1919
"font_family": "Pacifico"
20-
}
20+
},
21+
"radius": 12
2122
},
2223
"code_config": {
2324
"font_family": "CaskaydiaCove Nerd Font",

core/src/components/image.rs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use tiny_skia::{Pixmap, PixmapPaint};
1+
use anyhow::anyhow;
2+
use tiny_skia::{FillRule, FilterQuality, Mask, Path, PathBuilder, Pixmap, PixmapPaint, Transform};
23

34
use crate::components::interface::{
45
component::{Component, ComponentContext, RenderParams},
@@ -31,9 +32,10 @@ impl Component for Image {
3132
_style: &ComponentStyle,
3233
_parent_style: &Style<f32>,
3334
) -> render_error::Result<()> {
34-
let transform =
35-
tiny_skia::Transform::from_scale(context.scale_factor, context.scale_factor);
36-
let paint = PixmapPaint::default();
35+
let transform = Transform::from_scale(context.scale_factor, context.scale_factor);
36+
let mut paint = PixmapPaint::default();
37+
38+
paint.quality = FilterQuality::Bilinear;
3739

3840
pixmap.draw_pixmap(
3941
render_params.x as i32,
@@ -49,12 +51,55 @@ impl Component for Image {
4951
}
5052

5153
impl Image {
52-
pub fn new(image_data: Vec<u8>) -> anyhow::Result<Self> {
53-
let image_pixmap = Pixmap::decode_png(&image_data)?;
54+
pub fn new(radius: f32, image_data: Vec<u8>) -> anyhow::Result<Self> {
55+
let mut image_pixmap = Pixmap::decode_png(&image_data)?;
56+
let rounded_mask = Self::build_round_mask(&image_pixmap, radius)?;
57+
image_pixmap.apply_mask(&rounded_mask);
5458

5559
Ok(Self {
5660
image_pixmap,
5761
children: vec![],
5862
})
5963
}
64+
65+
fn build_round_mask(pixmap: &Pixmap, radius: f32) -> anyhow::Result<Mask> {
66+
let width = pixmap.width();
67+
let height = pixmap.height();
68+
69+
if width == 0 || height == 0 {
70+
return Err(anyhow!("image pixmap must have non-zero dimensions"));
71+
}
72+
73+
let path = Self::rounded_rect_path(width as f32, height as f32, radius)?;
74+
let mut mask = Mask::new(width, height).ok_or_else(|| anyhow!("failed to create mask"))?;
75+
mask.fill_path(&path, FillRule::Winding, true, Transform::identity());
76+
77+
Ok(mask)
78+
}
79+
80+
fn rounded_rect_path(width: f32, height: f32, radius: f32) -> anyhow::Result<Path> {
81+
if width <= 0.0 || height <= 0.0 {
82+
return Err(anyhow!("rounded rect requires positive size"));
83+
}
84+
85+
let mut builder = PathBuilder::new();
86+
let radius = radius.max(0.0).min(width / 2.0).min(height / 2.0);
87+
let right = width;
88+
let bottom = height;
89+
90+
builder.move_to(radius, 0.0);
91+
builder.line_to(right - radius, 0.0);
92+
builder.quad_to(right, 0.0, right, radius);
93+
builder.line_to(right, bottom - radius);
94+
builder.quad_to(right, bottom, right - radius, bottom);
95+
builder.line_to(radius, bottom);
96+
builder.quad_to(0.0, bottom, 0.0, bottom - radius);
97+
builder.line_to(0.0, radius);
98+
builder.quad_to(0.0, 0.0, radius, 0.0);
99+
builder.close();
100+
101+
builder
102+
.finish()
103+
.ok_or_else(|| anyhow!("failed to create rounded rectangle path"))
104+
}
60105
}

core/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ pub struct Window {
174174

175175
#[builder(default = ShadowBuilder::default().build().unwrap())]
176176
pub shadow: Shadow,
177+
178+
#[builder(default = 12.0)]
179+
pub radius: f32,
177180
}
178181

179182
impl WindowBuilder {
@@ -184,6 +187,7 @@ impl WindowBuilder {
184187
border: Some(window.border),
185188
mac_window_bar: Some(window.mac_window_bar),
186189
shadow: Some(window.shadow),
190+
radius: Some(window.radius),
187191
}
188192
}
189193
}

core/src/snapshot/image_snapshot.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ pub struct ImageSnapshot {
4444

4545
impl ImageSnapshot {
4646
pub fn raw_data(&self) -> Result<SnapshotData, anyhow::Error> {
47-
Ok(SnapshotData::from_pixmap(&self.pixmap, false)?)
47+
SnapshotData::from_pixmap(&self.pixmap, false)
4848
}
4949

5050
pub fn png_data(&self) -> Result<SnapshotData, anyhow::Error> {
51-
Ok(SnapshotData::from_pixmap(&self.pixmap, true)?)
51+
SnapshotData::from_pixmap(&self.pixmap, true)
5252
}
5353

5454
pub fn svg_data(&self) -> Result<SnapshotData, anyhow::Error> {
@@ -202,7 +202,7 @@ impl ImageSnapshot {
202202
theme_provider_cloned.clone(),
203203
Box::new(
204204
Rect::create_with_border(
205-
12.,
205+
config_cloned.window.radius,
206206
editor_background_color.into(),
207207
DEFAULT_WINDOW_MIN_WIDTH,
208208
window_padding_cloned,
@@ -232,12 +232,15 @@ impl ImageSnapshot {
232232
theme_provider.clone(),
233233
Box::new(
234234
Rect::new(
235-
12.,
236-
Color::from_rgba8(255, 255, 255, 255),
235+
config.window.radius,
236+
Color::from_rgba8(255, 255, 255, 0),
237237
None,
238238
Padding::default(),
239239
"ImageContainer",
240-
vec![Box::new(Image::new(image_data.to_owned())?)],
240+
vec![Box::new(Image::new(
241+
config.window.radius,
242+
image_data.to_owned(),
243+
)?)],
241244
)
242245
.shadow(
243246
0.,

0 commit comments

Comments
 (0)