diff --git a/CHANGELOG.md b/CHANGELOG.md index 96074ab..69e5b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,14 +16,15 @@ All notable changes to kage are recorded here. The format follows - Container-aware Chrome flags. kage detects a container from the `IN_DOCKER` environment variable or a `/.dockerenv` marker and, only there, drops the sandbox and adds `--disable-dev-shm-usage` (the default 64 MB `/dev/shm` is too small for Chrome on large pages). Outside a container the faster shared memory is left in place. - Asset downloads retry on a transient failure (a 403/429, a 5xx, or a network blip) with a short backoff, recovering files that bot-protection rejects on the first request of a burst. Permanent failures (404, 401, ...) are not retried. -- `kage pack --format app` wraps the packed viewer in a double-click desktop app - with the site's favicon as the icon. On macOS it writes a `.app` bundle - (`Info.plist`, the viewer under `Contents/MacOS`, and an `.icns` generated from - the favicon); on Linux, with a Linux `--base`, it writes an AppImage-style - `.AppDir` and folds it into a single `.AppImage` when `appimagetool` is - installed. The icon is found in the mirror automatically (preferring a large - `apple-touch-icon.png`, then `favicon.png` or a PNG-based `favicon.ico`) and - can be overridden with `--icon`. +- `kage pack --app` wraps the packed viewer in a double-click desktop app with + the site's favicon as the icon. The flag builds on the binary format, so it + composes with `--base` (including a `webview` base) and `--icon`. On macOS it + writes a `.app` bundle (`Info.plist`, the viewer under `Contents/MacOS`, and an + `.icns` generated from the favicon); on Linux, with a Linux `--base`, it writes + an AppImage-style `.AppDir` and folds it into a single `.AppImage` when + `appimagetool` is installed. The icon is found in the mirror automatically + (preferring a large `apple-touch-icon.png`, then `favicon.png` or a PNG-based + `favicon.ico`) and can be overridden with `--icon`. - The release now ships a GUI-subsystem Windows base, `kage__windows-gui_.zip`. Packing a viewer onto it with `--format binary --base` produces a `.exe` that opens with no console window diff --git a/README.md b/README.md index 62ca049..e650d2c 100644 --- a/README.md +++ b/README.md @@ -160,22 +160,22 @@ The trade is size. The binary carries a whole kage, so it weighs around 13 MiB p ### 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. `--format app` 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. +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. On macOS you get a real `.app` bundle: ```bash -kage pack paulgraham.com --format app # -> paulgraham.app +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: ```bash -kage pack paulgraham.com --format app --base kage-linux-amd64 # -> paulgraham.AppDir (+ .AppImage) +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 `--format 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 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. 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: diff --git a/cli/pack.go b/cli/pack.go index 0b795aa..c40be60 100644 --- a/cli/pack.go +++ b/cli/pack.go @@ -21,6 +21,7 @@ type packFlags struct { format string out string base string + app bool icon string noCompress bool title string @@ -38,18 +39,19 @@ func newPackCmd() *cobra.Command { "it writes an open ZIM archive (the format Kiwix uses) that kage open or any\n" + "ZIM reader can browse. With --format binary it appends that archive to a copy\n" + "of kage, producing a single executable that serves the site offline when run.\n" + - "With --format app it wraps that executable in a macOS .app you can double-click,\n" + - "with the site's favicon as the icon.", + "Add --app to wrap that executable in a double-click desktop app (a .app bundle\n" + + "on macOS, an AppImage-style .AppDir on Linux) with the site's favicon as the icon.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return runPack(args[0], f) }, } fs := cmd.Flags() - fs.StringVar(&f.format, "format", "zim", "output format: zim, binary, or app") + fs.StringVar(&f.format, "format", "zim", "output format: zim or binary") fs.StringVarP(&f.out, "out", "o", "", "output path (default per format)") - fs.StringVar(&f.base, "base", "", "base kage binary for binary/app (default this kage)") - fs.StringVar(&f.icon, "icon", "", "icon file for --format app (default the site's favicon)") + fs.StringVar(&f.base, "base", "", "base kage binary for the viewer (default this kage)") + fs.BoolVar(&f.app, "app", false, "wrap the viewer in a double-click desktop app (.app on macOS, .AppImage/.AppDir on Linux)") + fs.StringVar(&f.icon, "icon", "", "icon file for --app (default the site's favicon)") fs.BoolVar(&f.noCompress, "no-compress", false, "store every cluster raw, no zstd") fs.StringVar(&f.title, "title", "", "archive title (default the main page's )") fs.StringVar(&f.description, "description", "", "archive description") @@ -70,6 +72,12 @@ func runPack(mirrorArg string, f *packFlags) error { Version: Version, } + // --app wraps the packed viewer in a desktop bundle. It builds on the binary + // format, so it owns the flow rather than being one more --format value. + if f.app { + return runPackApp(dir, f, zopts) + } + switch f.format { case "zim": out, size, err := pack.BuildZIM(dir, zopts) @@ -103,11 +111,8 @@ func runPack(mirrorArg string, f *packFlags) error { printRunHint(path, target) return nil - case "app": - return runPackApp(dir, f, zopts) - default: - return fmt.Errorf("unknown --format %q (want zim, binary, or app)", f.format) + return fmt.Errorf("unknown --format %q (want zim or binary)", f.format) } } @@ -122,7 +127,7 @@ func runPackApp(dir string, f *packFlags, zopts pack.ZIMOptions) error { return fmt.Errorf("a Windows app is just the .exe, with no bundle to build: use --format binary and a GUI base (kage built with -ldflags -H=windowsgui)") case "": if f.base != "" { - return fmt.Errorf("--format app could not tell which OS %q is for; pass a macOS or Linux kage as --base", f.base) + return fmt.Errorf("--app could not tell which OS %q is for; pass a macOS or Linux kage as --base", f.base) } // No base and an unknown runtime: fall through with the host's GOOS. target = runtime.GOOS @@ -148,7 +153,7 @@ func runPackApp(dir string, f *packFlags, zopts pack.ZIMOptions) error { case "linux": return packLinuxApp(zbytes, dir, f, prog, name, icon, iconSrc) default: - return fmt.Errorf("--format app supports macOS and Linux bases; %s is not one of them", osLabel(target)) + return fmt.Errorf("--app supports macOS and Linux bases; %s is not one of them", osLabel(target)) } } diff --git a/docs/content/guides/packing-a-mirror.md b/docs/content/guides/packing-a-mirror.md index 222705d..cb0b939 100644 --- a/docs/content/guides/packing-a-mirror.md +++ b/docs/content/guides/packing-a-mirror.md @@ -101,12 +101,12 @@ xattr -d com.apple.quarantine ./paulgraham ## A double-click app -The self-contained binary is perfect from a terminal, but double-clicking it in a file manager is less tidy: on macOS Finder opens a Terminal window behind the site, and on Windows a console flashes alongside it. `--format app` wraps the same viewer in a real desktop app, so a double-click just opens the mirror with no terminal in sight, using the site's own favicon as the icon. +The self-contained binary is perfect from a terminal, but double-clicking it in a file manager is less tidy: on macOS Finder opens a Terminal window behind the site, and on Windows a console flashes alongside it. Add `--app` and kage wraps the same viewer in a real desktop app, so a double-click just opens the mirror with no terminal in sight, using the site's own favicon as the icon. On macOS it writes a standard `.app` bundle: ```bash -kage pack paulgraham.com --format app +kage pack paulgraham.com --app ``` ``` @@ -121,16 +121,16 @@ The bundle holds the packed viewer under `Contents/MacOS`, an `Info.plist` descr On Linux, point `--base` at a Linux kage and you get an [AppImage](https://appimage.org)-style `.AppDir`: the viewer as `AppRun`, a `.desktop` launcher with `Terminal=false`, and the icon as a PNG. When [`appimagetool`](https://github.com/AppImage/appimagetool) is on your `PATH`, kage runs it for you and turns the directory into one double-clickable `.AppImage`; otherwise it leaves the `.AppDir` ready for any AppImage tool. ```bash -kage pack paulgraham.com --format app --base kage-linux-amd64 # -> paulgraham.AppDir (+ .AppImage) +kage pack paulgraham.com --app --base kage-linux-amd64 # -> paulgraham.AppDir (+ .AppImage) ``` kage picks the icon by digging through the mirror for the site's favicon. It prefers a large `apple-touch-icon.png` and falls back to `favicon.png` or a PNG-based `favicon.ico`; if a site only ships a legacy BMP `.ico` the bundle is built without a custom icon rather than with a mangled one. Override the choice with `--icon path/to/image.png`. -For the full "it's an app" effect, pair `--format app` with a `webview` base so the double-click opens a native window instead of the system browser: +For the full "it's an app" effect, pair `--app` with a `webview` base so the double-click opens a native window instead of the system browser: ```bash make build-webview -kage pack paulgraham.com --format app --base bin/kage +kage pack paulgraham.com --app --base bin/kage ``` Windows needs no bundle, because there a single `.exe` already is the app. What it needs is to lose the console window. A normal build is console-attached (handy for the CLI, since that is where clone progress prints), so the release ships a second Windows binary linked for the GUI subsystem in `kage_<version>_windows-gui_<arch>.zip`. Pack a viewer onto that base and double-clicking the result opens the site with no console behind it: diff --git a/docs/content/reference/release-notes.md b/docs/content/reference/release-notes.md index 1402dec..698cec2 100644 --- a/docs/content/reference/release-notes.md +++ b/docs/content/reference/release-notes.md @@ -10,7 +10,7 @@ The authoritative, commit-level history lives in [`CHANGELOG.md`](https://github Double-click apps, so a packed mirror opens like a real desktop app instead of a terminal program. -- **`kage pack --format app`** wraps the viewer in a double-click app with the site's favicon as its icon. On macOS that is a `.app` bundle; on Linux, with a Linux `--base`, an [AppImage](https://appimage.org)-style `.AppDir` that becomes a single `.AppImage` when `appimagetool` is installed. The icon is pulled from the mirror automatically, or set with `--icon`. +- **`kage pack --app`** wraps the viewer in a double-click app with the site's favicon as its icon. The flag builds on the binary format, so it composes with `--base` (including a `webview` base) and `--icon`. On macOS that is a `.app` bundle; on Linux, with a Linux `--base`, an [AppImage](https://appimage.org)-style `.AppDir` that becomes a single `.AppImage` when `appimagetool` is installed. The icon is pulled from the mirror automatically, or set with `--icon`. - **A GUI-subsystem Windows base** ships in the release as `kage_<version>_windows-gui_<arch>.zip`. Pack a viewer onto it with `--format binary --base` and the resulting `.exe` opens with no console window behind it. - **Smarter cross-platform packing.** kage reads the base binary's executable header to detect its target OS, so a Windows viewer always gets a `.exe` name and the right run hint, regardless of how the base file is named.