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.
This commit is contained in:
+9
-8
@@ -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_<version>_windows-gui_<arch>.zip`. Packing a viewer onto it with
|
||||
`--format binary --base` produces a `.exe` that opens with no console window
|
||||
|
||||
@@ -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_<version>_windows-gui_<arch>.zip` whose binary is linked for the GUI subsystem, so a viewer packed onto it opens with no console behind it:
|
||||
|
||||
|
||||
+16
-11
@@ -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 <title>)")
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user