v0.4.0 6203a22

init.go RawHistoryFolder

1package db
2
3import (
4	"database/sql"
5	"fmt"
6	"os"
7	"path/filepath"
8
9	"github.com/bnema/dumber/internal/config"
10	_ "github.com/ncruces/go-sqlite3/driver" // SQLite driver
11	_ "github.com/ncruces/go-sqlite3/embed"  // Embed SQLite
12)
13
14// InitDB initializes the database connection and schema
15func InitDB(dbPath string) (*sql.DB, error) {
16	if dbPath == "" {
17		return nil, fmt.Errorf("database path cannot be empty")
18	}
19
20	// Ensure database directory exists
21	if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
22		return nil, fmt.Errorf("failed to create database directory: %w", err)
23	}
24
25	// Open database connection
26	database, err := sql.Open("sqlite3", dbPath)
27	if err != nil {
28		return nil, fmt.Errorf("failed to open database: %w", err)
29	}
30
31	// Test the connection
32	if err := database.Ping(); err != nil {
33		database.Close()
34		return nil, fmt.Errorf("failed to connect to database: %w", err)
35	}
36
37	// Initialize database schema
38	if err := initializeSchema(database); err != nil {
39		database.Close()
40		return nil, fmt.Errorf("failed to initialize database schema: %w", err)
41	}
42
43	return database, nil
44}
45
46// InitDBWithConfig initializes the database with configuration-based shortcuts
47func InitDBWithConfig(dbPath string, cfg *config.Config) (*sql.DB, error) {
48	db, err := InitDB(dbPath)
49	if err != nil {
50		return nil, err
51	}
52
53	// Initialize shortcuts from configuration
54	if err := initializeShortcuts(db, cfg); err != nil {
55		db.Close()
56		return nil, fmt.Errorf("failed to initialize shortcuts: %w", err)
57	}
58
59	return db, nil
60}
61
62// initializeSchema creates the database schema if it doesn't exist
63func initializeSchema(db *sql.DB) error {
64	schema := `
65	-- History tracking for visited URLs
66	CREATE TABLE IF NOT EXISTS history (
67		id INTEGER PRIMARY KEY AUTOINCREMENT,
68		url TEXT NOT NULL UNIQUE,
69		title TEXT,
70		visit_count INTEGER DEFAULT 1,
71		last_visited DATETIME DEFAULT CURRENT_TIMESTAMP,
72		created_at DATETIME DEFAULT CURRENT_TIMESTAMP
73	);
74
75	CREATE INDEX IF NOT EXISTS idx_history_url ON history(url);
76	CREATE INDEX IF NOT EXISTS idx_history_last_visited ON history(last_visited);
77
78	-- URL shortcuts configuration
79	CREATE TABLE IF NOT EXISTS shortcuts (
80		id INTEGER PRIMARY KEY AUTOINCREMENT,
81		shortcut TEXT NOT NULL UNIQUE,
82		url_template TEXT NOT NULL,
83		description TEXT,
84		created_at DATETIME DEFAULT CURRENT_TIMESTAMP
85	);
86
87	-- Zoom persistence per domain (not full URL)
88	CREATE TABLE IF NOT EXISTS zoom_levels (
89		domain TEXT PRIMARY KEY,
90		zoom_factor REAL NOT NULL DEFAULT 1.0 CHECK(zoom_factor >= 0.3 AND zoom_factor <= 5.0),
91		updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
92	);
93
94	CREATE INDEX IF NOT EXISTS idx_zoom_levels_updated_at ON zoom_levels(updated_at);
95	`
96
97	// Execute schema creation
98	_, err := db.Exec(schema)
99	return err
100}
101
102// initializeShortcuts inserts configured shortcuts into the database
103func initializeShortcuts(db *sql.DB, cfg *config.Config) error {
104	if cfg == nil || cfg.SearchShortcuts == nil {
105		return nil // No shortcuts to initialize
106	}
107
108	// Insert configured shortcuts
109	for shortcut, shortcutCfg := range cfg.SearchShortcuts {
110		_, err := db.Exec(
111			"INSERT OR IGNORE INTO shortcuts (shortcut, url_template, description) VALUES (?, ?, ?)",
112			shortcut, shortcutCfg.URL, shortcutCfg.Description,
113		)
114		if err != nil {
115			return fmt.Errorf("failed to insert shortcut %s: %w", shortcut, err)
116		}
117	}
118
119	return nil
120}

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