npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@karinjs/plugin-shotium

v0.3.6

Published

karin 的 shotium 截图、渲染插件

Readme

@karinjs/plugin-shotium

karin 的 shotium 截图渲染插件。

shotium 是一个被裁剪过的 chromium:只留下 Blink 布局、Skia CPU 光栅化、字体与图片解码,以及 chromium 的网络栈, V8、content 框架、GPU 进程、DevTools 协议全部拿掉了。 它以 Node-API 的形式跑在 node 进程里,没有浏览器进程,也没有 CDP 往返。

对 karin 来说,这意味着渲染模板图这件事不再需要装一个 100+ MB 的浏览器。 代价是页面里的 JavaScript 不会执行——模板必须是服务端渲染好的静态 HTML。

安装

pnpm add @karinjs/plugin-shotium -w

装好重启 karin 即可,插件会自己把渲染器注册进 karin 的渲染器列表。

它做了什么

karin 的渲染器就是一个函数:收下 Options,还回 base64。 这个插件把 karin/puppeteer 那一套参数翻译成 shotium 的参数,再把结果翻译回去。

| karin | shotium | 说明 | | --- | --- | --- | | file | file | 本地路径按配置转成 file:// 或桥接 http 地址 | | data | — | 交给 karin 的 renderTpl 先跑 art-template | | selector | selector | 引擎内部用 Document::querySelector 解析,不注入脚本 | | fullPage | fullPage | 与 selector 互斥,fullPage 优先 | | setViewport.width/height | viewport | | | setViewport.deviceScaleFactor | scale | | | type / quality | type / quality | png 不带 quality | | omitBackground | omitBackground | jpeg 没有 alpha,会被忽略 | | clip | clip | clip.scale 无对应项 | | pageGotoParams.waitUntil | pageGotoParams.waitUntil | networkidle0/2 统一归到 networkidle | | pageGotoParams.timeout | pageGotoParams.timeout | 0(不超时) 回退到引擎默认值 | | headers | headers | 只对同源子资源生效,见下 | | multiPage | tile.height | 走引擎的分片接口 screenshotTiles() | | waitForSelector 等 | — | 引擎没有 JS 运行时,忽略并提示一次 |

分片渲染

multiPage 在 puppeteer 那边是「改视窗高度重截几次」,这里交给引擎的 screenshotTiles():文档只加载、布局、光栅化一次,引擎在光栅化的过程中按行切开、 逐片编码,同一时刻只存在一片的位图。每一片都来自同一次布局,不会出现分片之间样式对不上的情况。

multiPage: true 用配置里的 autoMultiPageHeight,传数字就是这个数字, 单位是 css 像素,上限 32000(引擎从一个滚动位置能画出的最大行数),超过会被夹到上限。 最后一片是余数,所以片数是 ceil(高度 / 单片高度)

分片不再限定输出格式:png / jpeg / webp 都能分片,quality 照常生效。

分片模式下没有「整张图」可以往 path 上写,所以 path 按片编号存:路径里写了 {n} 就替换成 1 起的序号,没写就把序号插在扩展名前面(a.pnga-1.pnga-2.png)。

本地文件怎么交给引擎

localAccess 有三档:

  • file(默认路径):file:// 直接打开,同时打开引擎的 allowFileAccess, 模板里 file:// 形式的图片、字体照常能读到。
  • http:借用 karin 自己的 express 服务,把本地文件挂在一个带随机 token 的路径下转成 http:// 再截图。 插件不另开端口。 http 文档下 chromium 会拒绝加载 file:// 子资源,所以这条路径上 HTML/CSS 里的 file:// 引用会被一并改写成桥接地址。
  • auto(默认):平时走 file,只有调用方传了 headers 时才切到 http—— 引擎只对同源子资源发送自定义头,file:// 文档没有同源可言,headers 会被整个丢掉。

桥接路径里带一段进程级随机 token,并且只放行渲染会用到的后缀, 避免 karin 监听在 0.0.0.0 时把工作目录直接暴露出去。

配置

首次运行会在 @karinjs/@karinjs-plugin-shotium/config/config.json 生成默认配置,也可以在 WebUI 里改。

