cryosnap 0.2.2

CLI for cryosnap.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
# cryosnap

面向 CLI / Rust library / Node & TypeScript 绑定的代码与终端输出截图工具。
| Code and terminal screenshot tool for CLI / Rust library / Node & TypeScript bindings.

[![CI](https://github.com/Wangnov/cryosnap/actions/workflows/ci.yml/badge.svg)](https://github.com/Wangnov/cryosnap/actions/workflows/ci.yml)
[![Release](https://github.com/Wangnov/cryosnap/actions/workflows/release.yml/badge.svg)](https://github.com/Wangnov/cryosnap/actions/workflows/release.yml)
[![Publish NAPI](https://github.com/Wangnov/cryosnap/actions/workflows/publish-napi.yml/badge.svg)](https://github.com/Wangnov/cryosnap/actions/workflows/publish-napi.yml)
[![Crates.io](https://img.shields.io/crates/v/cryosnap.svg)](https://crates.io/crates/cryosnap)
[![NPM](https://img.shields.io/npm/v/cryosnap.svg)](https://www.npmjs.com/package/cryosnap)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

[中文]#中文 | [English]#english

---

## 中文

### 功能概览

- 代码高亮(syntect 主题)
- ANSI 终端输出解析(含 `--execute` PTY)
- SVG / PNG / WebP 输出
- PNG 无损优化(oxipng)
- PNG 有损量化(libimagequant,可选,支持预设)
- 可选多输出:`out.{svg,png,webp}`
- 交互式配置(`--interactive`- 配置文件:`default / base / full / user / 自定义路径`
- 字体嵌入(TTF/WOFF/WOFF2)
- Nerd Font 符号回退(按需自动下载,可配置)
- 标题栏(自动文件路径 / tmux 信息)
- 栅格化后端:纯 Rust / rsvg-convert(自动检测)
- 细节配置:padding/margin/border/shadow/line-height/lines/wrap/line-numbers

### 安装

crates.io 安装(推荐):

```bash
# CLI
cargo install cryosnap

# Rust 库
cargo add cryosnap-core
```

源码安装:

```bash
cargo install --path crates/cryosnap-cli
# 
cargo build --release
```

### CLI 使用

```bash
# 代码文件 -> PNG
cryosnap main.rs -o out.png

# stdin -> SVG
cat main.rs | cryosnap --language rust -o out.svg

# ANSI 命令输出
cryosnap --execute "eza -lah" -o out.png

# 一次生成多种格式
cryosnap main.rs --output out.{svg,png,webp}

# tmux capture-pane -> PNG (zsh 需转义 %)
cryosnap --tmux --tmux-args "-t %3 -S -200 -J" --config full -o out.png
```

默认行为:stdout 为 TTY 且未指定 `--output/--format` 时,会写入 `cryosnap.png`。

查看完整参数:

```bash
cryosnap --help
```

### 配置

内置配置:

```bash
cryosnap --config base
cryosnap --config full
cryosnap --config user
```

配置文件路径优先级:

1. `CRYOSNAP_CONFIG_PATH`
2. `CRYOSNAP_CONFIG_DIR`(会读取 `user.json`3. 系统配置目录(`ProjectDirs`
### PNG 压缩

默认使用无损优化(不会影响画质):

```bash
# 关闭无损优化
cryosnap main.rs -o out.png --png-opt=false

# 调整优化级别(0-6)
cryosnap main.rs -o out.png --png-opt-level 4

# 控制元数据剥离
cryosnap main.rs -o out.png --png-strip safe
```

有损量化(体积更小,轻微画质损失):

```bash
# 开启量化
cryosnap main.rs -o out.png --png-quant

# 使用预设(fast/balanced/best)
cryosnap main.rs -o out.png --png-quant-preset balanced

# 自定义质量/速度/抖动
cryosnap main.rs -o out.png --png-quant-quality 85 --png-quant-speed 4 --png-quant-dither 1
```

### 栅格化后端

默认使用纯 Rust 渲染(resvg/tiny-skia)。如系统已安装 `rsvg-convert`,可选用:

```bash
# 自动检测(找到 rsvg-convert 则使用)
cryosnap main.rs -o out.png --raster.backend auto

# 强制使用 rsvg-convert(未安装会报错)
cryosnap main.rs -o out.png --raster.backend rsvg

# 强制使用纯 Rust
cryosnap main.rs -o out.png --raster.backend resvg
```

### 字体与 Nerd Font

默认字体为系统 `monospace`,**不再内置任何字体**。
当输入包含非 ASCII 脚本、Emoji、或 Nerd Font 私用区字符时,会按 Unicode Script
自动匹配 Noto 系列字体;若系统字体已覆盖则不下载,否则从 **GitHub 官方仓库**
自动下载到 `~/.cryosnap/fonts`(可用 `font.dirs` 或 `CRYOSNAP_FONT_DIRS` 覆盖),随后渲染。
可用 `font.fallbacks` 指定回退链,`font.system-fallback` 控制系统字体加载
(auto/always/never),`font.auto-download` 控制自动下载;
`font.cjk-region` 指定 CJK 偏好(auto/sc/tc/hk/jp/kr,auto 会按 LANG/LC_CTYPE 推断);
`font.force-update` 可强制刷新已下载字体:

```bash
# 自动下载缺失字体(默认开启)
cryosnap main.rs -o out.png --font.auto-download true

# Nerd Font + 中文回退
cryosnap main.rs -o out.png --font.fallbacks "Symbols Nerd Font Mono, Noto Sans CJK SC" \
  --font.system-fallback auto

# 指定 CJK 偏好
cryosnap main.rs -o out.png --font.cjk-region hk

# 禁用自动下载
cryosnap main.rs -o out.png --font.auto-download false

# 强制刷新已下载字体
cryosnap main.rs -o out.png --font.force-update true
```

### 标题栏

默认启用,文件输入时显示**绝对路径**;tmux 模式显示:
`#{session_name}:#{window_index}.#{pane_index} #{pane_title}`。

```bash
# 关闭标题
cryosnap main.rs -o out.png --title=false

# 覆盖标题文本
cryosnap main.rs -o out.png --title.text "hello"

# tmux 标题格式(自定义)
cryosnap --tmux --tmux-args "-t %7 -S -200 -J" \
  --title.tmux-format "#{session_name}:#{window_name} · #{pane_current_path}" \
  -o out.png
```

### 性能建议

```bash
# 降低栅格缩放
cryosnap main.rs -o out.png --raster.scale 2

# 限制最大像素数(0 表示不限制)
cryosnap main.rs -o out.png --raster.max-pixels 8000000

# 如需极致速度可关闭 PNG 优化
cryosnap main.rs -o out.png --png-opt=false
```

### Rust 库用法

```rust
use cryosnap_core::{Config, InputSource, OutputFormat, RenderRequest};

let request = RenderRequest {
    input: InputSource::Text("fn main() {}".to_string()),
    config: Config::default(),
    format: OutputFormat::Svg,
};

let result = cryosnap_core::render(&request).unwrap();
std::fs::write("out.svg", result.bytes).unwrap();
```

### Node / TypeScript 绑定

```bash
cd crates/cryosnap-node
npm install
npm run build
```

发布版(npm):

```bash
npm install cryosnap
```

```ts
import { render, renderToFile } from "cryosnap";

const bytes = render({
  input: "console.log('hi')",
  inputKind: "text",
  config: {
    theme: "charm",
    window: true,
    showLineNumbers: true,
    padding: [20, 40, 20, 20],
    lineHeight: 1.2
  },
  format: "png"
});

require("fs").writeFileSync("out.png", bytes);

renderToFile(
  {
    input: "console.log('hi')",
    inputKind: "text",
    config: { theme: "charm" }
  },
  "out.webp"
);
```

### 开发与测试

```bash
cargo test --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo llvm-cov --workspace --ignore-filename-regex "cryosnap-node" --fail-under-lines 80
```

### 发布流程(cargo-release + cargo-dist)

```bash
# 预演
cargo release patch --workspace --dry-run

# 执行(会创建 tag vX.Y.Z)
cargo release patch --workspace --execute

# tag 会触发 GitHub Actions 的 cargo-dist 发布产物
```

crates.io 发布顺序:

```bash
cargo publish -p cryosnap-core
cargo publish -p cryosnap
```

npm 发布:

```bash
cd crates/cryosnap-node
npm publish
```

### 许可证

MIT,详见 `LICENSE`。

### 第三方许可证

- Noto 字体(含 CJK/多脚本/Emoji):SIL Open Font License 1.1(自动下载,来源 GitHub)
- Nerd Font(Symbols Nerd Font Mono):MIT(自动下载,来源 GitHub)

---

## English

### Overview

- Syntax highlighting (syntect themes)
- ANSI terminal capture (with `--execute` PTY)
- SVG / PNG / WebP output
- PNG lossless optimization (oxipng)
- PNG lossy quantization (libimagequant, optional, with presets)
- Multi-output pattern: `out.{svg,png,webp}`
- Interactive config (`--interactive`)
- Config files: `default / base / full / user / custom`
- Font embedding (TTF/WOFF/WOFF2)
- Nerd Font symbol fallback (auto-download, configurable)
- Title bar (auto file path / tmux metadata)
- Raster backends: pure Rust / rsvg-convert (auto-detect)
- Detailed styling: padding/margin/border/shadow/line-height/lines/wrap/line-numbers

### Install

From crates.io (recommended):

```bash
cargo install cryosnap
cargo add cryosnap-core
```

From source:

```bash
cargo install --path crates/cryosnap-cli
# or
cargo build --release
```

### CLI Usage

```bash
# File -> PNG
cryosnap main.rs -o out.png

# stdin -> SVG
cat main.rs | cryosnap --language rust -o out.svg

# ANSI command output
cryosnap --execute "eza -lah" -o out.png

# Multi-format output
cryosnap main.rs --output out.{svg,png,webp}

# tmux capture-pane -> PNG (escape % in zsh)
cryosnap --tmux --tmux-args "-t %3 -S -200 -J" --config full -o out.png
```

Default behavior: when stdout is a TTY and `--output/--format` is not provided, it writes `cryosnap.png`.

Show all options:

```bash
cryosnap --help
```

### Configuration

Built-in configs:

```bash
cryosnap --config base
cryosnap --config full
cryosnap --config user
```

Config path priority:

1. `CRYOSNAP_CONFIG_PATH`
2. `CRYOSNAP_CONFIG_DIR` (reads `user.json`)
3. System config directory (`ProjectDirs`)

### PNG Optimization

Lossless optimization (no quality loss):

```bash
# Disable lossless optimization
cryosnap main.rs -o out.png --png-opt=false

# Optimize level (0-6)
cryosnap main.rs -o out.png --png-opt-level 4

# Metadata stripping
cryosnap main.rs -o out.png --png-strip safe
```

Lossy quantization (smaller size with slight quality loss):

```bash
# Enable quantization
cryosnap main.rs -o out.png --png-quant

# Preset (fast/balanced/best)
cryosnap main.rs -o out.png --png-quant-preset balanced

# Custom quality/speed/dither
cryosnap main.rs -o out.png --png-quant-quality 85 --png-quant-speed 4 --png-quant-dither 1
```

### Raster Backend

Default uses pure Rust (resvg/tiny-skia). If `rsvg-convert` is available, you can opt in:

```bash
cryosnap main.rs -o out.png --raster.backend auto
cryosnap main.rs -o out.png --raster.backend rsvg
cryosnap main.rs -o out.png --raster.backend resvg
```

### Fonts & Nerd Font

Default font is system `monospace`, and **no fonts are embedded**.
When input contains non-ASCII scripts, emoji, or Nerd Font private-use glyphs, cryosnap
auto-maps to Noto families by Unicode Script. If system fonts already cover them it will not
download; otherwise it downloads from **GitHub official repos** into `~/.cryosnap/fonts`
(override via `font.dirs` or `CRYOSNAP_FONT_DIRS`).
Use `font.fallbacks` to set a fallback chain, `font.system-fallback` for system font loading
(auto/always/never), `font.auto-download` to control auto download,
`font.cjk-region` to set CJK preference (auto/sc/tc/hk/jp/kr, auto uses LANG/LC_CTYPE),
and `font.force-update` to force refreshing downloaded fonts:

```bash
# Auto-download missing fonts (default on)
cryosnap main.rs -o out.png --font.auto-download true

# Nerd Font + CJK fallback
cryosnap main.rs -o out.png --font.fallbacks "Symbols Nerd Font Mono, Noto Sans CJK SC" \
  --font.system-fallback auto

# Set CJK preference
cryosnap main.rs -o out.png --font.cjk-region hk

# Disable auto-download
cryosnap main.rs -o out.png --font.auto-download false

# Force refresh downloaded fonts
cryosnap main.rs -o out.png --font.force-update true
```

### Title Bar

Enabled by default. For file input it shows the **absolute path**; for tmux it shows:
`#{session_name}:#{window_index}.#{pane_index} #{pane_title}`.

```bash
# Disable title
cryosnap main.rs -o out.png --title=false

# Override title text
cryosnap main.rs -o out.png --title.text "hello"

# Custom tmux title format
cryosnap --tmux --tmux-args "-t %7 -S -200 -J" \
  --title.tmux-format "#{session_name}:#{window_name} · #{pane_current_path}" \
  -o out.png
```

### Performance Tips

```bash
# Lower raster scale
cryosnap main.rs -o out.png --raster.scale 2

# Limit max pixels (0 = no limit)
cryosnap main.rs -o out.png --raster.max-pixels 8000000

# Disable PNG optimization for max speed
cryosnap main.rs -o out.png --png-opt=false
```

### Rust Library Usage

```rust
use cryosnap_core::{Config, InputSource, OutputFormat, RenderRequest};

let request = RenderRequest {
    input: InputSource::Text("fn main() {}".to_string()),
    config: Config::default(),
    format: OutputFormat::Svg,
};

let result = cryosnap_core::render(&request).unwrap();
std::fs::write("out.svg", result.bytes).unwrap();
```

### Node / TypeScript Bindings

```bash
cd crates/cryosnap-node
npm install
npm run build
```

Published version (npm):

```bash
npm install cryosnap
```

```ts
import { render, renderToFile } from "cryosnap";

const bytes = render({
  input: "console.log('hi')",
  inputKind: "text",
  config: {
    theme: "charm",
    window: true,
    showLineNumbers: true,
    padding: [20, 40, 20, 20],
    lineHeight: 1.2
  },
  format: "png"
});

require("fs").writeFileSync("out.png", bytes);

renderToFile(
  {
    input: "console.log('hi')",
    inputKind: "text",
    config: { theme: "charm" }
  },
  "out.webp"
);
```

### Development & Testing

```bash
cargo test --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo llvm-cov --workspace --ignore-filename-regex "cryosnap-node" --fail-under-lines 80
```

### Release Flow

```bash
cargo release patch --workspace --dry-run
cargo release patch --workspace --execute
```

Pushing the tag will trigger GitHub Actions to publish cargo-dist artifacts.

Publish to crates.io:

```bash
cargo publish -p cryosnap-core
cargo publish -p cryosnap
```

Publish to npm:

```bash
cd crates/cryosnap-node
npm publish
```

### License

MIT, see `LICENSE`.

### Third-party Licenses

- Noto fonts (including CJK/multi-script/emoji): SIL Open Font License 1.1 (auto-downloaded from GitHub)
- Nerd Font (Symbols Nerd Font Mono): MIT (auto-downloaded from GitHub)