feat(config): add configurable default zoom level
Added default_zoom config option to set the default zoom level for pages without saved zoom preferences. Defaults to 1.2 (120%) for better readability. Per-domain zoom settings still override this default.
- commit
- bc5cb18cc7b3ad7165f86cbd9ee72464ea609cb6
- author
- bamen · · 2025-10-06 16:56 UTC
- committer
- bamen · · 2025-10-06 17:30 UTC
- parent
- 43d9ee2
- signature
- unsigned
5 files · +38 -5
M CHANGELOG.md +1 -0
| @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. | |||
| 5 | 5 | ## [Unreleased] | |
| 6 | 6 | ||
| 7 | 7 | ### Added | |
| 8 | + | - **Configurable default zoom**: Added `default_zoom` config (defaults to 1.2/120%). Per-domain zoom still overrides this | |
| 8 | 9 | - **Pin/favorite sites**: Pin websites from history or quick access to prioritize them in "Jump back in" section. Uses localStorage persistence with star icons for pin/unpin actions | |
| 9 | 10 | - **Zellij-style stacked panes**: Added Ctrl+P → 's' to stack panes instead of splitting, with Alt+Up/Down navigation between stacked panes. Features collapsed page title bars showing for inactive panes and full WebView interaction for the active pane. Includes GTK4 CSS styling for proper visual feedback | |
| 10 | 11 | - **Stacked pane favicons**: Title bars display site favicons with async caching. Click title bars to switch between stacked panes | |
| @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. | |||||
| 5 | ## [Unreleased] | 5 | ## [Unreleased] | ||
| 6 | 6 | ||||
| 7 | ### Added | 7 | ### Added | ||
| 8 | + | - **Configurable default zoom**: Added `default_zoom` config (defaults to 1.2/120%). Per-domain zoom still overrides this | |||
| 8 | - **Pin/favorite sites**: Pin websites from history or quick access to prioritize them in "Jump back in" section. Uses localStorage persistence with star icons for pin/unpin actions | 9 | - **Pin/favorite sites**: Pin websites from history or quick access to prioritize them in "Jump back in" section. Uses localStorage persistence with star icons for pin/unpin actions | ||
| 9 | - **Zellij-style stacked panes**: Added Ctrl+P → 's' to stack panes instead of splitting, with Alt+Up/Down navigation between stacked panes. Features collapsed page title bars showing for inactive panes and full WebView interaction for the active pane. Includes GTK4 CSS styling for proper visual feedback | 10 | - **Zellij-style stacked panes**: Added Ctrl+P → 's' to stack panes instead of splitting, with Alt+Up/Down navigation between stacked panes. Features collapsed page title bars showing for inactive panes and full WebView interaction for the active pane. Includes GTK4 CSS styling for proper visual feedback | ||
| 10 | - **Stacked pane favicons**: Title bars display site favicons with async caching. Click title bars to switch between stacked panes | 11 | - **Stacked pane favicons**: Title bars display site favicons with async caching. Click title bars to switch between stacked panes | ||
M internal/app/browser/webview.go +1 -1
| @@ -86,7 +86,7 @@ func (app *BrowserApp) buildWebkitConfig() (*webkit.Config, error) { | |||
| 86 | 86 | cfg := &webkit.Config{ | |
| 87 | 87 | Assets: app.assets, | |
| 88 | 88 | InitialURL: "dumb://homepage", | |
| 89 | - | ZoomDefault: 1.0, | |
| 89 | + | ZoomDefault: app.config.DefaultZoom, | |
| 90 | 90 | EnableDeveloperExtras: true, | |
| 91 | 91 | DataDir: webkitData, | |
| 92 | 92 | CacheDir: webkitCache, | |
| @@ -86,7 +86,7 @@ func (app *BrowserApp) buildWebkitConfig() (*webkit.Config, error) { | |||||
| 86 | cfg := &webkit.Config{ | 86 | cfg := &webkit.Config{ | ||
| 87 | Assets: app.assets, | 87 | Assets: app.assets, | ||
| 88 | InitialURL: "dumb://homepage", | 88 | InitialURL: "dumb://homepage", | ||
| 89 | - | ZoomDefault: 1.0, | 89 | + | ZoomDefault: app.config.DefaultZoom, |
| 90 | EnableDeveloperExtras: true, | 90 | EnableDeveloperExtras: true, | ||
| 91 | DataDir: webkitData, | 91 | DataDir: webkitData, | ||
| 92 | CacheDir: webkitCache, | 92 | CacheDir: webkitCache, | ||
M internal/config/config.go +31 -0
| @@ -40,6 +40,8 @@ type Config struct { | |||
| 40 | 40 | RenderingMode RenderingMode `mapstructure:"rendering_mode" yaml:"rendering_mode"` | |
| 41 | 41 | // UseDomZoom toggles DOM-based zoom instead of native WebKit zoom. | |
| 42 | 42 | UseDomZoom bool `mapstructure:"use_dom_zoom" yaml:"use_dom_zoom"` | |
| 43 | + | // DefaultZoom sets the default zoom level for pages without saved zoom settings (1.0 = 100%, 1.2 = 120%) | |
| 44 | + | DefaultZoom float64 `mapstructure:"default_zoom" yaml:"default_zoom"` | |
| 43 | 45 | // Workspace defines workspace, pane, and tab handling behaviour. | |
| 44 | 46 | Workspace WorkspaceConfig `mapstructure:"workspace" yaml:"workspace"` | |
| 45 | 47 | } | |
| @@ -381,6 +383,9 @@ func NewManager() (*Manager, error) { | |||
| 381 | 383 | if err := v.BindEnv("use_dom_zoom", "DUMBER_USE_DOM_ZOOM"); err != nil { | |
| 382 | 384 | return nil, fmt.Errorf("failed to bind DUMBER_USE_DOM_ZOOM: %w", err) | |
| 383 | 385 | } | |
| 386 | + | if err := v.BindEnv("default_zoom", "DUMBER_DEFAULT_ZOOM"); err != nil { | |
| 387 | + | return nil, fmt.Errorf("failed to bind DUMBER_DEFAULT_ZOOM: %w", err) | |
| 388 | + | } | |
| 384 | 389 | ||
| 385 | 390 | // Video acceleration environment variable bindings | |
| 386 | 391 | videoAccelEnvBindings := map[string]string{ | |
| @@ -716,6 +721,7 @@ func (m *Manager) setDefaults() { | |||
| 716 | 721 | // Rendering defaults | |
| 717 | 722 | m.viper.SetDefault("rendering_mode", string(RenderingModeGPU)) | |
| 718 | 723 | m.viper.SetDefault("use_dom_zoom", defaults.UseDomZoom) | |
| 724 | + | m.viper.SetDefault("default_zoom", defaults.DefaultZoom) | |
| 719 | 725 | ||
| 720 | 726 | // Workspace defaults | |
| 721 | 727 | m.viper.SetDefault("workspace.enable_zellij_controls", defaults.Workspace.EnableZellijControls) | |
| @@ -767,6 +773,31 @@ func (m *Manager) ensurePersistedDefaults(cfg *Config) { | |||
| 767 | 773 | logging.Info(fmt.Sprintf("Config: persisted missing use_dom_zoom default (%v) to %s", cfg.UseDomZoom, cfgFile)) | |
| 768 | 774 | } | |
| 769 | 775 | ||
| 776 | + | // persistDefaultZoomIfMissing persists the default_zoom value to the config file if it's not already set. | |
| 777 | + | func (m *Manager) persistDefaultZoomIfMissing(cfg *Config) { | |
| 778 | + | if cfg == nil { | |
| 779 | + | return | |
| 780 | + | } | |
| 781 | + | ||
| 782 | + | // Only persist when the key is missing from the user config file. | |
| 783 | + | if m.viper.InConfig("default_zoom") { | |
| 784 | + | return | |
| 785 | + | } | |
| 786 | + | ||
| 787 | + | cfgFile := m.viper.ConfigFileUsed() | |
| 788 | + | if cfgFile == "" { | |
| 789 | + | return | |
| 790 | + | } | |
| 791 | + | ||
| 792 | + | m.viper.Set("default_zoom", cfg.DefaultZoom) | |
| 793 | + | if err := m.viper.WriteConfig(); err != nil { | |
| 794 | + | logging.Warn(fmt.Sprintf("Config: failed to persist default_zoom default to %s: %v", cfgFile, err)) | |
| 795 | + | return | |
| 796 | + | } | |
| 797 | + | ||
| 798 | + | logging.Info(fmt.Sprintf("Config: persisted missing default_zoom default (%.2f) to %s", cfg.DefaultZoom, cfgFile)) | |
| 799 | + | } | |
| 800 | + | ||
| 770 | 801 | // createDefaultConfig creates a default configuration file. | |
| 771 | 802 | func (m *Manager) createDefaultConfig() error { | |
| 772 | 803 | configFile, err := GetConfigFile() | |
| @@ -40,6 +40,8 @@ type Config struct { | |||||
| 40 | RenderingMode RenderingMode `mapstructure:"rendering_mode" yaml:"rendering_mode"` | 40 | RenderingMode RenderingMode `mapstructure:"rendering_mode" yaml:"rendering_mode"` | ||
| 41 | // UseDomZoom toggles DOM-based zoom instead of native WebKit zoom. | 41 | // UseDomZoom toggles DOM-based zoom instead of native WebKit zoom. | ||
| 42 | UseDomZoom bool `mapstructure:"use_dom_zoom" yaml:"use_dom_zoom"` | 42 | UseDomZoom bool `mapstructure:"use_dom_zoom" yaml:"use_dom_zoom"` | ||
| 43 | + | // DefaultZoom sets the default zoom level for pages without saved zoom settings (1.0 = 100%, 1.2 = 120%) | |||
| 44 | + | DefaultZoom float64 `mapstructure:"default_zoom" yaml:"default_zoom"` | |||
| 43 | // Workspace defines workspace, pane, and tab handling behaviour. | 45 | // Workspace defines workspace, pane, and tab handling behaviour. | ||
| 44 | Workspace WorkspaceConfig `mapstructure:"workspace" yaml:"workspace"` | 46 | Workspace WorkspaceConfig `mapstructure:"workspace" yaml:"workspace"` | ||
| 45 | } | 47 | } | ||
| @@ -381,6 +383,9 @@ func NewManager() (*Manager, error) { | |||||
| 381 | if err := v.BindEnv("use_dom_zoom", "DUMBER_USE_DOM_ZOOM"); err != nil { | 383 | if err := v.BindEnv("use_dom_zoom", "DUMBER_USE_DOM_ZOOM"); err != nil { | ||
| 382 | return nil, fmt.Errorf("failed to bind DUMBER_USE_DOM_ZOOM: %w", err) | 384 | return nil, fmt.Errorf("failed to bind DUMBER_USE_DOM_ZOOM: %w", err) | ||
| 383 | } | 385 | } | ||
| 386 | + | if err := v.BindEnv("default_zoom", "DUMBER_DEFAULT_ZOOM"); err != nil { | |||
| 387 | + | return nil, fmt.Errorf("failed to bind DUMBER_DEFAULT_ZOOM: %w", err) | |||
| 388 | + | } | |||
| 384 | 389 | ||||
| 385 | // Video acceleration environment variable bindings | 390 | // Video acceleration environment variable bindings | ||
| 386 | videoAccelEnvBindings := map[string]string{ | 391 | videoAccelEnvBindings := map[string]string{ | ||
| @@ -716,6 +721,7 @@ func (m *Manager) setDefaults() { | |||||
| 716 | // Rendering defaults | 721 | // Rendering defaults | ||
| 717 | m.viper.SetDefault("rendering_mode", string(RenderingModeGPU)) | 722 | m.viper.SetDefault("rendering_mode", string(RenderingModeGPU)) | ||
| 718 | m.viper.SetDefault("use_dom_zoom", defaults.UseDomZoom) | 723 | m.viper.SetDefault("use_dom_zoom", defaults.UseDomZoom) | ||
| 724 | + | m.viper.SetDefault("default_zoom", defaults.DefaultZoom) | |||
| 719 | 725 | ||||
| 720 | // Workspace defaults | 726 | // Workspace defaults | ||
| 721 | m.viper.SetDefault("workspace.enable_zellij_controls", defaults.Workspace.EnableZellijControls) | 727 | m.viper.SetDefault("workspace.enable_zellij_controls", defaults.Workspace.EnableZellijControls) | ||
| @@ -767,6 +773,31 @@ func (m *Manager) ensurePersistedDefaults(cfg *Config) { | |||||
| 767 | logging.Info(fmt.Sprintf("Config: persisted missing use_dom_zoom default (%v) to %s", cfg.UseDomZoom, cfgFile)) | 773 | logging.Info(fmt.Sprintf("Config: persisted missing use_dom_zoom default (%v) to %s", cfg.UseDomZoom, cfgFile)) | ||
| 768 | } | 774 | } | ||
| 769 | 775 | ||||
| 776 | + | // persistDefaultZoomIfMissing persists the default_zoom value to the config file if it's not already set. | |||
| 777 | + | func (m *Manager) persistDefaultZoomIfMissing(cfg *Config) { | |||
| 778 | + | if cfg == nil { | |||
| 779 | + | return | |||
| 780 | + | } | |||
| 781 | + | ||||
| 782 | + | // Only persist when the key is missing from the user config file. | |||
| 783 | + | if m.viper.InConfig("default_zoom") { | |||
| 784 | + | return | |||
| 785 | + | } | |||
| 786 | + | ||||
| 787 | + | cfgFile := m.viper.ConfigFileUsed() | |||
| 788 | + | if cfgFile == "" { | |||
| 789 | + | return | |||
| 790 | + | } | |||
| 791 | + | ||||
| 792 | + | m.viper.Set("default_zoom", cfg.DefaultZoom) | |||
| 793 | + | if err := m.viper.WriteConfig(); err != nil { | |||
| 794 | + | logging.Warn(fmt.Sprintf("Config: failed to persist default_zoom default to %s: %v", cfgFile, err)) | |||
| 795 | + | return | |||
| 796 | + | } | |||
| 797 | + | ||||
| 798 | + | logging.Info(fmt.Sprintf("Config: persisted missing default_zoom default (%.2f) to %s", cfg.DefaultZoom, cfgFile)) | |||
| 799 | + | } | |||
| 800 | + | ||||
| 770 | // createDefaultConfig creates a default configuration file. | 801 | // createDefaultConfig creates a default configuration file. | ||
| 771 | func (m *Manager) createDefaultConfig() error { | 802 | func (m *Manager) createDefaultConfig() error { | ||
| 772 | configFile, err := GetConfigFile() | 803 | configFile, err := GetConfigFile() | ||
M internal/config/defaults.go +1 -0
| @@ -164,6 +164,7 @@ func DefaultConfig() *Config { | |||
| 164 | 164 | }, | |
| 165 | 165 | RenderingMode: RenderingModeGPU, | |
| 166 | 166 | UseDomZoom: false, | |
| 167 | + | DefaultZoom: 1.2, // 120% default zoom for better readability | |
| 167 | 168 | Workspace: WorkspaceConfig{ | |
| 168 | 169 | EnableZellijControls: true, | |
| 169 | 170 | PaneMode: PaneModeConfig{ | |
| @@ -164,6 +164,7 @@ func DefaultConfig() *Config { | |||||
| 164 | }, | 164 | }, | ||
| 165 | RenderingMode: RenderingModeGPU, | 165 | RenderingMode: RenderingModeGPU, | ||
| 166 | UseDomZoom: false, | 166 | UseDomZoom: false, | ||
| 167 | + | DefaultZoom: 1.2, // 120% default zoom for better readability | |||
| 167 | Workspace: WorkspaceConfig{ | 168 | Workspace: WorkspaceConfig{ | ||
| 168 | EnableZellijControls: true, | 169 | EnableZellijControls: true, | ||
| 169 | PaneMode: PaneModeConfig{ | 170 | PaneMode: PaneModeConfig{ | ||
M internal/services/browser.go +4 -4
| @@ -446,7 +446,7 @@ func (s *BrowserService) ResetZoom(ctx context.Context, url string) (float64, er | |||
| 446 | 446 | // GetZoomLevel retrieves the saved zoom level for a URL. | |
| 447 | 447 | func (s *BrowserService) GetZoomLevel(ctx context.Context, url string) (float64, error) { | |
| 448 | 448 | if url == "" { | |
| 449 | - | return 1.0, nil | |
| 449 | + | return s.config.DefaultZoom, nil | |
| 450 | 450 | } | |
| 451 | 451 | ||
| 452 | 452 | key := zoomKeyFromURL(url) | |
| @@ -458,10 +458,10 @@ func (s *BrowserService) GetZoomLevel(ctx context.Context, url string) (float64, | |||
| 458 | 458 | if err2 == nil { | |
| 459 | 459 | return zl, nil | |
| 460 | 460 | } | |
| 461 | - | // No zoom setting found, return default | |
| 462 | - | return 1.0, nil | |
| 461 | + | // No zoom setting found, return configured default | |
| 462 | + | return s.config.DefaultZoom, nil | |
| 463 | 463 | } | |
| 464 | - | return 1.0, err | |
| 464 | + | return s.config.DefaultZoom, err | |
| 465 | 465 | } | |
| 466 | 466 | ||
| 467 | 467 | return zoomLevel, nil | |
| @@ -446,7 +446,7 @@ func (s *BrowserService) ResetZoom(ctx context.Context, url string) (float64, er | |||||
| 446 | // GetZoomLevel retrieves the saved zoom level for a URL. | 446 | // GetZoomLevel retrieves the saved zoom level for a URL. | ||
| 447 | func (s *BrowserService) GetZoomLevel(ctx context.Context, url string) (float64, error) { | 447 | func (s *BrowserService) GetZoomLevel(ctx context.Context, url string) (float64, error) { | ||
| 448 | if url == "" { | 448 | if url == "" { | ||
| 449 | - | return 1.0, nil | 449 | + | return s.config.DefaultZoom, nil |
| 450 | } | 450 | } | ||
| 451 | 451 | ||||
| 452 | key := zoomKeyFromURL(url) | 452 | key := zoomKeyFromURL(url) | ||
| @@ -458,10 +458,10 @@ func (s *BrowserService) GetZoomLevel(ctx context.Context, url string) (float64, | |||||
| 458 | if err2 == nil { | 458 | if err2 == nil { | ||
| 459 | return zl, nil | 459 | return zl, nil | ||
| 460 | } | 460 | } | ||
| 461 | - | // No zoom setting found, return default | 461 | + | // No zoom setting found, return configured default |
| 462 | - | return 1.0, nil | 462 | + | return s.config.DefaultZoom, nil |
| 463 | } | 463 | } | ||
| 464 | - | return 1.0, err | 464 | + | return s.config.DefaultZoom, err |
| 465 | } | 465 | } | ||
| 466 | 466 | ||||
| 467 | return zoomLevel, nil | 467 | return zoomLevel, nil | ||