1// Package cmd provides Cobra CLI commands for dumber.
2package cmd
3
4import (
5 "errors"
6 "fmt"
7 "os"
8
9 "github.com/spf13/cobra"
10
11 "github.com/bnema/dumber/internal/cli"
12 "github.com/bnema/dumber/internal/domain/build"
13)
14
15var (
16 app *cli.App
17 buildInfo build.Info
18 rootCmd = &cobra.Command{
19 Use: "dumber",
20 Short: "A fully unfeatured unbloated browser for tiling WMs",
21 SilenceErrors: true,
22 SilenceUsage: true,
23 Long: `Dumber - a dumb browser that works like your favorite terminal multiplexer.
24
25A fully unfeatured unbloated browser for tiling WMs, built with GTK4 and WebKitGTK.
26
27Features:
28 - Wayland native (Sway, Hyprland, River, Niri, etc.)
29 - Tabs and workspaces with split or stacked panes
30 - Keyboard-driven workflow inspired by Zellij
31 - GPU rendering with automatic VA-API/VDPAU detection
32 - Built-in ad blocking (UBlock-based network + cosmetic filtering)
33 - Launcher integration (rofi/fuzzel) with favicons
34 - Search shortcuts via bangs (!g, !gi, etc.)
35 - Session management with auto-save and restore
36
37Use 'dumber browse' to launch the graphical browser, or explore the
38subcommands for CLI-based operations like history search and session
39management.`,
40 PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
41 // Skip initialization for commands that don't need app context
42 switch cmd.Name() {
43 case "help", "completion", "gen-docs":
44 return nil
45 }
46
47 var err error
48 app, err = cli.NewApp()
49 if err != nil {
50 return fmt.Errorf("initialize app: %w", err)
51 }
52 // Set build info from main.go
53 app.BuildInfo = buildInfo
54 return nil
55 },
56 PersistentPostRun: func(_ *cobra.Command, _ []string) {
57 if app != nil {
58 _ = app.Close()
59 }
60 },
61 }
62)
63
64// Execute runs the root command.
65func Execute() {
66 if err := rootCmd.Execute(); err != nil {
67 var printedErr *printedError
68 if errors.As(err, &printedErr) {
69 os.Exit(1)
70 }
71 fmt.Fprintln(os.Stderr, err)
72 os.Exit(1)
73 }
74}
75
76type printedError struct {
77 err error
78}
79
80func (e *printedError) Error() string {
81 if e == nil || e.err == nil {
82 return ""
83 }
84 return e.err.Error()
85}
86
87func (e *printedError) Unwrap() error {
88 if e == nil {
89 return nil
90 }
91 return e.err
92}
93
94func wrapPrintedError(err error) error {
95 if err == nil {
96 return nil
97 }
98 return &printedError{err: err}
99}
100
101// GetApp returns the initialized app (for use by subcommands).
102func GetApp() *cli.App {
103 return app
104}
105
106// browseCmd is a placeholder for help - actual execution is in main.go
107var browseCmd = &cobra.Command{
108 Use: "browse [url]",
109 Short: "Launch the graphical browser",
110 Long: `Launch the GTK4 graphical browser.
111
112If a URL is provided, navigate to it. Otherwise, open the homepage.
113
114Examples:
115 dumber browse # Open browser to homepage
116 dumber browse example.com # Open browser to URL`,
117 Run: func(_ *cobra.Command, _ []string) {
118 // This is handled by main.go before cobra runs
119 },
120}
121
122func init() {
123 rootCmd.AddCommand(browseCmd)
124}
125
126// SetBuildInfo sets the build information (called from main.go before Execute).
127func SetBuildInfo(info build.Info) {
128 buildInfo = info
129}