| 字段 | 默认值 | 说明 | | --- | --- | --- | | mode | inprocess | inprocess 进程内 / daemon 常驻守护进程 | | localAccess | auto | 见上 | | type | png | 调用方没指定时的截图格式 | | quality | 90 | jpeg / webp 的压缩质量 | | viewport | 800x600 | 默认视窗 | | scale | 1 | 默认设备像素比 | | timeout | 30000 | 默认导航超时 | | autoMultiPageHeight | 4000 | multiPage: true 时每片的高度,css 像素,上限 32000 | | cacheDir | '' | HTTP 磁盘缓存目录,留空用引擎默认,填 off 关闭 | | cacheMaxBytes | 256MB | 缓存上限 | | userAgent | '' | 留空使用引擎内置 | | idleTimeoutMs | 300000 | 守护进程空闲退出时间 | | logStats | true | 日志里带上引擎耗时统计 |

改完配置会热重建引擎,不需要重启 karin。

性能与实测

性能结论以 Shotium 主仓库的六平台基准报告为准。 README 不再混用不同机器、版本和页面的绝对耗时,也不跨平台排名。

标准基准

下面摘录 Shotium 0.3.3 的 linux-x64 合格结果。三者在同一原生 runner、同一静态页面、 PNG、1280×720、scale 1、waitUntil: load 条件下测试;表内为 p50,越低越好。

| 场景 | Shotium | Puppeteer Shell | Puppeteer Chrome | | --- | ---: | ---: | ---: | | 冷启动 | 53 ms | 282 ms(5.32×) | 471 ms(8.89×) | | 启动稳定后的首张 | 21.028 ms | 104.061 ms(4.95×) | 114.146 ms(5.43×) | | 预热截图 | 24.905 ms | 131.601 ms(5.28×) | 157.438 ms(6.32×) | | 启动—截图—关闭循环 | 53.989 ms | 289.344 ms(5.36×) | 565.139 ms(10.47×) |

数据来源:Shotium 0.3.3 基准报告。 该次六平台聚合结果标记为“不完整”,所以上表只引用其中状态为“通过”、允许排名的 linux-x64 同平台数据,不推测缺失结果。

KKK 帮助卡片兼容性实测

2026-08-30 使用 karin-plugin-kkk 2.42.2、@karinjs/plugin-shotium 0.1.0 和 Shotium 0.3.3,在 Windows x64 对实际 SSR 帮助卡片进行了明暗主题冒烟测试:

| 主题 | 尺寸 | PNG 大小 | 插件总耗时 | 引擎耗时 | | --- | ---: | ---: | ---: | ---: | | 亮色 | 1440×2673 | 5.58 MB | 800 ms | 781.2 ms | | 暗色 | 1440×2673 | 6.01 MB | 746 ms | 732.5 ms |

两张卡片的布局、中文、背景和主题均正确。此处只有各一次样本,而且隔离测试没有启动 KKK 的 3780 字体服务,20 个 HarmonyOS 字体请求回退到系统字体,因此它只用于兼容性证明, 不参与上面的性能排名。

这类大卡片会同时承担数 MB CSS 解析和大尺寸 PNG 编码成本;Shotium 主要节省的是冷启动、 常驻内存与浏览器进程/CDP 往返开销。页面越小、调用越频繁,固定开销差异越明显。

需要更快的话,按收益排序:

  1. 输出换成 jpegwebp;代价是 jpeg 没有 alpha 通道,omitBackground 会失效。
  2. 模板侧把 CSS purge 掉,别把整份 tailwind 内联进每张图。
  3. 图别做那么大——同一份内容宽度减半,像素少四分之三。

已知限制

  • 页面里的 JavaScript 不会执行。 模板必须是 SSR 好的静态 HTML。 用 Vue/React 组件路径当模板的 file_typevue3vueStringreact)会直接报错。
  • data: 开头的地址不能当作主文档(子资源里的 data URI 正常)。 插件会把 file_type: 'htmlString' 的字符串先落盘再截。
  • 没有 waitForSelector / waitForFunction / waitForRequest / waitForResponse
  • 抗锯齿是确定性的灰阶抗锯齿,和 puppeteer 的输出会有极细微的差别。

License

MIT