v0.6.0 5c50dd3

init.go RawHistoryFolder

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

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