5b7f7d9f31
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.
67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/tamnd/kage/pack"
|
|
"github.com/tamnd/kage/viewer"
|
|
"github.com/tamnd/kage/zim"
|
|
)
|
|
|
|
func newOpenCmd() *cobra.Command {
|
|
var addr string
|
|
var openBrowser bool
|
|
cmd := &cobra.Command{
|
|
Use: "open <file.zim>",
|
|
Short: "Serve a ZIM archive in your browser for offline reading",
|
|
Long: "open serves a packed ZIM file over a local HTTP server so you can browse the\n" +
|
|
"site exactly as it was cloned. It is the read side of kage pack --format zim.",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return runOpen(cmd.Context(), args[0], addr, openBrowser)
|
|
},
|
|
}
|
|
cmd.Flags().StringVarP(&addr, "addr", "a", "127.0.0.1:8800", "address to listen on")
|
|
cmd.Flags().BoolVar(&openBrowser, "open", true, "open the default browser")
|
|
return cmd
|
|
}
|
|
|
|
func runOpen(ctx context.Context, path, addr string, openBrowser bool) error {
|
|
r, err := zim.Open(path)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot open %q: %w", path, err)
|
|
}
|
|
defer r.Close()
|
|
|
|
ln, err := net.Listen("tcp", addr)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot listen on %s: %w", addr, err)
|
|
}
|
|
url := "http://" + ln.Addr().String()
|
|
|
|
fmt.Fprintln(os.Stderr, styleTitle.Render("kage open")+" "+styleDim.Render(path))
|
|
fmt.Fprintln(os.Stderr, " open "+styleAccent.Render(url))
|
|
if viewer.Native {
|
|
fmt.Fprintln(os.Stderr, styleDim.Render(" close the window to stop"))
|
|
} else {
|
|
fmt.Fprintln(os.Stderr, styleDim.Render(" press Ctrl-C to stop"))
|
|
}
|
|
|
|
srv := &http.Server{Handler: pack.Handler(r)}
|
|
srvErr := make(chan error, 1)
|
|
go func() { srvErr <- srv.Serve(ln) }()
|
|
|
|
_ = viewer.Show(ctx, viewer.Options{Title: archiveTitle(r), URL: url, Browser: openBrowser})
|
|
_ = srv.Close()
|
|
if err := <-srvErr; err != nil && err != http.ErrServerClosed {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|