diff --git a/README.md b/README.md index 76195d2..ec074e4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ + +> [!NOTE] +> 本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。 +> [English](./README.en.md) · [原始项目](https://gitea-dev.autobee.pro/frostbyte_neo/kage) · [上游 README](https://gitea-dev.autobee.pro/frostbyte_neo/kage/blob/HEAD/README.md) +> 原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。 + # kage [![ci](https://github.com/tamnd/kage/actions/workflows/ci.yml/badge.svg)](https://github.com/tamnd/kage/actions/workflows/ci.yml) @@ -6,25 +12,25 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/tamnd/kage)](https://goreportcard.com/report/github.com/tamnd/kage) [![License](https://img.shields.io/github/license/tamnd/kage)](./LICENSE) -**kage** (影, "shadow") clones a website into a folder you can browse offline, with every script stripped out. It opens each page in real headless Chrome, waits for the page to settle, snapshots the DOM a human would have seen, then deletes all the JavaScript and pulls the CSS, images, and fonts down to local paths. What lands on disk looks like the live site and runs no code. +**kage**(影,"shadow")会将网站克隆到一个可离线浏览的文件夹中,并剔除所有脚本。它会在真实的无头 Chrome(headless Chrome)中打开每个页面,等待页面稳定后,快照人类所见到的 DOM,然后删除所有 JavaScript,并将 CSS、图片和字体下载到本地路径。最终落盘的内容看起来与线上站点一致,且不会执行任何代码。 -[Install](#install) • [Quick start](#quick-start) • [Commands](#commands) • [Clone](#clone) • [Pack](#pack-it-into-one-file) • [Double-click app](#a-double-click-app) • [Native window](#a-real-window-not-a-browser-tab) • [How it works](#how-it-works) +[安装](#install) • [快速开始](#quick-start) • [命令](#commands) • [克隆](#clone) • [打包](#pack-it-into-one-file) • [双击应用](#a-double-click-app) • [原生窗口](#a-real-window-not-a-browser-tab) • [工作原理](#how-it-works) -![kage cloning paulgraham.com, packing it into one file, and serving it back offline](docs/static/demo.gif) +![kage 克隆 paulgraham.com、打包为单个文件并离线提供访问的演示](docs/static/demo.gif) -You already know the problem. You hit "Save As" on a page you want to keep, and six months later you open it to find a blank screen, a spinner that never stops, or a copy that still tries to phone home to an analytics server that no longer exists. The page was never really yours. It was a thin client for someone else's JavaScript. +你早已熟悉这个问题。你对想保存的页面点击「另存为」,六个月后打开却发现一片空白、转圈不停,或者副本仍在尝试连接早已不存在的分析服务器。这页面从未真正属于你。它只是别人 JavaScript 的瘦客户端。 -kage takes the other road. It drives a real browser, lets the page finish doing whatever it does, grabs the finished result, and then rips every script out of it. No tracking, no network calls, no surprises. Just `.html` files you can open straight off disk, hand to a friend, or pack into a single file and forget about for a decade. +kage 走了另一条路。它驱动真实浏览器,让页面完成该做的一切,抓取最终结果,然后从中剥离所有脚本。无追踪、无网络调用、无意外。只有 `.html` 文件,你可以直接从磁盘打开、交给朋友,或打包成单个文件后十年都不用管。 -Full docs and guides live at **[kage.tamnd.com](https://kage.tamnd.com)**. +完整文档与指南见 **[kage.tamnd.com](https://kage.tamnd.com)**. -## Install +## 安装 ```bash go install github.com/tamnd/kage/cmd/kage@latest ``` -Prefer a prebuilt binary? Grab an archive, a `.deb`/`.rpm`/`.apk`, or a checksum from [releases](https://github.com/tamnd/kage/releases). Or let a package manager handle it: +更喜欢预编译二进制?可从 [releases](https://github.com/tamnd/kage/releases). 获取压缩包、`.deb`/`.rpm`/`.apk` 或校验和。也可以交给包管理器: ```bash # Homebrew (macOS) @@ -44,19 +50,19 @@ sudo dnf config-manager --add-repo https://tamnd.github.io/linux-repo/dnf/tamnd. sudo dnf install kage ``` -Or skip installing Chrome yourself and use the container image, which bundles Chromium: +或者不必自行安装 Chrome,使用捆绑了 Chromium 的容器镜像: ```bash docker run --rm -v "$PWD/out:/out" ghcr.io/tamnd/kage clone paulgraham.com ``` -kage drives a real browser, so it needs Chrome or Chromium on the host. It finds a system install on its own; point it somewhere specific with `--chrome` or the `KAGE_CHROME` environment variable. The container needs nothing extra. +kage 驱动真实浏览器,因此主机上需要 Chrome 或 Chromium。它会自动查找系统安装;也可用 `--chrome` 或 `KAGE_CHROME` 环境变量指向特定路径。容器无需额外配置。 -Shell completion ships in the box: `kage completion bash|zsh|fish|powershell`. +Shell 补全已内置:`kage completion bash|zsh|fish|powershell`。 -## Quick start +## 快速开始 -Let's mirror Paul Graham's essays so you can read them on a plane, on a laptop with no wifi, or in the year 2050 after the site has finally changed its design: +我们来镜像 Paul Graham 的文章,这样你可以在飞机上、没有 Wi-Fi 的笔记本上,或在站点终于改版后的 2050 年阅读它们: ```bash # 1. Clone the site into $HOME/data/kage/paulgraham.com/ @@ -67,7 +73,7 @@ kage serve $HOME/data/kage/paulgraham.com # open http://127.0.0.1:8800 ``` -That's the whole loop. Every essay, every image, every stylesheet, frozen on your disk and runnable with zero network. The next two steps are optional but nice: collapse the whole thing into one file, and pop it open in its own window. +这就是完整流程。每篇文章、每张图片、每份样式表,都冻结在你的磁盘上,零网络即可运行。接下来两步可选但很方便:将整个镜像折叠成单个文件,并在独立窗口中打开。 ```bash # 3. Squeeze the mirror into a single shareable file @@ -79,16 +85,16 @@ kage pack paulgraham.com --format binary -o paulgraham ./paulgraham # serves itself, needs nothing installed ``` -## Commands +## 命令 -| Command | What it does | +| 命令 | 作用 | | --- | --- | -| `kage clone ` | render a site in headless Chrome and write a browsable, script-free mirror | -| `kage serve [dir]` | preview a cloned folder over a local HTTP server | -| `kage pack ` | collapse a mirror into one ZIM archive, a self-contained viewer binary, or a double-click app | -| `kage open ` | serve a packed ZIM back for offline reading | +| `kage clone ` | 在无头 Chrome 中渲染站点,并写入可浏览、无脚本的镜像 | +| `kage serve [dir]` | 通过本地 HTTP 服务器预览已克隆的文件夹 | +| `kage pack ` | 将镜像折叠为单个 ZIM 归档、自包含查看器二进制文件或双击应用 | +| `kage open ` | 提供已打包 ZIM 的离线阅读服务 | -## Clone +## 克隆 ```bash # The whole site, into $HOME/data/kage// @@ -107,109 +113,109 @@ kage clone example.com --subdomains --scroll kage clone paulgraham.com --refresh ``` -A clone is a polite, breadth-first crawl. It reads `robots.txt`, seeds itself from `sitemap.xml`, and stays on the seed host unless you tell it otherwise. It is also stubbornly idempotent: each page is keyed by the file it writes, so the same essay reached over http and https, with or without a trailing slash, gets fetched exactly once. Hit Ctrl-C and it saves its place on the way out; run it again and it picks up where it stopped. `--refresh` re-renders in place, `--force` wipes the host and starts clean. +克隆是一次礼貌的广度优先爬取。它读取 `robots.txt`,从 `sitemap.xml` 播种,除非你另行指定,否则始终停留在种子主机上。它还固执地保持幂等:每个页面以其写入的文件为键,因此同一篇文章无论通过 http 还是 https 访问、有无尾部斜杠,都只会抓取一次。按 Ctrl-C 会在退出时保存进度;再次运行会从停止处继续。`--refresh` 会原地重新渲染,`--force` 会清除该主机并从头开始。 -The flags you'll actually reach for: +你实际会用到的标志: -| Flag | Default | Meaning | +| 标志 | 默认值 | 含义 | |------|---------|---------| -| `-o, --out` | `$HOME/data/kage` | Output root; the mirror lands in `//` | -| `-p, --max-pages` | `0` | Stop after N pages (0 = no limit) | -| `-d, --max-depth` | `0` | How many links deep to follow (0 = no limit) | -| `--scope-prefix` | | Only crawl paths starting with this prefix | -| `--subdomains` | `false` | Treat subdomains of the seed host as in scope | -| `--exclude` | | Path prefixes to skip (repeatable) | -| `--scroll` | `false` | Auto-scroll each page to trigger lazy loading | -| `--workers` | `4` | How many pages to render at once | -| `--no-robots` | `false` | Ignore `robots.txt` (be nice) | -| `--crawl-delay` | `0s` | Override robots.txt `Crawl-delay` between page starts | -| `-f, --force` | `false` | Delete any existing mirror for the host first | -| `--chrome` | | Path to the Chrome/Chromium binary | +| `-o, --out` | `$HOME/data/kage` | 输出根目录;镜像落在 `//` | +| `-p, --max-pages` | `0` | 抓取 N 页后停止(0 = 无限制) | +| `-d, --max-depth` | `0` | 跟随链接的深度(0 = 无限制) | +| `--scope-prefix` | | 仅爬取以此前缀开头的路径 | +| `--subdomains` | `false` | 将种子主机的子域视为在范围内 | +| `--exclude` | | 要跳过的路径前缀(可重复) | +| `--scroll` | `false` | 自动滚动每页以触发懒加载 | +| `--workers` | `4` | 同时渲染的页面数 | +| `--no-robots` | `false` | 忽略 `robots.txt`(礼貌爬取) | +| `--crawl-delay` | `0s` | 覆盖 robots.txt;`Crawl-delay` 为页面启动间隔 | +| `-f, --force` | `false` | 先删除该主机已有的镜像 | +| `--chrome` | | Chrome/Chromium 二进制文件路径 | -`kage clone --help` has the rest, including render-timing, concurrency, and asset-size knobs. +`kage clone --help` 包含其余选项,包括渲染时序、并发与资源大小调节。 -### Serve +### 提供访问 -`kage serve` runs a tiny static file server over a cloned folder so links and assets resolve the way they would on a real host: +`kage serve` 会在已克隆文件夹上运行轻量静态文件服务器,使链接与资源像真实主机上一样解析: ```bash kage serve $HOME/data/kage/paulgraham.com # open http://127.0.0.1:8800 ``` -## Pack it into one file +## 打包成单个文件 -A mirror is a folder, which is great for browsing and lousy for moving around. Copying thousands of little files is slow, and "here, have this directory" is a clumsy thing to hand someone. `kage pack` collapses the whole mirror into one artifact, and you choose the shape: an open ZIM archive, or a single executable that *is* the site. +镜像是文件夹,便于浏览,却不便于搬运。复制成千上万个小文件很慢,「给你这个目录」也不是好送的礼物。`kage pack` 将整个镜像折叠为一个制品,形状由你选择:开放的 ZIM 归档,或本身就是站点的单个可执行文件。 -### A single ZIM file +### 单个 ZIM 文件 ```bash kage pack paulgraham.com # -> paulgraham.com.zim kage open paulgraham.com.zim ``` -ZIM is an open file format built for exactly this: a whole website (or a whole Wikipedia) squeezed into one compressed, indexed, read-only file. kage writes the entire mirror into it, text zstd-compressed and media stored as-is. It is the format behind [Kiwix](https://kiwix.org), the offline-content project people use to carry Wikipedia, Stack Overflow, and Project Gutenberg onto boats, into classrooms with no internet, and onto a phone for a long flight. Because the format is a documented standard and not a kage invention, a `paulgraham.com.zim` you make today will still open in any ZIM reader years from now. +ZIM 是专为此而生的开放文件格式:将整个网站(或整本维基百科)压缩、索引后塞进一个只读文件。kage 将整个镜像写入其中,文本经 zstd 压缩,媒体原样存储。它是 [Kiwix](https://kiwix.org), 背后的格式——人们用这一离线内容项目把维基百科、Stack Overflow 和 Project Gutenberg 带上船、带进没有互联网的教室、装进手机度过长途飞行。由于该格式是 documented standard(有文档的标准),而非 kage 的发明,你今天制作的 `paulgraham.com.zim` 多年后仍可在任何 ZIM 阅读器中打开。 -So you are not locked into kage. `kage open` is the quickest way back in, but the very same file works across the wider Kiwix ecosystem: +所以你并不被锁定在 kage 上。`kage open` 是重新接入的最快方式,但同一个文件也适用于更广泛的 Kiwix 生态系统: ```bash kage open paulgraham.com.zim # read it back with kage kiwix-serve paulgraham.com.zim # or serve it with Kiwix at http://localhost ``` -You can also double-click the file in the [Kiwix desktop app](https://kiwix.org/en/applications/), or load it on [Kiwix for Android or iOS](https://kiwix.org/en/applications/) to read your mirror on your phone. One caveat: kage writes a structurally valid archive with the standard metadata, but it does not build the full-text search index that Kiwix's own packs ship with, so browsing and clicking work everywhere while in-reader search is limited. +你也可以在 [Kiwix 桌面应用](https://kiwix.org/en/applications/), 中双击该文件,或在 [Android 或 iOS 版 Kiwix](https://kiwix.org/en/applications/) 上加载它,在手机上阅读你的镜像。有一点需要注意:kage 会写入结构合法、带有标准元数据的归档文件,但不会构建 Kiwix 官方包自带的全文搜索索引,因此浏览和点击在各处都能正常工作,但阅读器内的搜索功能会受限。 -Packing is deterministic. The same mirror always produces a byte-identical file, with the archive UUID derived from the content instead of randomized, so a pack is safe to checksum and cache. A bare host name resolves against the default output directory, which is why `kage pack paulgraham.com` just works right after `kage clone paulgraham.com`. +打包是确定性的。同一镜像始终会生成字节级完全相同的文件,归档 UUID 由内容派生而非随机生成,因此包可以安全地进行校验和与缓存。裸主机名会相对于默认输出目录解析,因此 `kage pack paulgraham.com` 在 `kage clone paulgraham.com` 之后就能直接使用。 -### A self-contained binary +### 自包含二进制文件 -`--format binary` glues the archive onto a copy of kage and hands you a single executable that serves the site offline when you run it. Whoever you send it to needs nothing installed: not kage, not a ZIM reader, nothing. +`--format binary` 将归档粘接到 kage 的副本上,生成单个可执行文件,运行后即可离线提供该站点。你把它发给任何人都无需安装任何东西:不需要 kage,不需要 ZIM 阅读器,什么都不需要。 ```bash kage pack paulgraham.com --format binary -o paulgraham ./paulgraham ``` -The appended archive is platform-independent; only the base executable carries the architecture. By default kage appends to itself, so you get a viewer for the machine you ran it on. Point `--base` at a kage built for another OS (grab one from a [release](https://github.com/tamnd/kage/releases); every platform ships one) to produce a viewer for that platform from your own machine. kage reads the base's executable header to figure out the target, so a Windows viewer automatically gets a `.exe` name: +附加的归档与平台无关;只有基础可执行文件携带架构信息。默认情况下 kage 会追加到自身,因此你会得到适用于运行它的那台机器的查看器。将 `--base` 指向为其他操作系统构建的 kage(从 [release](https://github.com/tamnd/kage/releases); 获取,每个平台都会提供)即可在你自己的机器上生成适用于该平台的查看器。kage 会读取基础可执行文件的头部以确定目标平台,因此 Windows 查看器会自动获得 `.exe` 名称: ```bash # Sitting on a Mac, build a Windows viewer kage pack paulgraham.com --format binary --base kage-windows-amd64.exe # -> paulgraham.exe ``` -The trade is size. The binary carries a whole kage, so it weighs around 13 MiB plus the site no matter how small the mirror is. When you only need the content, the ZIM is far leaner. +代价是体积。该二进制文件包含完整的 kage,因此无论镜像多小,体积都约为 13 MiB 加上站点内容。如果你只需要内容,ZIM 要精简得多。 -### A double-click app +### 双击即可运行的应用 -A bare binary is great from a terminal, but double-click it in a file manager and the experience is rough: macOS opens a Terminal window behind the site, and on Windows a console flashes up next to it. Add `--app` and kage wraps the same viewer in a proper desktop app so a double-click just opens the site, no terminal, with the mirror's own favicon as the icon. +裸二进制文件在终端中很好用,但在文件管理器中双击时体验不佳:macOS 会在站点后面打开一个 Terminal 窗口,Windows 则会在旁边闪现一个控制台。添加 `--app`,kage 会将同一查看器封装为正规的桌面应用,双击即可打开站点,无需终端,并使用镜像自身的 favicon 作为图标。 -On macOS you get a real `.app` bundle: +在 macOS 上你会得到一个真正的 `.app` 包: ```bash kage pack paulgraham.com --app # -> paulgraham.app open paulgraham.app # or double-click it in Finder ``` -On Linux, point `--base` at a Linux kage and you get an [AppImage](https://appimage.org)-style `.AppDir` with a `.desktop` launcher (`Terminal=false`, so no console). If [`appimagetool`](https://github.com/AppImage/appimagetool) is installed, kage folds it into a single double-clickable `.AppImage` for you: +在 Linux 上,将 `--base` 指向 Linux 版 kage,你会得到一个带 `.desktop` 启动器(`Terminal=false`,因此没有控制台)的 [AppImage](https://appimage.org)-style `.AppDir`。如果已安装 [`appimagetool`](https://github.com/AppImage/appimagetool),kage 会将其合并为单个可双击的 `.AppImage`: ```bash kage pack paulgraham.com --app --base kage-linux-amd64 # -> paulgraham.AppDir (+ .AppImage) ``` -kage finds the icon by digging the favicon out of the mirror (it prefers a large `apple-touch-icon.png` and falls back to `favicon.ico`); pass `--icon some.png` to override it. Pair `--app` with a `webview` base (below) and the double-click opens a native window instead of the browser, which is the full "it's an app" effect. +kage 会从镜像中提取 favicon 来查找图标(优先使用较大的 `apple-touch-icon.png`,否则回退到 `favicon.ico`);传入 `--icon some.png` 可覆盖它。将 `--app` 与 `webview` 基础(见下文)配合使用,双击会打开原生窗口而非浏览器,这才是完整的「像一个应用」效果。 -Windows needs no bundle, because there a single `.exe` already is the app. The catch is the console window. The release ships a `kage__windows-gui_.zip` whose binary is linked for the GUI subsystem, so a viewer packed onto it opens with no console behind it: +Windows 不需要打包,因为单个 `.exe` 本身就是应用。问题在于控制台窗口。发布版提供了一个 `kage__windows-gui_.zip`,其二进制链接到 GUI 子系统,因此打包在其上的查看器打开时背后不会出现控制台: ```bash # Build a console-free Windows viewer (from any OS) kage pack paulgraham.com --format binary --base kage-windows-gui-amd64.exe # -> paulgraham.exe ``` -## A real window, not a browser tab +## 真正的窗口,而非浏览器标签页 -By default a packed binary opens your system browser, which means the site shows up as yet another tab, address bar and all, next to the 47 you already have open. Build kage with the `webview` tag and it opens the site in its own window instead, backed by the operating system's WebView (WKWebView on macOS, WebView2 on Windows, WebKitGTK on Linux). Paul Graham's essays, offline, in something that looks and feels like a real app: +默认情况下,打包后的二进制文件会打开系统浏览器,这意味着站点会作为又一个标签页出现,地址栏一应俱全,挤在你已经打开的 47 个标签旁边。使用 `webview` 标签构建 kage,它会在自己的窗口中打开站点,底层使用操作系统的 WebView(macOS 上为 WKWebView,Windows 上为 WebView2,Linux 上为 WebKitGTK)。Paul Graham 的文章,离线阅读,放在一个看起来、用起来都像真正应用的东西里: -![paulgraham.com served offline in a native kage window](docs/static/webview.png) +![在原生 kage 窗口中离线提供 paulgraham.com](docs/static/webview.png) ```bash make build-webview # or: CGO_ENABLED=1 go build -tags webview ./cmd/kage @@ -217,16 +223,16 @@ kage pack paulgraham.com --format binary --base bin/kage -o paulgraham ./paulgraham # opens a window, no browser in sight ``` -This build needs cgo and links the platform WebView, so it stays opt-in. The default build is pure Go (`CGO_ENABLED=0`) and the prebuilt release binaries open the browser, which keeps the cross-compiled release simple. `kage open` honours the same tag, so built with `-tags webview` it shows a ZIM in a native window too. +此构建需要 cgo 并链接平台 WebView,因此仍为可选。默认构建为纯 Go(`CGO_ENABLED=0`),预构建的发布二进制文件会打开浏览器,这使交叉编译的发布保持简单。`kage open` 遵循同一标签,因此使用 `-tags webview` 构建时,也会在原生窗口中显示 ZIM。 -## How it works +## 工作原理 ``` seed URL ─▶ headless Chrome ─▶ final DOM ─▶ strip JS ─▶ localise assets ─▶ disk (render) (snapshot) (sanitize) (rewrite links) ``` -A pool of Chrome tabs renders pages; a separate pool fetches assets over plain HTTP. Every URL maps deterministically to a local path, so links get rewritten before the asset they point at has even finished downloading. The output looks like this: +一组 Chrome 标签页负责渲染页面;另一组通过纯 HTTP 获取资源。每个 URL 都会确定性地映射到本地路径,因此在它们指向的资源下载完成之前,链接就已经被重写。输出如下所示: ``` paulgraham.com/ @@ -239,9 +245,9 @@ paulgraham.com/ └── ... ``` -`pack` rides on the same idea: the mirror's links are already mirror-relative paths, and those map one-to-one onto the archive's content entries, so a click in a served page hits the right entry with no rewriting at all. +`pack` 沿用同一思路:镜像中的链接已经是镜像相对路径,这些路径与归档内容条目一一对应,因此在已提供服务的页面中点击会直接命中正确的条目,完全无需重写。 -## Building from source +## 从源码构建 ```bash git clone https://github.com/tamnd/kage @@ -252,7 +258,7 @@ make test # full suite, including the Chrome-driven end-to-end tests make test-short # skip the tests that launch a browser ``` -The repo is split by concern: +仓库按职责划分: ``` cmd/kage/ thin main: pins the main thread, then hands off to cli.Execute @@ -268,17 +274,17 @@ viewer/ present a served site: system browser, or native window (webview tag docs/ the tago documentation site ``` -## Releasing +## 发布 -Push a version tag and GitHub Actions runs GoReleaser, which builds the archives, the `.deb`/`.rpm`/`.apk` packages, a multi-arch GHCR image with Chromium bundled, checksums, SBOMs, and a cosign signature: +推送版本标签后,GitHub Actions 会运行 GoReleaser,构建归档文件、`.deb`/`.rpm`/`.apk` 包、捆绑 Chromium 的多架构 GHCR 镜像、校验和、SBOM 以及 cosign 签名: ```bash git tag v0.1.1 git push --tags ``` -The image tag carries no `v` prefix (`ghcr.io/tamnd/kage:0.1.1`). The Homebrew and Scoop steps self-disable until their tokens exist, so the first release works with no extra secrets. +镜像标签不带 `v` 前缀(`ghcr.io/tamnd/kage:0.1.1`)。Homebrew 和 Scoop 步骤会在其 token 存在之前自动禁用,因此首次发布无需额外密钥即可工作。 -## License +## 许可证 -MIT. See [LICENSE](LICENSE). +MIT。参见 [LICENSE](LICENSE)。