cryosnap 0.2.4

CLI for cryosnap.
cryosnap-0.2.4 is not a library.

cryosnap

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

CI Release Publish NAPI Crates.io NPM License

中文 | 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 安装(推荐):

# CLI
cargo install cryosnap

# Rust 库
cargo add cryosnap-core

源码安装:

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

CLI 使用

# 代码文件 -> 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

查看完整参数:

cryosnap --help

配置

内置配置:

cryosnap --config base
cryosnap --config full
cryosnap --config user

配置文件路径优先级:

  1. CRYOSNAP_CONFIG_PATH
  2. CRYOSNAP_CONFIG_DIR(会读取 user.json
  3. 系统配置目录(ProjectDirs

PNG 压缩

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

# 关闭无损优化
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

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

# 开启量化
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,可选用:

# 自动检测(找到 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.dirsCRYOSNAP_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 可强制刷新已下载字体:

# 自动下载缺失字体(默认开启)
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}

# 关闭标题
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

性能建议

# 降低栅格缩放
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 库用法

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 绑定

cd crates/cryosnap-node
npm install
npm run build

发布版(npm):

npm install cryosnap
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"
);

开发与测试

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)

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

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

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

crates.io 发布顺序:

cargo publish -p cryosnap-core
cargo publish -p cryosnap

npm 发布:

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):

cargo install cryosnap
cargo add cryosnap-core

From source:

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

CLI Usage

# 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:

cryosnap --help

Configuration

Built-in configs:

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):

# 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):

# 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:

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:

# 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}.

# 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

# 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

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

cd crates/cryosnap-node
npm install
npm run build

Published version (npm):

npm install cryosnap
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

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

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:

cargo publish -p cryosnap-core
cargo publish -p cryosnap

Publish to npm:

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)