detached 7f21e33

chore(repo): reorganize services structure and codebase cleanup

commit
7f21e33997a14739cc1d91a65fc932fa573d07d4
author
bamen · · 2025-09-18 18:45 UTC
committer
bamen · · 2025-09-18 20:05 UTC
parent
3b4fc68
signature
unsigned
14 files · +9 -335
D configs/sqlc.yaml +0 -15
@@ -1,15 +0,0 @@
1-version: "2"
2-sql:
3- - engine: "sqlite"
4- queries: "./internal/db/queries/"
5- schema: "./migrations/"
6- gen:
7- go:
8- package: "db"
9- out: "./internal/db"
10- sql_package: "database/sql"
11- emit_json_tags: true
12- emit_empty_slices: true
13- emit_exported_queries: true
14- emit_interface: true
15- query_parameter_limit: 5⏎̸
@@ -1,15 +0,0 @@
1-version: "2"
2-sql:
3- - engine: "sqlite"
4- queries: "./internal/db/queries/"
5- schema: "./migrations/"
6- gen:
7- go:
8- package: "db"
9- out: "./internal/db"
10- sql_package: "database/sql"
11- emit_json_tags: true
12- emit_empty_slices: true
13- emit_exported_queries: true
14- emit_interface: true
15- query_parameter_limit: 5⏎̸
M gui/bindings/database/sql/index.ts +0 -1
@@ -1,4 +1,3 @@
1-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
21 // This file is automatically generated. DO NOT EDIT
32
43 export {
@@ -1,4 +1,3 @@
1-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
2 // This file is automatically generated. DO NOT EDIT1 // This file is automatically generated. DO NOT EDIT
3 2
4 export {3 export {
M gui/package.json +1 -0
@@ -13,6 +13,7 @@
1313 "dev:color-scheme": "npm run build:color-scheme -- --watch",
1414 "check": "svelte-check",
1515 "lint": "eslint src --fix",
16+ "fmt": "prettier --write .",
1617 "generate:types": "node ../scripts/generate-types.js"
1718 },
1819 "devDependencies": {
@@ -13,6 +13,7 @@
13 "dev:color-scheme": "npm run build:color-scheme -- --watch",13 "dev:color-scheme": "npm run build:color-scheme -- --watch",
14 "check": "svelte-check",14 "check": "svelte-check",
15 "lint": "eslint src --fix",15 "lint": "eslint src --fix",
16+ "fmt": "prettier --write .",
16 "generate:types": "node ../scripts/generate-types.js"17 "generate:types": "node ../scripts/generate-types.js"
17 },18 },
18 "devDependencies": {19 "devDependencies": {
M internal/app/api/scheme.go +1 -1
@@ -10,7 +10,7 @@ import (
1010
1111 "github.com/bnema/dumber/internal/app/constants"
1212 "github.com/bnema/dumber/internal/config"
13- "github.com/bnema/dumber/services"
13+ "github.com/bnema/dumber/internal/services"
1414 )
1515
1616 // SchemeHandler handles custom dumb:// scheme resolution
@@ -10,7 +10,7 @@ import (
10 10
11 "github.com/bnema/dumber/internal/app/constants"11 "github.com/bnema/dumber/internal/app/constants"
12 "github.com/bnema/dumber/internal/config"12 "github.com/bnema/dumber/internal/config"
13- "github.com/bnema/dumber/services"13+ "github.com/bnema/dumber/internal/services"
14 )14 )
15 15
16 // SchemeHandler handles custom dumb:// scheme resolution16 // SchemeHandler handles custom dumb:// scheme resolution
M internal/app/browser/browser.go +1 -1
@@ -20,7 +20,7 @@ import (
2020 "github.com/bnema/dumber/internal/logging"
2121 "github.com/bnema/dumber/internal/migrations"
2222 "github.com/bnema/dumber/pkg/webkit"
23- "github.com/bnema/dumber/services"
23+ "github.com/bnema/dumber/internal/services"
2424 )
2525
2626 // BrowserApp represents the main browser application
@@ -20,7 +20,7 @@ import (
20 "github.com/bnema/dumber/internal/logging"20 "github.com/bnema/dumber/internal/logging"
21 "github.com/bnema/dumber/internal/migrations"21 "github.com/bnema/dumber/internal/migrations"
22 "github.com/bnema/dumber/pkg/webkit"22 "github.com/bnema/dumber/pkg/webkit"
23- "github.com/bnema/dumber/services"23+ "github.com/bnema/dumber/internal/services"
24 )24 )
25 25
26 // BrowserApp represents the main browser application26 // BrowserApp represents the main browser application
M internal/app/control/navigation.go +1 -1
@@ -6,7 +6,7 @@ import (
66 "os"
77
88 "github.com/bnema/dumber/pkg/webkit"
9- "github.com/bnema/dumber/services"
9+ "github.com/bnema/dumber/internal/services"
1010 )
1111
1212 // NavigationController manages navigation functionality
@@ -6,7 +6,7 @@ import (
6 "os"6 "os"
7 7
8 "github.com/bnema/dumber/pkg/webkit"8 "github.com/bnema/dumber/pkg/webkit"
9- "github.com/bnema/dumber/services"9+ "github.com/bnema/dumber/internal/services"
10 )10 )
11 11
12 // NavigationController manages navigation functionality12 // NavigationController manages navigation functionality
M internal/app/control/zoom.go +1 -1
@@ -5,7 +5,7 @@ import (
55 "log"
66
77 "github.com/bnema/dumber/pkg/webkit"
8- "github.com/bnema/dumber/services"
8+ "github.com/bnema/dumber/internal/services"
99 )
1010
1111 // ZoomController manages zoom functionality for the WebView
@@ -5,7 +5,7 @@ import (
5 "log"5 "log"
6 6
7 "github.com/bnema/dumber/pkg/webkit"7 "github.com/bnema/dumber/pkg/webkit"
8- "github.com/bnema/dumber/services"8+ "github.com/bnema/dumber/internal/services"
9 )9 )
10 10
11 // ZoomController manages zoom functionality for the WebView11 // ZoomController manages zoom functionality for the WebView
M internal/app/messaging/handler.go +1 -1
@@ -12,7 +12,7 @@ import (
1212 "github.com/bnema/dumber/internal/app/constants"
1313 "github.com/bnema/dumber/internal/app/control"
1414 "github.com/bnema/dumber/pkg/webkit"
15- "github.com/bnema/dumber/services"
15+ "github.com/bnema/dumber/internal/services"
1616 )
1717
1818 // Handler processes script messages from the WebView
@@ -12,7 +12,7 @@ import (
12 "github.com/bnema/dumber/internal/app/constants"12 "github.com/bnema/dumber/internal/app/constants"
13 "github.com/bnema/dumber/internal/app/control"13 "github.com/bnema/dumber/internal/app/control"
14 "github.com/bnema/dumber/pkg/webkit"14 "github.com/bnema/dumber/pkg/webkit"
15- "github.com/bnema/dumber/services"15+ "github.com/bnema/dumber/internal/services"
16 )16 )
17 17
18 // Handler processes script messages from the WebView18 // Handler processes script messages from the WebView
M internal/cli/root.go +1 -1
@@ -12,7 +12,7 @@ import (
1212 "github.com/bnema/dumber/internal/config"
1313 "github.com/bnema/dumber/internal/db"
1414 "github.com/bnema/dumber/internal/migrations"
15- "github.com/bnema/dumber/services"
15+ "github.com/bnema/dumber/internal/services"
1616
1717 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver for database/sql
1818 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite for cross-platform compatibility
@@ -12,7 +12,7 @@ import (
12 "github.com/bnema/dumber/internal/config"12 "github.com/bnema/dumber/internal/config"
13 "github.com/bnema/dumber/internal/db"13 "github.com/bnema/dumber/internal/db"
14 "github.com/bnema/dumber/internal/migrations"14 "github.com/bnema/dumber/internal/migrations"
15- "github.com/bnema/dumber/services"15+ "github.com/bnema/dumber/internal/services"
16 16
17 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver for database/sql17 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver for database/sql
18 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite for cross-platform compatibility18 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite for cross-platform compatibility
R services/browser_service.go → internal/services/browser.go +0 -0 renamed, 100% similar

renamed, 100% similar

R services/parser_service.go → internal/services/parser.go +0 -0 renamed, 100% similar

renamed, 100% similar

D services/config_service.go +0 -311
@@ -1,311 +0,0 @@
1-package services
2-
3-import (
4- "context"
5- "fmt"
6- "os"
7- "path/filepath"
8-
9- "github.com/bnema/dumber/internal/config"
10- "strings"
11-)
12-
13-// ConfigService handles configuration operations for the application.
14-type ConfigService struct {
15- config *config.Config
16- configPath string
17-}
18-
19-const defaultDirPerm = 0o750
20-
21-// ConfigInfo represents configuration information for the frontend.
22-type ConfigInfo struct {
23- ConfigPath string `json:"config_path"`
24- DatabasePath string `json:"database_path"`
25- SearchShortcuts map[string]config.SearchShortcut `json:"search_shortcuts"`
26- DmenuSettings *config.DmenuConfig `json:"dmenu_settings"`
27- RenderingMode string `json:"rendering_mode"`
28- UseDomZoom bool `json:"use_dom_zoom"`
29- Workspace config.WorkspaceConfig `json:"workspace"`
30-}
31-
32-// NewConfigService creates a new ConfigService instance.
33-func NewConfigService(cfg *config.Config, configPath string) *ConfigService {
34- return &ConfigService{
35- config: cfg,
36- configPath: configPath,
37- }
38-}
39-
40-// GetConfigInfo returns comprehensive configuration information.
41-func (s *ConfigService) GetConfigInfo(ctx context.Context) (*ConfigInfo, error) {
42- _ = ctx
43- return &ConfigInfo{
44- ConfigPath: s.configPath,
45- DatabasePath: s.config.Database.Path,
46- SearchShortcuts: s.config.SearchShortcuts,
47- DmenuSettings: &s.config.Dmenu,
48- RenderingMode: string(s.config.RenderingMode),
49- UseDomZoom: s.config.UseDomZoom,
50- Workspace: s.config.Workspace,
51- }, nil
52-}
53-
54-// GetSearchShortcuts returns all configured search shortcuts.
55-func (s *ConfigService) GetSearchShortcuts(ctx context.Context) (map[string]config.SearchShortcut, error) {
56- _ = ctx
57- return s.config.SearchShortcuts, nil
58-}
59-
60-// AddSearchShortcut adds a new search shortcut.
61-func (s *ConfigService) AddSearchShortcut(ctx context.Context, key string, shortcut config.SearchShortcut) error {
62- _ = ctx
63- if key == "" {
64- return fmt.Errorf("shortcut key cannot be empty")
65- }
66-
67- if shortcut.URL == "" {
68- return fmt.Errorf("shortcut URL cannot be empty")
69- }
70-
71- if s.config.SearchShortcuts == nil {
72- s.config.SearchShortcuts = make(map[string]config.SearchShortcut)
73- }
74-
75- s.config.SearchShortcuts[key] = shortcut
76- return s.saveConfig()
77-}
78-
79-// UpdateSearchShortcut updates an existing search shortcut.
80-func (s *ConfigService) UpdateSearchShortcut(ctx context.Context, key string, shortcut config.SearchShortcut) error {
81- _ = ctx
82- if key == "" {
83- return fmt.Errorf("shortcut key cannot be empty")
84- }
85-
86- if _, exists := s.config.SearchShortcuts[key]; !exists {
87- return fmt.Errorf("shortcut '%s' does not exist", key)
88- }
89-
90- if shortcut.URL == "" {
91- return fmt.Errorf("shortcut URL cannot be empty")
92- }
93-
94- s.config.SearchShortcuts[key] = shortcut
95- return s.saveConfig()
96-}
97-
98-// DeleteSearchShortcut removes a search shortcut.
99-func (s *ConfigService) DeleteSearchShortcut(ctx context.Context, key string) error {
100- _ = ctx
101- if key == "" {
102- return fmt.Errorf("shortcut key cannot be empty")
103- }
104-
105- if _, exists := s.config.SearchShortcuts[key]; !exists {
106- return fmt.Errorf("shortcut '%s' does not exist", key)
107- }
108-
109- delete(s.config.SearchShortcuts, key)
110- return s.saveConfig()
111-}
112-
113-// GetDmenuSettings returns current dmenu settings.
114-func (s *ConfigService) GetDmenuSettings(ctx context.Context) (*config.DmenuConfig, error) {
115- _ = ctx
116- return &s.config.Dmenu, nil
117-}
118-
119-// UpdateDmenuSettings updates dmenu configuration.
120-func (s *ConfigService) UpdateDmenuSettings(ctx context.Context, settings *config.DmenuConfig) error {
121- _ = ctx
122- if settings == nil {
123- return fmt.Errorf("dmenu settings cannot be nil")
124- }
125-
126- s.config.Dmenu = *settings
127- return s.saveConfig()
128-}
129-
130-// GetDatabasePath returns the current database path.
131-func (s *ConfigService) GetDatabasePath(ctx context.Context) (string, error) {
132- _ = ctx
133- return s.config.Database.Path, nil
134-}
135-
136-// SetDatabasePath updates the database path.
137-func (s *ConfigService) SetDatabasePath(ctx context.Context, path string) error {
138- _ = ctx
139- if path == "" {
140- return fmt.Errorf("database path cannot be empty")
141- }
142-
143- // Validate that the directory exists or can be created
144- dir := filepath.Dir(path)
145- if err := os.MkdirAll(dir, defaultDirPerm); err != nil {
146- return fmt.Errorf("failed to create database directory: %w", err)
147- }
148-
149- s.config.Database.Path = path
150- return s.saveConfig()
151-}
152-
153-// GetDefaultBrowser returns the default browser setting.
154-func (s *ConfigService) GetDefaultBrowser(ctx context.Context) (string, error) {
155- _ = ctx
156- // Default browser is now built into the app
157- return "dumber (built-in)", nil
158-}
159-
160-// SetDefaultBrowser updates the default browser setting.
161-func (s *ConfigService) SetDefaultBrowser(ctx context.Context, browser string) error {
162- _ = ctx
163- _ = browser
164- // Default browser setting is not needed for built-in browser
165- return fmt.Errorf("default browser is built-in and cannot be changed")
166-}
167-
168-// ReloadConfig reloads configuration from file.
169-func (s *ConfigService) ReloadConfig(ctx context.Context) error {
170- _ = ctx
171- // Create new manager and load config
172- manager, err := config.NewManager()
173- if err != nil {
174- return fmt.Errorf("failed to create config manager: %w", err)
175- }
176-
177- if err := manager.Load(); err != nil {
178- return fmt.Errorf("failed to load config: %w", err)
179- }
180-
181- s.config = manager.Get()
182- return nil
183-}
184-
185-// ValidateConfig validates the current configuration.
186-func (s *ConfigService) ValidateConfig(ctx context.Context) ([]string, error) {
187- _ = ctx
188- var errors []string
189-
190- // Validate database path
191- if s.config.Database.Path == "" {
192- errors = append(errors, "database path is not set")
193- } else {
194- dir := filepath.Dir(s.config.Database.Path)
195- if _, err := os.Stat(dir); os.IsNotExist(err) {
196- errors = append(errors, fmt.Sprintf("database directory does not exist: %s", dir))
197- }
198- }
199-
200- // Validate search shortcuts
201- if len(s.config.SearchShortcuts) == 0 {
202- errors = append(errors, "no search shortcuts configured")
203- } else {
204- for key, shortcut := range s.config.SearchShortcuts {
205- if shortcut.URL == "" {
206- errors = append(errors, fmt.Sprintf("search shortcut '%s' has empty URL", key))
207- }
208- }
209- }
210-
211- // Check for default search shortcut
212- if _, hasDefault := s.config.SearchShortcuts["g"]; !hasDefault {
213- errors = append(errors, "no default search shortcut 'g' configured")
214- }
215-
216- // Validate dmenu settings
217- if s.config.Dmenu.MaxHistoryItems < 1 {
218- errors = append(errors, "dmenu max_history_items must be at least 1")
219- }
220-
221- // Validate workspace configuration when enabled
222- if s.config.Workspace.EnableZellijControls {
223- if strings.TrimSpace(s.config.Workspace.PaneMode.ActivationShortcut) == "" {
224- errors = append(errors, "workspace pane_mode activation_shortcut is empty")
225- }
226- if len(s.config.Workspace.PaneMode.ActionBindings) == 0 {
227- errors = append(errors, "workspace pane_mode action_bindings are empty")
228- }
229- }
230-
231- if strings.TrimSpace(s.config.Workspace.Popups.Placement) == "" {
232- errors = append(errors, "workspace popups placement is empty")
233- }
234-
235- return errors, nil
236-}
237-
238-// GetConfigPath returns the path to the configuration file.
239-func (s *ConfigService) GetConfigPath(ctx context.Context) (string, error) {
240- _ = ctx
241- return s.configPath, nil
242-}
243-
244-// ExportConfig returns the current configuration as JSON.
245-func (s *ConfigService) ExportConfig(ctx context.Context) (*config.Config, error) {
246- _ = ctx
247- // Return a copy of the config to prevent modifications
248- configCopy := *s.config
249- return &configCopy, nil
250-}
251-
252-// ImportConfig imports configuration from a Config object.
253-func (s *ConfigService) ImportConfig(ctx context.Context, newConfig *config.Config) error {
254- if newConfig == nil {
255- return fmt.Errorf("config cannot be nil")
256- }
257-
258- // Validate the new config
259- tempService := &ConfigService{config: newConfig}
260- validationErrors, err := tempService.ValidateConfig(ctx)
261- if err != nil {
262- return fmt.Errorf("failed to validate config: %w", err)
263- }
264-
265- if len(validationErrors) > 0 {
266- return fmt.Errorf("config validation failed: %v", validationErrors)
267- }
268-
269- // Apply the new config
270- s.config = newConfig
271- return s.saveConfig()
272-}
273-
274-// GetRenderingMode returns the current rendering mode (auto|gpu|cpu).
275-func (s *ConfigService) GetRenderingMode(ctx context.Context) (string, error) {
276- _ = ctx
277- return string(s.config.RenderingMode), nil
278-}
279-
280-// SetRenderingMode updates the rendering mode (auto|gpu|cpu).
281-func (s *ConfigService) SetRenderingMode(ctx context.Context, mode string) error {
282- _ = ctx
283- m := strings.ToLower(strings.TrimSpace(mode))
284- switch m {
285- case "auto", "gpu", "cpu":
286- s.config.RenderingMode = config.RenderingMode(m)
287- return s.saveConfig()
288- default:
289- return fmt.Errorf("invalid rendering mode: %s", mode)
290- }
291-}
292-
293-// GetUseDomZoom reports whether DOM-based zoom is enabled.
294-func (s *ConfigService) GetUseDomZoom(ctx context.Context) (bool, error) {
295- _ = ctx
296- return s.config.UseDomZoom, nil
297-}
298-
299-// SetUseDomZoom toggles DOM-based zoom.
300-func (s *ConfigService) SetUseDomZoom(ctx context.Context, enabled bool) error {
301- _ = ctx
302- s.config.UseDomZoom = enabled
303- return s.saveConfig()
304-}
305-
306-// saveConfig saves the current configuration to file.
307-func (s *ConfigService) saveConfig() error {
308- // For now, return success - implementing config saving would require
309- // extending the config package with save functionality
310- return nil
311-}
@@ -1,311 +0,0 @@
1-package services
2-
3-import (
4- "context"
5- "fmt"
6- "os"
7- "path/filepath"
8-
9- "github.com/bnema/dumber/internal/config"
10- "strings"
11-)
12-
13-// ConfigService handles configuration operations for the application.
14-type ConfigService struct {
15- config *config.Config
16- configPath string
17-}
18-
19-const defaultDirPerm = 0o750
20-
21-// ConfigInfo represents configuration information for the frontend.
22-type ConfigInfo struct {
23- ConfigPath string `json:"config_path"`
24- DatabasePath string `json:"database_path"`
25- SearchShortcuts map[string]config.SearchShortcut `json:"search_shortcuts"`
26- DmenuSettings *config.DmenuConfig `json:"dmenu_settings"`
27- RenderingMode string `json:"rendering_mode"`
28- UseDomZoom bool `json:"use_dom_zoom"`
29- Workspace config.WorkspaceConfig `json:"workspace"`
30-}
31-
32-// NewConfigService creates a new ConfigService instance.
33-func NewConfigService(cfg *config.Config, configPath string) *ConfigService {
34- return &ConfigService{
35- config: cfg,
36- configPath: configPath,
37- }
38-}
39-
40-// GetConfigInfo returns comprehensive configuration information.
41-func (s *ConfigService) GetConfigInfo(ctx context.Context) (*ConfigInfo, error) {
42- _ = ctx
43- return &ConfigInfo{
44- ConfigPath: s.configPath,
45- DatabasePath: s.config.Database.Path,
46- SearchShortcuts: s.config.SearchShortcuts,
47- DmenuSettings: &s.config.Dmenu,
48- RenderingMode: string(s.config.RenderingMode),
49- UseDomZoom: s.config.UseDomZoom,
50- Workspace: s.config.Workspace,
51- }, nil
52-}
53-
54-// GetSearchShortcuts returns all configured search shortcuts.
55-func (s *ConfigService) GetSearchShortcuts(ctx context.Context) (map[string]config.SearchShortcut, error) {
56- _ = ctx
57- return s.config.SearchShortcuts, nil
58-}
59-
60-// AddSearchShortcut adds a new search shortcut.
61-func (s *ConfigService) AddSearchShortcut(ctx context.Context, key string, shortcut config.SearchShortcut) error {
62- _ = ctx
63- if key == "" {
64- return fmt.Errorf("shortcut key cannot be empty")
65- }
66-
67- if shortcut.URL == "" {
68- return fmt.Errorf("shortcut URL cannot be empty")
69- }
70-
71- if s.config.SearchShortcuts == nil {
72- s.config.SearchShortcuts = make(map[string]config.SearchShortcut)
73- }
74-
75- s.config.SearchShortcuts[key] = shortcut
76- return s.saveConfig()
77-}
78-
79-// UpdateSearchShortcut updates an existing search shortcut.
80-func (s *ConfigService) UpdateSearchShortcut(ctx context.Context, key string, shortcut config.SearchShortcut) error {
81- _ = ctx
82- if key == "" {
83- return fmt.Errorf("shortcut key cannot be empty")
84- }
85-
86- if _, exists := s.config.SearchShortcuts[key]; !exists {
87- return fmt.Errorf("shortcut '%s' does not exist", key)
88- }
89-
90- if shortcut.URL == "" {
91- return fmt.Errorf("shortcut URL cannot be empty")
92- }
93-
94- s.config.SearchShortcuts[key] = shortcut
95- return s.saveConfig()
96-}
97-
98-// DeleteSearchShortcut removes a search shortcut.
99-func (s *ConfigService) DeleteSearchShortcut(ctx context.Context, key string) error {
100- _ = ctx
101- if key == "" {
102- return fmt.Errorf("shortcut key cannot be empty")
103- }
104-
105- if _, exists := s.config.SearchShortcuts[key]; !exists {
106- return fmt.Errorf("shortcut '%s' does not exist", key)
107- }
108-
109- delete(s.config.SearchShortcuts, key)
110- return s.saveConfig()
111-}
112-
113-// GetDmenuSettings returns current dmenu settings.
114-func (s *ConfigService) GetDmenuSettings(ctx context.Context) (*config.DmenuConfig, error) {
115- _ = ctx
116- return &s.config.Dmenu, nil
117-}
118-
119-// UpdateDmenuSettings updates dmenu configuration.
120-func (s *ConfigService) UpdateDmenuSettings(ctx context.Context, settings *config.DmenuConfig) error {
121- _ = ctx
122- if settings == nil {
123- return fmt.Errorf("dmenu settings cannot be nil")
124- }
125-
126- s.config.Dmenu = *settings
127- return s.saveConfig()
128-}
129-
130-// GetDatabasePath returns the current database path.
131-func (s *ConfigService) GetDatabasePath(ctx context.Context) (string, error) {
132- _ = ctx
133- return s.config.Database.Path, nil
134-}
135-
136-// SetDatabasePath updates the database path.
137-func (s *ConfigService) SetDatabasePath(ctx context.Context, path string) error {
138- _ = ctx
139- if path == "" {
140- return fmt.Errorf("database path cannot be empty")
141- }
142-
143- // Validate that the directory exists or can be created
144- dir := filepath.Dir(path)
145- if err := os.MkdirAll(dir, defaultDirPerm); err != nil {
146- return fmt.Errorf("failed to create database directory: %w", err)
147- }
148-
149- s.config.Database.Path = path
150- return s.saveConfig()
151-}
152-
153-// GetDefaultBrowser returns the default browser setting.
154-func (s *ConfigService) GetDefaultBrowser(ctx context.Context) (string, error) {
155- _ = ctx
156- // Default browser is now built into the app
157- return "dumber (built-in)", nil
158-}
159-
160-// SetDefaultBrowser updates the default browser setting.
161-func (s *ConfigService) SetDefaultBrowser(ctx context.Context, browser string) error {
162- _ = ctx
163- _ = browser
164- // Default browser setting is not needed for built-in browser
165- return fmt.Errorf("default browser is built-in and cannot be changed")
166-}
167-
168-// ReloadConfig reloads configuration from file.
169-func (s *ConfigService) ReloadConfig(ctx context.Context) error {
170- _ = ctx
171- // Create new manager and load config
172- manager, err := config.NewManager()
173- if err != nil {
174- return fmt.Errorf("failed to create config manager: %w", err)
175- }
176-
177- if err := manager.Load(); err != nil {
178- return fmt.Errorf("failed to load config: %w", err)
179- }
180-
181- s.config = manager.Get()
182- return nil
183-}
184-
185-// ValidateConfig validates the current configuration.
186-func (s *ConfigService) ValidateConfig(ctx context.Context) ([]string, error) {
187- _ = ctx
188- var errors []string
189-
190- // Validate database path
191- if s.config.Database.Path == "" {
192- errors = append(errors, "database path is not set")
193- } else {
194- dir := filepath.Dir(s.config.Database.Path)
195- if _, err := os.Stat(dir); os.IsNotExist(err) {
196- errors = append(errors, fmt.Sprintf("database directory does not exist: %s", dir))
197- }
198- }
199-
200- // Validate search shortcuts
201- if len(s.config.SearchShortcuts) == 0 {
202- errors = append(errors, "no search shortcuts configured")
203- } else {
204- for key, shortcut := range s.config.SearchShortcuts {
205- if shortcut.URL == "" {
206- errors = append(errors, fmt.Sprintf("search shortcut '%s' has empty URL", key))
207- }
208- }
209- }
210-
211- // Check for default search shortcut
212- if _, hasDefault := s.config.SearchShortcuts["g"]; !hasDefault {
213- errors = append(errors, "no default search shortcut 'g' configured")
214- }
215-
216- // Validate dmenu settings
217- if s.config.Dmenu.MaxHistoryItems < 1 {
218- errors = append(errors, "dmenu max_history_items must be at least 1")
219- }
220-
221- // Validate workspace configuration when enabled
222- if s.config.Workspace.EnableZellijControls {
223- if strings.TrimSpace(s.config.Workspace.PaneMode.ActivationShortcut) == "" {
224- errors = append(errors, "workspace pane_mode activation_shortcut is empty")
225- }
226- if len(s.config.Workspace.PaneMode.ActionBindings) == 0 {
227- errors = append(errors, "workspace pane_mode action_bindings are empty")
228- }
229- }
230-
231- if strings.TrimSpace(s.config.Workspace.Popups.Placement) == "" {
232- errors = append(errors, "workspace popups placement is empty")
233- }
234-
235- return errors, nil
236-}
237-
238-// GetConfigPath returns the path to the configuration file.
239-func (s *ConfigService) GetConfigPath(ctx context.Context) (string, error) {
240- _ = ctx
241- return s.configPath, nil
242-}
243-
244-// ExportConfig returns the current configuration as JSON.
245-func (s *ConfigService) ExportConfig(ctx context.Context) (*config.Config, error) {
246- _ = ctx
247- // Return a copy of the config to prevent modifications
248- configCopy := *s.config
249- return &configCopy, nil
250-}
251-
252-// ImportConfig imports configuration from a Config object.
253-func (s *ConfigService) ImportConfig(ctx context.Context, newConfig *config.Config) error {
254- if newConfig == nil {
255- return fmt.Errorf("config cannot be nil")
256- }
257-
258- // Validate the new config
259- tempService := &ConfigService{config: newConfig}
260- validationErrors, err := tempService.ValidateConfig(ctx)
261- if err != nil {
262- return fmt.Errorf("failed to validate config: %w", err)
263- }
264-
265- if len(validationErrors) > 0 {
266- return fmt.Errorf("config validation failed: %v", validationErrors)
267- }
268-
269- // Apply the new config
270- s.config = newConfig
271- return s.saveConfig()
272-}
273-
274-// GetRenderingMode returns the current rendering mode (auto|gpu|cpu).
275-func (s *ConfigService) GetRenderingMode(ctx context.Context) (string, error) {
276- _ = ctx
277- return string(s.config.RenderingMode), nil
278-}
279-
280-// SetRenderingMode updates the rendering mode (auto|gpu|cpu).
281-func (s *ConfigService) SetRenderingMode(ctx context.Context, mode string) error {
282- _ = ctx
283- m := strings.ToLower(strings.TrimSpace(mode))
284- switch m {
285- case "auto", "gpu", "cpu":
286- s.config.RenderingMode = config.RenderingMode(m)
287- return s.saveConfig()
288- default:
289- return fmt.Errorf("invalid rendering mode: %s", mode)
290- }
291-}
292-
293-// GetUseDomZoom reports whether DOM-based zoom is enabled.
294-func (s *ConfigService) GetUseDomZoom(ctx context.Context) (bool, error) {
295- _ = ctx
296- return s.config.UseDomZoom, nil
297-}
298-
299-// SetUseDomZoom toggles DOM-based zoom.
300-func (s *ConfigService) SetUseDomZoom(ctx context.Context, enabled bool) error {
301- _ = ctx
302- s.config.UseDomZoom = enabled
303- return s.saveConfig()
304-}
305-
306-// saveConfig saves the current configuration to file.
307-func (s *ConfigService) saveConfig() error {
308- // For now, return success - implementing config saving would require
309- // extending the config package with save functionality
310- return nil
311-}
M tests/contract/browser_controls_test.go +1 -1
@@ -9,7 +9,7 @@ import (
99
1010 "github.com/bnema/dumber/internal/config"
1111 mock_db "github.com/bnema/dumber/internal/db/mocks"
12- "github.com/bnema/dumber/services"
12+ "github.com/bnema/dumber/internal/services"
1313 "github.com/golang/mock/gomock"
1414 )
1515
@@ -9,7 +9,7 @@ import (
9 9
10 "github.com/bnema/dumber/internal/config"10 "github.com/bnema/dumber/internal/config"
11 mock_db "github.com/bnema/dumber/internal/db/mocks"11 mock_db "github.com/bnema/dumber/internal/db/mocks"
12- "github.com/bnema/dumber/services"12+ "github.com/bnema/dumber/internal/services"
13 "github.com/golang/mock/gomock"13 "github.com/golang/mock/gomock"
14 )14 )
15 15
M tests/integration/browser_controls_integration_test.go +1 -1
@@ -12,7 +12,7 @@ import (
1212
1313 "github.com/bnema/dumber/internal/config"
1414 "github.com/bnema/dumber/internal/db"
15- "github.com/bnema/dumber/services"
15+ "github.com/bnema/dumber/internal/services"
1616 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver
1717 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite
1818 )
@@ -12,7 +12,7 @@ import (
12 12
13 "github.com/bnema/dumber/internal/config"13 "github.com/bnema/dumber/internal/config"
14 "github.com/bnema/dumber/internal/db"14 "github.com/bnema/dumber/internal/db"
15- "github.com/bnema/dumber/services"15+ "github.com/bnema/dumber/internal/services"
16 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver16 _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver
17 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite17 _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite
18 )18 )

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