25 Commits

Author SHA1 Message Date
Tam Nguyen Duc 35860d5aaa Drop the embedded leakless binary that trips antivirus (#69)
* Drop the embedded leakless binary that trips antivirus (#68)

go-rod's launcher imports github.com/ysmood/leakless, which base64/gzip
embeds a prebuilt leakless.exe for every target. On Windows that helper is
linked straight into kage.exe, and Windows Defender flags its signature and
quarantines a fresh scoop install before kage ever runs.

kage already launches Chrome with leakless disabled (browser/leakless.go),
so the guard was never doing anything, only adding the flagged bytes. This
adds an API-compatible stub for the package under third_party/leakless with
no embedded binary and points a replace directive at it. The Windows build
loses about 1.28 MB of packed executable and no longer carries the payload
that antivirus reacts to. Support() returns false, so go-rod skips the
leakless path even if a caller re-enabled it.

* Document the leakless antivirus fix for v0.3.9

Add the release-notes and changelog entries for the leakless helper removal
(#68), so the docs site and CHANGELOG explain why the Windows build shrank and
what the earlier virus warning was.

* Drop the now-unused leakless module hash from go.sum

The replace points leakless at a local directory, so go mod tidy no longer
needs the upstream module's checksum. Keeps the tidy CI check green.
2026-07-08 19:45:16 +07:00
Xirui 8bc9c8bea1 feat: 1. add a crawl delay function to honor the Crawl-delay directive parsed from robots.txt during clone. (#57)
2. add --craw-delay flag to specify/override robots directive.
2026-06-22 16:16:54 +07:00
Tam Nguyen Duc 28d08ade88 fix brew install command to include --cask flag (#56)
The tap distributes kage as a Cask (prebuilt binary), not a Formula,
so brew needs --cask to find it. Without the flag brew errors out with
"No available formula or cask with the name kage".
2026-06-21 10:31:34 +07:00
Duc-Tam Nguyen 5ad8fff9df Add the Linux apt/dnf repository to install docs and releases
Documents the signed apt and dnf repository alongside Homebrew and Scoop, and
fires a repository_dispatch on release so the Linux repo rebuilds with the new
packages. The step is skipped when the dispatch token is unset.
2026-06-19 19:16:57 +07:00
Duc-Tam Nguyen 7157144c75 Document Homebrew and Scoop, and fix the deb/rpm filenames
The README never mentioned the package managers and the docs covered Homebrew
but not Scoop, even though both ship from every release. Add the Homebrew
(macOS) and Scoop (Windows) channels to both. Fix the Linux package globs:
GoReleaser names them kage_<ver>_amd64.deb and kage-<ver>-1.x86_64.rpm, not
kage_*_linux_amd64.{deb,rpm}, so the documented commands matched nothing.
2026-06-19 17:16:11 +07:00
Duc-Tam Nguyen 6655a434b9 Store each page under its real title in a packed ZIM
A packed page entry took its URL path as its title, so a ZIM reader's
search box suggested filenames like 5founders.html instead of readable
titles. The page's <title> was only read for the archive-level M/Title.

Read each HTML page's <title> at pack time and store it on the entry,
collapsing wrapped whitespace to one line and falling back to the path
when a page has none. A reader's suggestion search walks the title
pointer list, which is sorted by title, so typing now offers entries
like Five Founders and Female Founders. The per-page title also lands in
the title column of a parquet export.

Document the search story in the packing guide: title suggestions in any
reader, and full-text search of page bodies through parquet and DuckDB.
A Xapian full-text index stays out on purpose, since Xapian is GPL and
kage is MIT.
2026-06-17 18:12:46 +07:00
Duc-Tam Nguyen 122a80210c Cut the v0.3.4 release notes 2026-06-17 16:09:05 +07:00
Duc-Tam Nguyen a68d2b12e0 Cut the v0.3.3 release notes 2026-06-16 10:14:19 +07:00
Duc-Tam Nguyen 3ec5c8fdaf Cut the v0.3.2 release notes
Date the Unreleased charset fix as 0.3.2, update the compare links, and
add a v0.3.2 summary to the docs release-notes page.
2026-06-16 00:18:35 +07:00
Duc-Tam Nguyen a96233cf73 Cut the v0.3.1 release notes
Move the Unreleased fix into a dated 0.3.1 section, update the compare
links, and add a v0.3.1 summary to the docs release-notes page. This
patch redirects / to the main page so a mirror whose entry point is a
nested page keeps its CSS and images when served.
2026-06-15 23:12:08 +07:00
Duc-Tam Nguyen 15fb1d8be4 Cut the v0.3.0 release notes
Move the Unreleased entries into a dated 0.3.0 section, update the
compare links, and add a v0.3.0 summary to the docs release-notes page.
This release leaves bulk and off-domain assets remote, skips over-cap
downloads instead of truncating them, adds parquet export and import,
incremental packing, and identical-page dedup.
2026-06-15 21:29:49 +07:00
Tam Nguyen Duc f3704021cd Add mandatory ZIM metadata for zimcheck (#14)
* Add mandatory ZIM metadata for zimcheck

ZIM archives were missing two pieces of metadata that the spec and
zimcheck treat as mandatory: a Description and the Illustrator_48x48@1
favicon Kiwix shows as the book icon. A Name was missing too.

Every archive now writes a Name and a Description, defaulting the
description to a host-derived line when --description is not given. When
the mirror has a usable icon, the favicon is rescaled to a 48x48 PNG and
stored as Illustrator_48x48@1 with an image/png MIME, reusing the icon
discovery and square-fit scaling the app packer already uses.

AddMetadataBytes is added to the zim writer so a binary metadata value
can carry its own MIME instead of being forced to text/plain.

Verified by reading the output back through the libzim engine: all
mandatory keys are present and the illustrator decodes as a 48x48 PNG.

* Update docs for ZIM metadata and current flags

Document the new mandatory metadata in the packing guide and the Kiwix
compatibility note, and default --description in the CLI reference.

While in the reference, bring it back in line with the code: add the
--app and --icon pack flags (shipped in v0.2.0 but never documented),
drop the --max-asset-mb clone flag that does not exist, and fix a stale
--resume mention in the configuration layout.

Add the v0.2.1 release notes and cut the changelog entry.
2026-06-15 13:30:45 +07:00
Duc-Tam Nguyen 7cee00c331 Cut the v0.2.0 release notes
Give the double-click app work its own 0.2.0 section in the changelog,
above the released 0.1.2, and update the compare links. Summarise it on
the docs release-notes page: kage pack --app wraps the viewer in a
desktop app, the release ships a GUI-subsystem Windows base, and packing
detects the base binary's target OS from its executable header.
2026-06-15 12:50:46 +07:00
Duc-Tam Nguyen b5f32b7b2b Make the desktop app a --app flag instead of a format
Wrapping a packed viewer in a .app or .AppImage was its own --format app
value, parallel to zim and binary. But an app is really just the binary
format with a bundle around it, so a separate format meant duplicating the
base/icon handling and made the three formats feel like an awkward choice.

Turn it into a --app flag that builds on the binary format. It composes
with --base (including a webview base) and --icon, while --format stays
zim or binary. The bundle builders are unchanged; only the CLI surface
moves.
2026-06-15 12:49:39 +07:00
Duc-Tam Nguyen 8b8331c435 Document double-click app bundles and the Windows GUI base
README and the packing guide gain a double-click app section covering the macOS
.app, the Linux .AppDir/.AppImage, favicon icons, and the windows-gui base. The
changelog and docs release notes record the new format under Unreleased.
2026-06-15 12:49:39 +07:00
Duc-Tam Nguyen b5fb185b86 Cut the v0.1.2 release notes
Move the Unreleased entries into a 0.1.2 section in the changelog and
summarise the release on the docs release-notes page: the Chrome sandbox
now stays on by default, asset downloads retry on a transient failure,
crawl errors report a clear reason and provenance, and the container
image runs again.
2026-06-15 12:45:48 +07:00
Duc-Tam Nguyen 5d8057473b Cover all packages in gofmt and compile the webview build in CI
The gofmt gate listed packages by hand and missed pack, zim, and viewer, so a
formatting slip in the newer code would sail through. Check the whole module
instead. Add a macOS job that compiles the -tags webview viewer, the cgo path
the pure-Go CI never builds; the viewer code is identical across platforms, so
one compile guards it. Also note the new base-OS detection in the docs.
2026-06-15 00:17:02 +07:00
Duc-Tam Nguyen 5b4b523419 Feature paulgraham in the README and docs, split ZIM and binary packing
Rewrite the README around a real example, mirroring paulgraham.com for
offline reading, and split packing into two clean sections: a single ZIM
file (with what ZIM is and how to read it back through Kiwix) and a
self-contained binary. Re-record the demo gif against paulgraham.com and
add a screenshot of the native window serving the essays offline. Carry the
same framing into the docs intro pages and the packing guide, and cut the
v0.1.1 release notes.
2026-06-15 00:01:01 +07:00
Duc-Tam Nguyen 3af26ae0e5 Rewrite the README and add a recorded demo
Rework the README into the house style: badges, a one-line pitch, an
anchor nav, a commands table, and dedicated sections for clone, pack, and
the native viewer. Every flag and default is checked against the current
binary so the docs match what kage actually does.

Add a demo recorded with ascii-gif. The tape clones example.com, packs it
to a ZIM and to a self-contained binary, and serves it back offline, so
the whole loop reads in one frame. It sits at the top of the README and on
the docs home.

While reviewing the docs, fix the output path everywhere: the default is
$HOME/data/kage, not the kage-out the pages claimed, including a few
fabricated 'done kage-out/...' lines. Document pack, open, and the native
viewer in the release notes.
2026-06-14 22:25:31 +07:00
Duc-Tam Nguyen 5b7f7d9f31 Add an optional native-window viewer behind the webview tag
A packed binary opened the system browser, so it felt like a tab, not
an app. Build with -tags webview (cgo) and the viewer instead opens the
site in its own window backed by the OS WebView: WKWebView on macOS,
WebView2 on Windows, WebKitGTK on Linux.

The viewer package picks an implementation at build time. The default
file opens the browser and keeps the build pure Go, so CGO_ENABLED=0 and
the release pipeline are untouched. The webview file links the platform
WebView and runs its event loop on the main goroutine, which main now
pins with LockOSThread before anything else, since macOS requires UI on
the initial thread. Both kage open and the embedded viewer serve over
HTTP in a goroutine and hand the URL to the viewer, then tear the server
down when the window closes or Ctrl-C cancels.

The window title comes from the archive's M/Title. OpenInBrowser moves
out of pack into the viewer package, its only caller.
2026-06-14 21:07:53 +07:00
Duc-Tam Nguyen 26dabd03bf Document kage pack and kage open
Add a packing guide, the pack/open command reference, README usage, and an
Unreleased changelog section covering the zim package and the two commands.
2026-06-14 20:17:31 +07:00
Duc-Tam Nguyen 13b1891ecd Add changelog and release-notes page 2026-06-14 19:53:00 +07:00
Duc-Tam Nguyen a871265cbe Dedup pages and assets by output path, add --refresh
Crawling keyed off the raw URL, so the same page reached over http and
https, or as /index.html versus /, was a different frontier entry that
nonetheless wrote to the same file. A clone of paulgraham.com did 948
render passes for 474 files. Key pages and assets by the local path they
write instead, and collapse a directory-index document to its directory,
so each page is fetched exactly once.

Add --refresh to re-render a mirror in place (re-fetch every page, keep
the directory, overwrite) and make --no-resume truly stateless by not
persisting state.json. The default remains a resumable, idempotent crawl
that skips work already on disk.
2026-06-14 19:06:28 +07:00
Duc-Tam Nguyen e6afa91e09 Add the clone engine, CLI, tests, CI, and docs
kage renders every page in headless Chrome, snapshots the final
DOM, strips all JavaScript, and localises CSS, images, and fonts
so a site can be browsed offline as a plain folder of files.

The engine is split into small packages:

  urlx      deterministic URL to local-path mapping and scope rules
  sanitize  remove scripts, on* handlers, and javascript: URLs
  asset     rewrite HTML and CSS references, download assets
  browser   headless Chrome pool over the DevTools protocol
  robots    robots.txt matcher
  clone     the orchestrator: a polite resumable breadth-first crawl

The cli package wires a cobra and fang command surface with two
commands, clone and serve. Every pure package has table tests; the
browser and clone packages add Chrome-driven end-to-end tests that
skip when no browser is present or under -short.

CI runs gofmt, vet, build, race tests, golangci-lint, govulncheck,
and a tidy check on Linux and macOS. A goreleaser config fans one
tag out to archives, deb/rpm/apk, a Chromium-bundled GHCR image,
and the package managers. A tago docs site builds to Pages and
Cloudflare.
2026-06-14 18:22:25 +07:00
Duc-Tam Nguyen f7c5a9a1c6 Add license and readme
Start the kage repo: a tool that clones a website into a
self-contained folder you can browse offline, with the
JavaScript stripped out.
2026-06-14 18:21:50 +07:00