v0.14.0 76d1892

root.go RawHistoryFolder

1// Package cli provides the command-line interface for dumber.
2package cli
3
4import (
5	"database/sql"
6	"fmt"
7	"log"
8	"os"
9	"path/filepath"
10
11	"github.com/bnema/dumber/internal/config"
12	"github.com/bnema/dumber/internal/db"
13	"github.com/bnema/dumber/internal/services"
14
15	_ "github.com/ncruces/go-sqlite3/driver" // SQLite driver for database/sql
16	_ "github.com/ncruces/go-sqlite3/embed"  // Embed SQLite for cross-platform compatibility
17	"github.com/spf13/cobra"
18)
19
20// File permission constants
21const (
22	dirPerm = 0755 // Standard directory permissions (rwxr-xr-x)
23)
24
25// CLI holds the database connection, queries, and configuration for the CLI commands
26type CLI struct {
27	DB            *sql.DB
28	Queries       *db.Queries
29	Config        *config.Config
30	ParserService *services.ParserService
31}
32
33// NewCLI creates a new CLI instance with database connection and configuration
34func NewCLI() (*CLI, error) {
35	// Get configuration
36	cfg := config.Get()
37
38	// Use configured database path
39	dbPath := cfg.Database.Path
40	if dbPath == "" {
41		// Fallback to default path if not configured
42		var err error
43		dbPath, err = config.GetDatabaseFile()
44		if err != nil {
45			return nil, fmt.Errorf("failed to get database path: %w", err)
46		}
47	}
48
49	// Ensure database directory exists
50	if err := os.MkdirAll(filepath.Dir(dbPath), dirPerm); err != nil {
51		return nil, fmt.Errorf("failed to create database directory: %w", err)
52	}
53
54	// Open database connection
55	database, err := sql.Open("sqlite3", dbPath)
56	if err != nil {
57		return nil, fmt.Errorf("failed to open database: %w", err)
58	}
59
60	// Test the connection
61	if err := database.Ping(); err != nil {
62		if err := database.Close(); err != nil {
63			log.Printf("Warning: failed to close database: %v", err)
64		}
65		return nil, fmt.Errorf("failed to connect to database: %w", err)
66	}
67
68	queries := db.New(database)
69	parserService := services.NewParserService(cfg, queries)
70
71	return &CLI{
72		DB:            database,
73		Queries:       queries,
74		Config:        cfg,
75		ParserService: parserService,
76	}, nil
77}
78
79// Close closes the database connection
80func (c *CLI) Close() error {
81	if c.DB != nil {
82		return c.DB.Close()
83	}
84	return nil
85}
86
87// NewRootCmd creates the root command for dumber
88func NewRootCmd(version, commit, buildDate string) *cobra.Command {
89	rootCmd := &cobra.Command{
90		Use:   "dumber",
91		Short: "A dumb browser for Wayland window managers",
92		Long:  `A fast, simple browser with rofi/dmenu integration for sway and hyprland window managers.`,
93		RunE: func(cmd *cobra.Command, _ []string) error {
94			// Show help when no subcommand is provided
95			return cmd.Help()
96		},
97	}
98
99	// Add dmenu flag for launcher integration
100	rootCmd.Flags().Bool("dmenu", false, "Run in dmenu mode for launcher integration")
101
102	// Version command
103	versionCmd := &cobra.Command{
104		Use:   "version",
105		Short: "Print version information",
106		Run: func(_ *cobra.Command, _ []string) {
107			fmt.Printf("dumber %s\n", version)
108			fmt.Printf("commit: %s\n", commit)
109			fmt.Printf("built: %s\n", buildDate)
110		},
111	}
112
113	// Init command (enhanced)
114	initCmd := &cobra.Command{
115		Use:   "init",
116		Short: "Initialize dumber database and configuration",
117		RunE: func(_ *cobra.Command, _ []string) error {
118			cli, err := NewCLI()
119			if err != nil {
120				return fmt.Errorf("failed to initialize CLI: %w", err)
121			}
122			defer func() {
123				if closeErr := cli.Close(); closeErr != nil {
124					fmt.Fprintf(os.Stderr, "Warning: failed to close database: %v\n", closeErr)
125				}
126			}()
127
128			fmt.Printf("dumber %s - Initialization complete!\n", version)
129			fmt.Println("Database initialized at:", cli.Config.Database.Path)
130
131			// Show XDG directories
132			xdgDirs, err := config.GetXDGDirs()
133			if err == nil {
134				fmt.Println("Configuration directories:")
135				fmt.Printf("- Config: %s\n", xdgDirs.ConfigHome)
136				fmt.Printf("- Data: %s\n", xdgDirs.DataHome)
137				fmt.Printf("- State: %s\n", xdgDirs.StateHome)
138			}
139
140			// Show configured shortcuts
141			fmt.Println("Configured shortcuts:")
142			for shortcut, cfg := range cli.Config.SearchShortcuts {
143				fmt.Printf("- %s:%s -> %s\n", shortcut, "query", cfg.Description)
144			}
145			return nil
146		},
147	}
148
149	// Add subcommands
150	rootCmd.AddCommand(versionCmd)
151	rootCmd.AddCommand(initCmd)
152	rootCmd.AddCommand(NewBrowseCmd())
153	rootCmd.AddCommand(NewDmenuCmd())
154	rootCmd.AddCommand(NewHistoryCmd())
155	rootCmd.AddCommand(NewPurgeCmd())
156	rootCmd.AddCommand(NewLogsCmd())
157	rootCmd.AddCommand(NewConfigCmd())
158
159	return rootCmd
160}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close