refactor(config): move defaults to workspace and omnibox sections
- commit
- 8b8ac5d3894e1d51ec5f248189aa42b52a073508
- author
- brice · · 2025-12-26 10:23 UTC
- parent
- 9481ec4
- signature
- unsigned
8 files · +43 -58
M CHANGELOG.md +1 -1
| @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. | |||
| 5 | 5 | ## [Unreleased] | |
| 6 | 6 | ||
| 7 | 7 | ### Added | |
| 8 | - | - **Configurable new-pane defaults**: New `defaults.new_pane_url` (default: `about:blank`, supports `dumb://`) and `defaults.auto_open_omnibox_on_new_pane` (default: false). | |
| 8 | + | - **Configurable new-pane defaults**: New `workspace.new_pane_url` (default: `about:blank`, supports `dumb://`) and `omnibox.auto_open_on_new_pane` (default: false). | |
| 9 | 9 | - **Zellij-style pane resize mode**: New modal resize mode (`Ctrl+N`) to adjust split sizes with keyboard (`h/j/k/l` or arrow keys, `+/-` smart resize). Shows a per-pane border indicator and keeps the timeout alive while you resize. | |
| 10 | 10 | - **Mouse-driven pane resizing**: Drag split dividers with the mouse; split ratios are persisted in session snapshots (rounded to 2 decimals) and restored on resurrection. | |
| 11 | 11 | - **Dynamic window title**: Window title now displays the active pane's page title in format `<Page Title> - Dumber`. Updates when switching tabs/panes or navigating. Truncated at 255 characters. | |
| @@ -5,7 +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 new-pane defaults**: New `defaults.new_pane_url` (default: `about:blank`, supports `dumb://`) and `defaults.auto_open_omnibox_on_new_pane` (default: false). | 8 | + | - **Configurable new-pane defaults**: New `workspace.new_pane_url` (default: `about:blank`, supports `dumb://`) and `omnibox.auto_open_on_new_pane` (default: false). |
| 9 | - **Zellij-style pane resize mode**: New modal resize mode (`Ctrl+N`) to adjust split sizes with keyboard (`h/j/k/l` or arrow keys, `+/-` smart resize). Shows a per-pane border indicator and keeps the timeout alive while you resize. | 9 | - **Zellij-style pane resize mode**: New modal resize mode (`Ctrl+N`) to adjust split sizes with keyboard (`h/j/k/l` or arrow keys, `+/-` smart resize). Shows a per-pane border indicator and keeps the timeout alive while you resize. | ||
| 10 | - **Mouse-driven pane resizing**: Drag split dividers with the mouse; split ratios are persisted in session snapshots (rounded to 2 decimals) and restored on resurrection. | 10 | - **Mouse-driven pane resizing**: Drag split dividers with the mouse; split ratios are persisted in session snapshots (rounded to 2 decimals) and restored on resurrection. | ||
| 11 | - **Dynamic window title**: Window title now displays the active pane's page title in format `<Page Title> - Dumber`. Updates when switching tabs/panes or navigating. Truncated at 255 characters. | 11 | - **Dynamic window title**: Window title now displays the active pane's page title in format `<Page Title> - Dumber`. Updates when switching tabs/panes or navigating. Truncated at 255 characters. | ||
M docs/CONFIG.md +14 -14
| @@ -110,31 +110,19 @@ dumber dmenu --max 50 | |||
| 110 | 110 | | Key | Type | Default | Valid Values | Description | | |
| 111 | 111 | |-----|------|---------|--------------|-------------| | |
| 112 | 112 | | `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior | | |
| 113 | + | | `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane | | |
| 113 | 114 | ||
| 114 | 115 | **Example:** | |
| 115 | 116 | ```toml | |
| 116 | 117 | [omnibox] | |
| 117 | 118 | initial_behavior = "recent" # Show recent history when omnibox opens | |
| 119 | + | auto_open_on_new_pane = false | |
| 118 | 120 | ||
| 119 | 121 | # Alternative options: | |
| 120 | 122 | # initial_behavior = "most_visited" # Show most visited sites | |
| 121 | 123 | # initial_behavior = "none" # Show no initial suggestions | |
| 122 | 124 | ``` | |
| 123 | 125 | ||
| 124 | - | ## Defaults | |
| 125 | - | ||
| 126 | - | | Key | Type | Default | Description | | |
| 127 | - | |-----|------|---------|-------------| | |
| 128 | - | | `defaults.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) | | |
| 129 | - | | `defaults.auto_open_omnibox_on_new_pane` | bool | `false` | Automatically open the omnibox after creating a new pane | | |
| 130 | - | ||
| 131 | - | **Example:** | |
| 132 | - | ```toml | |
| 133 | - | [defaults] | |
| 134 | - | new_pane_url = "dumb://home" | |
| 135 | - | auto_open_omnibox_on_new_pane = false | |
| 136 | - | ``` | |
| 137 | - | ||
| 138 | 126 | ## Logging | |
| 139 | 127 | ||
| 140 | 128 | | Key | Type | Default | Valid Values | Description | | |
| @@ -208,6 +196,18 @@ border = "#363636" | |||
| 208 | 196 | ||
| 209 | 197 | ## Workspace Configuration | |
| 210 | 198 | ||
| 199 | + | ### General | |
| 200 | + | ||
| 201 | + | | Key | Type | Default | Description | | |
| 202 | + | |-----|------|---------|-------------| | |
| 203 | + | | `workspace.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) | | |
| 204 | + | ||
| 205 | + | **Example:** | |
| 206 | + | ```toml | |
| 207 | + | [workspace] | |
| 208 | + | new_pane_url = "dumb://home" | |
| 209 | + | ``` | |
| 210 | + | ||
| 211 | 211 | ### Pane Mode | |
| 212 | 212 | ||
| 213 | 213 | | Key | Type | Default | Description | | |
| @@ -110,31 +110,19 @@ dumber dmenu --max 50 | |||||
| 110 | | Key | Type | Default | Valid Values | Description | | 110 | | Key | Type | Default | Valid Values | Description | | ||
| 111 | |-----|------|---------|--------------|-------------| | 111 | |-----|------|---------|--------------|-------------| | ||
| 112 | | `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior | | 112 | | `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior | | ||
| 113 | + | | `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane | | |||
| 113 | 114 | ||||
| 114 | **Example:** | 115 | **Example:** | ||
| 115 | ```toml | 116 | ```toml | ||
| 116 | [omnibox] | 117 | [omnibox] | ||
| 117 | initial_behavior = "recent" # Show recent history when omnibox opens | 118 | initial_behavior = "recent" # Show recent history when omnibox opens | ||
| 119 | + | auto_open_on_new_pane = false | |||
| 118 | 120 | ||||
| 119 | # Alternative options: | 121 | # Alternative options: | ||
| 120 | # initial_behavior = "most_visited" # Show most visited sites | 122 | # initial_behavior = "most_visited" # Show most visited sites | ||
| 121 | # initial_behavior = "none" # Show no initial suggestions | 123 | # initial_behavior = "none" # Show no initial suggestions | ||
| 122 | ``` | 124 | ``` | ||
| 123 | 125 | ||||
| 124 | - | ## Defaults | |||
| 125 | - | ||||
| 126 | - | | Key | Type | Default | Description | | |||
| 127 | - | |-----|------|---------|-------------| | |||
| 128 | - | | `defaults.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) | | |||
| 129 | - | | `defaults.auto_open_omnibox_on_new_pane` | bool | `false` | Automatically open the omnibox after creating a new pane | | |||
| 130 | - | ||||
| 131 | - | **Example:** | |||
| 132 | - | ```toml | |||
| 133 | - | [defaults] | |||
| 134 | - | new_pane_url = "dumb://home" | |||
| 135 | - | auto_open_omnibox_on_new_pane = false | |||
| 136 | - | ``` | |||
| 137 | - | ||||
| 138 | ## Logging | 126 | ## Logging | ||
| 139 | 127 | ||||
| 140 | | Key | Type | Default | Valid Values | Description | | 128 | | Key | Type | Default | Valid Values | Description | | ||
| @@ -208,6 +196,18 @@ border = "#363636" | |||||
| 208 | 196 | ||||
| 209 | ## Workspace Configuration | 197 | ## Workspace Configuration | ||
| 210 | 198 | ||||
| 199 | + | ### General | |||
| 200 | + | ||||
| 201 | + | | Key | Type | Default | Description | | |||
| 202 | + | |-----|------|---------|-------------| | |||
| 203 | + | | `workspace.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) | | |||
| 204 | + | ||||
| 205 | + | **Example:** | |||
| 206 | + | ```toml | |||
| 207 | + | [workspace] | |||
| 208 | + | new_pane_url = "dumb://home" | |||
| 209 | + | ``` | |||
| 210 | + | ||||
| 211 | ### Pane Mode | 211 | ### Pane Mode | ||
| 212 | 212 | ||||
| 213 | | Key | Type | Default | Description | | 213 | | Key | Type | Default | Description | | ||
M internal/infrastructure/config/defaults.go +7 -9
| @@ -15,12 +15,12 @@ const ( | |||
| 15 | 15 | // Appearance defaults | |
| 16 | 16 | defaultFontSize = 16 // points | |
| 17 | 17 | ||
| 18 | - | // Defaults | |
| 19 | - | defaultNewPaneURL = "about:blank" | |
| 20 | - | defaultAutoOpenOmniboxOnNewPane = false | |
| 18 | + | // Workspace defaults | |
| 19 | + | defaultNewPaneURL = "about:blank" | |
| 21 | 20 | ||
| 22 | 21 | // Omnibox defaults | |
| 23 | - | defaultOmniboxInitialBehavior = "recent" | |
| 22 | + | defaultOmniboxInitialBehavior = "recent" | |
| 23 | + | defaultOmniboxAutoOpenOnNewPane = false | |
| 24 | 24 | ||
| 25 | 25 | // Workspace defaults | |
| 26 | 26 | defaultPaneActivationShortcut = "ctrl+p" | |
| @@ -154,11 +154,8 @@ func DefaultConfig() *Config { | |||
| 154 | 154 | }, | |
| 155 | 155 | DefaultWebpageZoom: 1.2, // 120% default zoom for better readability | |
| 156 | 156 | DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200% | |
| 157 | - | Defaults: DefaultsConfig{ | |
| 158 | - | NewPaneURL: defaultNewPaneURL, | |
| 159 | - | AutoOpenOmniboxOnNewPane: defaultAutoOpenOmniboxOnNewPane, | |
| 160 | - | }, | |
| 161 | 157 | Workspace: WorkspaceConfig{ | |
| 158 | + | NewPaneURL: defaultNewPaneURL, | |
| 162 | 159 | PaneMode: PaneModeConfig{ | |
| 163 | 160 | ActivationShortcut: defaultPaneActivationShortcut, | |
| 164 | 161 | TimeoutMilliseconds: defaultPaneTimeoutMilliseconds, | |
| @@ -245,7 +242,8 @@ func DefaultConfig() *Config { | |||
| 245 | 242 | AutoUpdate: true, // Auto-update filters from GitHub releases | |
| 246 | 243 | }, | |
| 247 | 244 | Omnibox: OmniboxConfig{ | |
| 248 | - | InitialBehavior: defaultOmniboxInitialBehavior, | |
| 245 | + | InitialBehavior: defaultOmniboxInitialBehavior, | |
| 246 | + | AutoOpenOnNewPane: defaultOmniboxAutoOpenOnNewPane, | |
| 249 | 247 | }, | |
| 250 | 248 | Session: SessionConfig{ | |
| 251 | 249 | AutoRestore: false, | |
| @@ -15,12 +15,12 @@ const ( | |||||
| 15 | // Appearance defaults | 15 | // Appearance defaults | ||
| 16 | defaultFontSize = 16 // points | 16 | defaultFontSize = 16 // points | ||
| 17 | 17 | ||||
| 18 | - | // Defaults | 18 | + | // Workspace defaults |
| 19 | - | defaultNewPaneURL = "about:blank" | 19 | + | defaultNewPaneURL = "about:blank" |
| 20 | - | defaultAutoOpenOmniboxOnNewPane = false | |||
| 21 | 20 | ||||
| 22 | // Omnibox defaults | 21 | // Omnibox defaults | ||
| 23 | - | defaultOmniboxInitialBehavior = "recent" | 22 | + | defaultOmniboxInitialBehavior = "recent" |
| 23 | + | defaultOmniboxAutoOpenOnNewPane = false | |||
| 24 | 24 | ||||
| 25 | // Workspace defaults | 25 | // Workspace defaults | ||
| 26 | defaultPaneActivationShortcut = "ctrl+p" | 26 | defaultPaneActivationShortcut = "ctrl+p" | ||
| @@ -154,11 +154,8 @@ func DefaultConfig() *Config { | |||||
| 154 | }, | 154 | }, | ||
| 155 | DefaultWebpageZoom: 1.2, // 120% default zoom for better readability | 155 | DefaultWebpageZoom: 1.2, // 120% default zoom for better readability | ||
| 156 | DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200% | 156 | DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200% | ||
| 157 | - | Defaults: DefaultsConfig{ | |||
| 158 | - | NewPaneURL: defaultNewPaneURL, | |||
| 159 | - | AutoOpenOmniboxOnNewPane: defaultAutoOpenOmniboxOnNewPane, | |||
| 160 | - | }, | |||
| 161 | Workspace: WorkspaceConfig{ | 157 | Workspace: WorkspaceConfig{ | ||
| 158 | + | NewPaneURL: defaultNewPaneURL, | |||
| 162 | PaneMode: PaneModeConfig{ | 159 | PaneMode: PaneModeConfig{ | ||
| 163 | ActivationShortcut: defaultPaneActivationShortcut, | 160 | ActivationShortcut: defaultPaneActivationShortcut, | ||
| 164 | TimeoutMilliseconds: defaultPaneTimeoutMilliseconds, | 161 | TimeoutMilliseconds: defaultPaneTimeoutMilliseconds, | ||
| @@ -245,7 +242,8 @@ func DefaultConfig() *Config { | |||||
| 245 | AutoUpdate: true, // Auto-update filters from GitHub releases | 242 | AutoUpdate: true, // Auto-update filters from GitHub releases | ||
| 246 | }, | 243 | }, | ||
| 247 | Omnibox: OmniboxConfig{ | 244 | Omnibox: OmniboxConfig{ | ||
| 248 | - | InitialBehavior: defaultOmniboxInitialBehavior, | 245 | + | InitialBehavior: defaultOmniboxInitialBehavior, |
| 246 | + | AutoOpenOnNewPane: defaultOmniboxAutoOpenOnNewPane, | |||
| 249 | }, | 247 | }, | ||
| 250 | Session: SessionConfig{ | 248 | Session: SessionConfig{ | ||
| 251 | AutoRestore: false, | 249 | AutoRestore: false, | ||
M internal/infrastructure/config/loader.go +2 -6
| @@ -309,7 +309,6 @@ func (m *Manager) setDefaults() { | |||
| 309 | 309 | m.setDebugDefaults(defaults) | |
| 310 | 310 | m.setAppearanceDefaults(defaults) | |
| 311 | 311 | m.setRenderingDefaults(defaults) | |
| 312 | - | m.setDefaultsDefaults(defaults) | |
| 313 | 312 | m.setWorkspaceDefaults(defaults) | |
| 314 | 313 | m.setContentFilteringDefaults(defaults) | |
| 315 | 314 | m.setOmniboxDefaults(defaults) | |
| @@ -380,12 +379,8 @@ func (m *Manager) setRenderingDefaults(defaults *Config) { | |||
| 380 | 379 | m.viper.SetDefault("default_ui_scale", defaults.DefaultUIScale) | |
| 381 | 380 | } | |
| 382 | 381 | ||
| 383 | - | func (m *Manager) setDefaultsDefaults(defaults *Config) { | |
| 384 | - | m.viper.SetDefault("defaults.new_pane_url", defaults.Defaults.NewPaneURL) | |
| 385 | - | m.viper.SetDefault("defaults.auto_open_omnibox_on_new_pane", defaults.Defaults.AutoOpenOmniboxOnNewPane) | |
| 386 | - | } | |
| 387 | - | ||
| 388 | 382 | func (m *Manager) setWorkspaceDefaults(defaults *Config) { | |
| 383 | + | m.viper.SetDefault("workspace.new_pane_url", defaults.Workspace.NewPaneURL) | |
| 389 | 384 | m.viper.SetDefault("workspace.pane_mode.activation_shortcut", defaults.Workspace.PaneMode.ActivationShortcut) | |
| 390 | 385 | m.viper.SetDefault("workspace.pane_mode.timeout_ms", defaults.Workspace.PaneMode.TimeoutMilliseconds) | |
| 391 | 386 | m.viper.SetDefault("workspace.pane_mode.actions", defaults.Workspace.PaneMode.Actions) | |
| @@ -429,6 +424,7 @@ func (m *Manager) setContentFilteringDefaults(defaults *Config) { | |||
| 429 | 424 | ||
| 430 | 425 | func (m *Manager) setOmniboxDefaults(defaults *Config) { | |
| 431 | 426 | m.viper.SetDefault("omnibox.initial_behavior", defaults.Omnibox.InitialBehavior) | |
| 427 | + | m.viper.SetDefault("omnibox.auto_open_on_new_pane", defaults.Omnibox.AutoOpenOnNewPane) | |
| 432 | 428 | } | |
| 433 | 429 | ||
| 434 | 430 | func (m *Manager) setMediaDefaults(defaults *Config) { | |
| @@ -309,7 +309,6 @@ func (m *Manager) setDefaults() { | |||||
| 309 | m.setDebugDefaults(defaults) | 309 | m.setDebugDefaults(defaults) | ||
| 310 | m.setAppearanceDefaults(defaults) | 310 | m.setAppearanceDefaults(defaults) | ||
| 311 | m.setRenderingDefaults(defaults) | 311 | m.setRenderingDefaults(defaults) | ||
| 312 | - | m.setDefaultsDefaults(defaults) | |||
| 313 | m.setWorkspaceDefaults(defaults) | 312 | m.setWorkspaceDefaults(defaults) | ||
| 314 | m.setContentFilteringDefaults(defaults) | 313 | m.setContentFilteringDefaults(defaults) | ||
| 315 | m.setOmniboxDefaults(defaults) | 314 | m.setOmniboxDefaults(defaults) | ||
| @@ -380,12 +379,8 @@ func (m *Manager) setRenderingDefaults(defaults *Config) { | |||||
| 380 | m.viper.SetDefault("default_ui_scale", defaults.DefaultUIScale) | 379 | m.viper.SetDefault("default_ui_scale", defaults.DefaultUIScale) | ||
| 381 | } | 380 | } | ||
| 382 | 381 | ||||
| 383 | - | func (m *Manager) setDefaultsDefaults(defaults *Config) { | |||
| 384 | - | m.viper.SetDefault("defaults.new_pane_url", defaults.Defaults.NewPaneURL) | |||
| 385 | - | m.viper.SetDefault("defaults.auto_open_omnibox_on_new_pane", defaults.Defaults.AutoOpenOmniboxOnNewPane) | |||
| 386 | - | } | |||
| 387 | - | ||||
| 388 | func (m *Manager) setWorkspaceDefaults(defaults *Config) { | 382 | func (m *Manager) setWorkspaceDefaults(defaults *Config) { | ||
| 383 | + | m.viper.SetDefault("workspace.new_pane_url", defaults.Workspace.NewPaneURL) | |||
| 389 | m.viper.SetDefault("workspace.pane_mode.activation_shortcut", defaults.Workspace.PaneMode.ActivationShortcut) | 384 | m.viper.SetDefault("workspace.pane_mode.activation_shortcut", defaults.Workspace.PaneMode.ActivationShortcut) | ||
| 390 | m.viper.SetDefault("workspace.pane_mode.timeout_ms", defaults.Workspace.PaneMode.TimeoutMilliseconds) | 385 | m.viper.SetDefault("workspace.pane_mode.timeout_ms", defaults.Workspace.PaneMode.TimeoutMilliseconds) | ||
| 391 | m.viper.SetDefault("workspace.pane_mode.actions", defaults.Workspace.PaneMode.Actions) | 386 | m.viper.SetDefault("workspace.pane_mode.actions", defaults.Workspace.PaneMode.Actions) | ||
| @@ -429,6 +424,7 @@ func (m *Manager) setContentFilteringDefaults(defaults *Config) { | |||||
| 429 | 424 | ||||
| 430 | func (m *Manager) setOmniboxDefaults(defaults *Config) { | 425 | func (m *Manager) setOmniboxDefaults(defaults *Config) { | ||
| 431 | m.viper.SetDefault("omnibox.initial_behavior", defaults.Omnibox.InitialBehavior) | 426 | m.viper.SetDefault("omnibox.initial_behavior", defaults.Omnibox.InitialBehavior) | ||
| 427 | + | m.viper.SetDefault("omnibox.auto_open_on_new_pane", defaults.Omnibox.AutoOpenOnNewPane) | |||
| 432 | } | 428 | } | ||
| 433 | 429 | ||||
| 434 | func (m *Manager) setMediaDefaults(defaults *Config) { | 430 | func (m *Manager) setMediaDefaults(defaults *Config) { | ||
M internal/infrastructure/config/schema.go +6 -15
| @@ -15,8 +15,6 @@ type Config struct { | |||
| 15 | 15 | DefaultWebpageZoom float64 `mapstructure:"default_webpage_zoom" yaml:"default_webpage_zoom" toml:"default_webpage_zoom"` | |
| 16 | 16 | // DefaultUIScale sets the default UI scale for GTK widgets (1.0 = 100%, 2.0 = 200%) | |
| 17 | 17 | DefaultUIScale float64 `mapstructure:"default_ui_scale" yaml:"default_ui_scale" toml:"default_ui_scale"` | |
| 18 | - | // Defaults controls default behavior like new-pane URL. | |
| 19 | - | Defaults DefaultsConfig `mapstructure:"defaults" yaml:"defaults" toml:"defaults"` | |
| 20 | 18 | // Workspace defines workspace, pane, and tab handling behavior. | |
| 21 | 19 | Workspace WorkspaceConfig `mapstructure:"workspace" yaml:"workspace" toml:"workspace"` | |
| 22 | 20 | // Session controls session persistence and restoration. | |
| @@ -37,19 +35,6 @@ type Config struct { | |||
| 37 | 35 | Update UpdateConfig `mapstructure:"update" yaml:"update" toml:"update"` | |
| 38 | 36 | } | |
| 39 | 37 | ||
| 40 | - | // DefaultsConfig controls default behavior like new-pane URL. | |
| 41 | - | // | |
| 42 | - | // These defaults are applied when a new pane/tab is created without an explicit URL. | |
| 43 | - | // This is not currently exposed in dumb://config UI. | |
| 44 | - | type DefaultsConfig struct { | |
| 45 | - | // NewPaneURL is the URL loaded when creating a new empty pane. | |
| 46 | - | // Default: "about:blank" | |
| 47 | - | NewPaneURL string `mapstructure:"new_pane_url" yaml:"new_pane_url" toml:"new_pane_url"` | |
| 48 | - | // AutoOpenOmniboxOnNewPane opens the omnibox automatically when a new pane is created. | |
| 49 | - | // Default: false | |
| 50 | - | AutoOpenOmniboxOnNewPane bool `mapstructure:"auto_open_omnibox_on_new_pane" yaml:"auto_open_omnibox_on_new_pane" toml:"auto_open_omnibox_on_new_pane"` //nolint:lll // struct tags must stay on one line | |
| 51 | - | } | |
| 52 | - | ||
| 53 | 38 | // RenderingMode selects GPU vs CPU rendering. | |
| 54 | 39 | type RenderingMode string | |
| 55 | 40 | ||
| @@ -289,6 +274,9 @@ type OmniboxConfig struct { | |||
| 289 | 274 | // InitialBehavior controls what to show when omnibox opens with empty input | |
| 290 | 275 | // Values: "recent" (recent visits), "most_visited" (most visited sites), "none" (nothing) | |
| 291 | 276 | InitialBehavior string `mapstructure:"initial_behavior" yaml:"initial_behavior" toml:"initial_behavior"` | |
| 277 | + | // AutoOpenOnNewPane opens the omnibox automatically when a new pane is created. | |
| 278 | + | // Default: false | |
| 279 | + | AutoOpenOnNewPane bool `mapstructure:"auto_open_on_new_pane" yaml:"auto_open_on_new_pane" toml:"auto_open_on_new_pane"` | |
| 292 | 280 | } | |
| 293 | 281 | ||
| 294 | 282 | // DebugConfig holds debug and troubleshooting options | |
| @@ -310,6 +298,9 @@ type UpdateConfig struct { | |||
| 310 | 298 | ||
| 311 | 299 | // WorkspaceConfig captures layout, pane, and tab behavior preferences. | |
| 312 | 300 | type WorkspaceConfig struct { | |
| 301 | + | // NewPaneURL is the URL loaded when creating a new empty pane/tab. | |
| 302 | + | // Default: "about:blank" | |
| 303 | + | NewPaneURL string `mapstructure:"new_pane_url" yaml:"new_pane_url" toml:"new_pane_url"` | |
| 313 | 304 | // PaneMode defines modal pane behavior and bindings. | |
| 314 | 305 | PaneMode PaneModeConfig `mapstructure:"pane_mode" yaml:"pane_mode" toml:"pane_mode" json:"pane_mode"` | |
| 315 | 306 | // TabMode defines modal tab behavior and bindings (Alt+T). | |
| @@ -15,8 +15,6 @@ type Config struct { | |||||
| 15 | DefaultWebpageZoom float64 `mapstructure:"default_webpage_zoom" yaml:"default_webpage_zoom" toml:"default_webpage_zoom"` | 15 | DefaultWebpageZoom float64 `mapstructure:"default_webpage_zoom" yaml:"default_webpage_zoom" toml:"default_webpage_zoom"` | ||
| 16 | // DefaultUIScale sets the default UI scale for GTK widgets (1.0 = 100%, 2.0 = 200%) | 16 | // DefaultUIScale sets the default UI scale for GTK widgets (1.0 = 100%, 2.0 = 200%) | ||
| 17 | DefaultUIScale float64 `mapstructure:"default_ui_scale" yaml:"default_ui_scale" toml:"default_ui_scale"` | 17 | DefaultUIScale float64 `mapstructure:"default_ui_scale" yaml:"default_ui_scale" toml:"default_ui_scale"` | ||
| 18 | - | // Defaults controls default behavior like new-pane URL. | |||
| 19 | - | Defaults DefaultsConfig `mapstructure:"defaults" yaml:"defaults" toml:"defaults"` | |||
| 20 | // Workspace defines workspace, pane, and tab handling behavior. | 18 | // Workspace defines workspace, pane, and tab handling behavior. | ||
| 21 | Workspace WorkspaceConfig `mapstructure:"workspace" yaml:"workspace" toml:"workspace"` | 19 | Workspace WorkspaceConfig `mapstructure:"workspace" yaml:"workspace" toml:"workspace"` | ||
| 22 | // Session controls session persistence and restoration. | 20 | // Session controls session persistence and restoration. | ||
| @@ -37,19 +35,6 @@ type Config struct { | |||||
| 37 | Update UpdateConfig `mapstructure:"update" yaml:"update" toml:"update"` | 35 | Update UpdateConfig `mapstructure:"update" yaml:"update" toml:"update"` | ||
| 38 | } | 36 | } | ||
| 39 | 37 | ||||
| 40 | - | // DefaultsConfig controls default behavior like new-pane URL. | |||
| 41 | - | // | |||
| 42 | - | // These defaults are applied when a new pane/tab is created without an explicit URL. | |||
| 43 | - | // This is not currently exposed in dumb://config UI. | |||
| 44 | - | type DefaultsConfig struct { | |||
| 45 | - | // NewPaneURL is the URL loaded when creating a new empty pane. | |||
| 46 | - | // Default: "about:blank" | |||
| 47 | - | NewPaneURL string `mapstructure:"new_pane_url" yaml:"new_pane_url" toml:"new_pane_url"` | |||
| 48 | - | // AutoOpenOmniboxOnNewPane opens the omnibox automatically when a new pane is created. | |||
| 49 | - | // Default: false | |||
| 50 | - | AutoOpenOmniboxOnNewPane bool `mapstructure:"auto_open_omnibox_on_new_pane" yaml:"auto_open_omnibox_on_new_pane" toml:"auto_open_omnibox_on_new_pane"` //nolint:lll // struct tags must stay on one line | |||
| 51 | - | } | |||
| 52 | - | ||||
| 53 | // RenderingMode selects GPU vs CPU rendering. | 38 | // RenderingMode selects GPU vs CPU rendering. | ||
| 54 | type RenderingMode string | 39 | type RenderingMode string | ||
| 55 | 40 | ||||
| @@ -289,6 +274,9 @@ type OmniboxConfig struct { | |||||
| 289 | // InitialBehavior controls what to show when omnibox opens with empty input | 274 | // InitialBehavior controls what to show when omnibox opens with empty input | ||
| 290 | // Values: "recent" (recent visits), "most_visited" (most visited sites), "none" (nothing) | 275 | // Values: "recent" (recent visits), "most_visited" (most visited sites), "none" (nothing) | ||
| 291 | InitialBehavior string `mapstructure:"initial_behavior" yaml:"initial_behavior" toml:"initial_behavior"` | 276 | InitialBehavior string `mapstructure:"initial_behavior" yaml:"initial_behavior" toml:"initial_behavior"` | ||
| 277 | + | // AutoOpenOnNewPane opens the omnibox automatically when a new pane is created. | |||
| 278 | + | // Default: false | |||
| 279 | + | AutoOpenOnNewPane bool `mapstructure:"auto_open_on_new_pane" yaml:"auto_open_on_new_pane" toml:"auto_open_on_new_pane"` | |||
| 292 | } | 280 | } | ||
| 293 | 281 | ||||
| 294 | // DebugConfig holds debug and troubleshooting options | 282 | // DebugConfig holds debug and troubleshooting options | ||
| @@ -310,6 +298,9 @@ type UpdateConfig struct { | |||||
| 310 | 298 | ||||
| 311 | // WorkspaceConfig captures layout, pane, and tab behavior preferences. | 299 | // WorkspaceConfig captures layout, pane, and tab behavior preferences. | ||
| 312 | type WorkspaceConfig struct { | 300 | type WorkspaceConfig struct { | ||
| 301 | + | // NewPaneURL is the URL loaded when creating a new empty pane/tab. | |||
| 302 | + | // Default: "about:blank" | |||
| 303 | + | NewPaneURL string `mapstructure:"new_pane_url" yaml:"new_pane_url" toml:"new_pane_url"` | |||
| 313 | // PaneMode defines modal pane behavior and bindings. | 304 | // PaneMode defines modal pane behavior and bindings. | ||
| 314 | PaneMode PaneModeConfig `mapstructure:"pane_mode" yaml:"pane_mode" toml:"pane_mode" json:"pane_mode"` | 305 | PaneMode PaneModeConfig `mapstructure:"pane_mode" yaml:"pane_mode" toml:"pane_mode" json:"pane_mode"` | ||
| 315 | // TabMode defines modal tab behavior and bindings (Alt+T). | 306 | // TabMode defines modal tab behavior and bindings (Alt+T). | ||
M internal/infrastructure/config/validation.go +8 -8
| @@ -23,7 +23,7 @@ func validateConfig(config *Config) error { | |||
| 23 | 23 | validationErrors = append(validationErrors, validateTabBar(config)...) | |
| 24 | 24 | validationErrors = append(validationErrors, validateTabMode(config)...) | |
| 25 | 25 | validationErrors = append(validationErrors, validateLogging(config)...) | |
| 26 | - | validationErrors = append(validationErrors, validateDefaults(config)...) | |
| 26 | + | validationErrors = append(validationErrors, validateWorkspaceNewPaneURL(config)...) | |
| 27 | 27 | validationErrors = append(validationErrors, validateOmnibox(config)...) | |
| 28 | 28 | validationErrors = append(validationErrors, validateRendering(config)...) | |
| 29 | 29 | validationErrors = append(validationErrors, validateColorScheme(config)...) | |
| @@ -249,20 +249,20 @@ func validateLogging(config *Config) []string { | |||
| 249 | 249 | return validationErrors | |
| 250 | 250 | } | |
| 251 | 251 | ||
| 252 | - | func validateDefaults(config *Config) []string { | |
| 252 | + | func validateWorkspaceNewPaneURL(config *Config) []string { | |
| 253 | 253 | var validationErrors []string | |
| 254 | 254 | ||
| 255 | - | if config.Defaults.NewPaneURL == "" { | |
| 256 | - | validationErrors = append(validationErrors, "defaults.new_pane_url cannot be empty") | |
| 255 | + | if config.Workspace.NewPaneURL == "" { | |
| 256 | + | validationErrors = append(validationErrors, "workspace.new_pane_url cannot be empty") | |
| 257 | 257 | return validationErrors | |
| 258 | 258 | } | |
| 259 | 259 | ||
| 260 | - | normalized := domainurl.Normalize(config.Defaults.NewPaneURL) | |
| 260 | + | normalized := domainurl.Normalize(config.Workspace.NewPaneURL) | |
| 261 | 261 | parsed, err := url.Parse(normalized) | |
| 262 | 262 | if err != nil { | |
| 263 | 263 | validationErrors = append(validationErrors, fmt.Sprintf( | |
| 264 | - | "defaults.new_pane_url must be a valid URL (got: %s)", | |
| 265 | - | config.Defaults.NewPaneURL, | |
| 264 | + | "workspace.new_pane_url must be a valid URL (got: %s)", | |
| 265 | + | config.Workspace.NewPaneURL, | |
| 266 | 266 | )) | |
| 267 | 267 | return validationErrors | |
| 268 | 268 | } | |
| @@ -272,7 +272,7 @@ func validateDefaults(config *Config) []string { | |||
| 272 | 272 | // ok | |
| 273 | 273 | default: | |
| 274 | 274 | validationErrors = append(validationErrors, fmt.Sprintf( | |
| 275 | - | "defaults.new_pane_url must use one of: http, https, dumb, file, about (got: %s)", | |
| 275 | + | "workspace.new_pane_url must use one of: http, https, dumb, file, about (got: %s)", | |
| 276 | 276 | parsed.Scheme, | |
| 277 | 277 | )) | |
| 278 | 278 | } | |
| @@ -23,7 +23,7 @@ func validateConfig(config *Config) error { | |||||
| 23 | validationErrors = append(validationErrors, validateTabBar(config)...) | 23 | validationErrors = append(validationErrors, validateTabBar(config)...) | ||
| 24 | validationErrors = append(validationErrors, validateTabMode(config)...) | 24 | validationErrors = append(validationErrors, validateTabMode(config)...) | ||
| 25 | validationErrors = append(validationErrors, validateLogging(config)...) | 25 | validationErrors = append(validationErrors, validateLogging(config)...) | ||
| 26 | - | validationErrors = append(validationErrors, validateDefaults(config)...) | 26 | + | validationErrors = append(validationErrors, validateWorkspaceNewPaneURL(config)...) |
| 27 | validationErrors = append(validationErrors, validateOmnibox(config)...) | 27 | validationErrors = append(validationErrors, validateOmnibox(config)...) | ||
| 28 | validationErrors = append(validationErrors, validateRendering(config)...) | 28 | validationErrors = append(validationErrors, validateRendering(config)...) | ||
| 29 | validationErrors = append(validationErrors, validateColorScheme(config)...) | 29 | validationErrors = append(validationErrors, validateColorScheme(config)...) | ||
| @@ -249,20 +249,20 @@ func validateLogging(config *Config) []string { | |||||
| 249 | return validationErrors | 249 | return validationErrors | ||
| 250 | } | 250 | } | ||
| 251 | 251 | ||||
| 252 | - | func validateDefaults(config *Config) []string { | 252 | + | func validateWorkspaceNewPaneURL(config *Config) []string { |
| 253 | var validationErrors []string | 253 | var validationErrors []string | ||
| 254 | 254 | ||||
| 255 | - | if config.Defaults.NewPaneURL == "" { | 255 | + | if config.Workspace.NewPaneURL == "" { |
| 256 | - | validationErrors = append(validationErrors, "defaults.new_pane_url cannot be empty") | 256 | + | validationErrors = append(validationErrors, "workspace.new_pane_url cannot be empty") |
| 257 | return validationErrors | 257 | return validationErrors | ||
| 258 | } | 258 | } | ||
| 259 | 259 | ||||
| 260 | - | normalized := domainurl.Normalize(config.Defaults.NewPaneURL) | 260 | + | normalized := domainurl.Normalize(config.Workspace.NewPaneURL) |
| 261 | parsed, err := url.Parse(normalized) | 261 | parsed, err := url.Parse(normalized) | ||
| 262 | if err != nil { | 262 | if err != nil { | ||
| 263 | validationErrors = append(validationErrors, fmt.Sprintf( | 263 | validationErrors = append(validationErrors, fmt.Sprintf( | ||
| 264 | - | "defaults.new_pane_url must be a valid URL (got: %s)", | 264 | + | "workspace.new_pane_url must be a valid URL (got: %s)", |
| 265 | - | config.Defaults.NewPaneURL, | 265 | + | config.Workspace.NewPaneURL, |
| 266 | )) | 266 | )) | ||
| 267 | return validationErrors | 267 | return validationErrors | ||
| 268 | } | 268 | } | ||
| @@ -272,7 +272,7 @@ func validateDefaults(config *Config) []string { | |||||
| 272 | // ok | 272 | // ok | ||
| 273 | default: | 273 | default: | ||
| 274 | validationErrors = append(validationErrors, fmt.Sprintf( | 274 | validationErrors = append(validationErrors, fmt.Sprintf( | ||
| 275 | - | "defaults.new_pane_url must use one of: http, https, dumb, file, about (got: %s)", | 275 | + | "workspace.new_pane_url must use one of: http, https, dumb, file, about (got: %s)", |
| 276 | parsed.Scheme, | 276 | parsed.Scheme, | ||
| 277 | )) | 277 | )) | ||
| 278 | } | 278 | } | ||
M internal/ui/coordinator/workspace.go +4 -4
| @@ -127,7 +127,7 @@ func (c *WorkspaceCoordinator) Split(ctx context.Context, direction usecase.Spli | |||
| 127 | 127 | Workspace: splitCtx.ws, | |
| 128 | 128 | TargetPane: splitCtx.activePane, | |
| 129 | 129 | Direction: direction, | |
| 130 | - | InitialURL: domainurl.Normalize(config.Get().Defaults.NewPaneURL), | |
| 130 | + | InitialURL: domainurl.Normalize(config.Get().Workspace.NewPaneURL), | |
| 131 | 131 | }) | |
| 132 | 132 | if err != nil { | |
| 133 | 133 | log.Error().Err(err).Str("direction", string(direction)).Msg("failed to split pane") | |
| @@ -145,7 +145,7 @@ func (c *WorkspaceCoordinator) Split(ctx context.Context, direction usecase.Spli | |||
| 145 | 145 | c.applySplitToView(ctx, splitCtx.wsView, splitCtx.ws, output, direction, splitCtx.existingWidget, splitCtx.isStackSplit, oldActivePaneID) | |
| 146 | 146 | } | |
| 147 | 147 | ||
| 148 | - | if splitCtx.wsView != nil && config.Get().Defaults.AutoOpenOmniboxOnNewPane { | |
| 148 | + | if splitCtx.wsView != nil && config.Get().Omnibox.AutoOpenOnNewPane { | |
| 149 | 149 | splitCtx.wsView.ShowOmnibox(ctx, "") | |
| 150 | 150 | } | |
| 151 | 151 | ||
| @@ -1222,7 +1222,7 @@ func (c *WorkspaceCoordinator) StackPane(ctx context.Context) error { | |||
| 1222 | 1222 | ||
| 1223 | 1223 | newPaneID := entity.PaneID(c.generateID()) | |
| 1224 | 1224 | newPane := entity.NewPane(newPaneID) | |
| 1225 | - | newPane.URI = domainurl.Normalize(config.Get().Defaults.NewPaneURL) | |
| 1225 | + | newPane.URI = domainurl.Normalize(config.Get().Workspace.NewPaneURL) | |
| 1226 | 1226 | newPane.Title = defaultPaneTitle | |
| 1227 | 1227 | ||
| 1228 | 1228 | stackNode, needsFirstPaneTitleUpdate := c.ensureStackNode(ctx, stackCtx.activeNode, stackCtx.activePaneID, stackCtx.originalTitle) | |
| @@ -1273,7 +1273,7 @@ func (c *WorkspaceCoordinator) StackPane(ctx context.Context) error { | |||
| 1273 | 1273 | log.Warn().Err(err).Msg("failed to set active pane") | |
| 1274 | 1274 | } | |
| 1275 | 1275 | ||
| 1276 | - | if config.Get().Defaults.AutoOpenOmniboxOnNewPane { | |
| 1276 | + | if config.Get().Omnibox.AutoOpenOnNewPane { | |
| 1277 | 1277 | stackCtx.wsView.ShowOmnibox(ctx, "") | |
| 1278 | 1278 | } | |
| 1279 | 1279 | ||
| @@ -127,7 +127,7 @@ func (c *WorkspaceCoordinator) Split(ctx context.Context, direction usecase.Spli | |||||
| 127 | Workspace: splitCtx.ws, | 127 | Workspace: splitCtx.ws, | ||
| 128 | TargetPane: splitCtx.activePane, | 128 | TargetPane: splitCtx.activePane, | ||
| 129 | Direction: direction, | 129 | Direction: direction, | ||
| 130 | - | InitialURL: domainurl.Normalize(config.Get().Defaults.NewPaneURL), | 130 | + | InitialURL: domainurl.Normalize(config.Get().Workspace.NewPaneURL), |
| 131 | }) | 131 | }) | ||
| 132 | if err != nil { | 132 | if err != nil { | ||
| 133 | log.Error().Err(err).Str("direction", string(direction)).Msg("failed to split pane") | 133 | log.Error().Err(err).Str("direction", string(direction)).Msg("failed to split pane") | ||
| @@ -145,7 +145,7 @@ func (c *WorkspaceCoordinator) Split(ctx context.Context, direction usecase.Spli | |||||
| 145 | c.applySplitToView(ctx, splitCtx.wsView, splitCtx.ws, output, direction, splitCtx.existingWidget, splitCtx.isStackSplit, oldActivePaneID) | 145 | c.applySplitToView(ctx, splitCtx.wsView, splitCtx.ws, output, direction, splitCtx.existingWidget, splitCtx.isStackSplit, oldActivePaneID) | ||
| 146 | } | 146 | } | ||
| 147 | 147 | ||||
| 148 | - | if splitCtx.wsView != nil && config.Get().Defaults.AutoOpenOmniboxOnNewPane { | 148 | + | if splitCtx.wsView != nil && config.Get().Omnibox.AutoOpenOnNewPane { |
| 149 | splitCtx.wsView.ShowOmnibox(ctx, "") | 149 | splitCtx.wsView.ShowOmnibox(ctx, "") | ||
| 150 | } | 150 | } | ||
| 151 | 151 | ||||
| @@ -1222,7 +1222,7 @@ func (c *WorkspaceCoordinator) StackPane(ctx context.Context) error { | |||||
| 1222 | 1222 | ||||
| 1223 | newPaneID := entity.PaneID(c.generateID()) | 1223 | newPaneID := entity.PaneID(c.generateID()) | ||
| 1224 | newPane := entity.NewPane(newPaneID) | 1224 | newPane := entity.NewPane(newPaneID) | ||
| 1225 | - | newPane.URI = domainurl.Normalize(config.Get().Defaults.NewPaneURL) | 1225 | + | newPane.URI = domainurl.Normalize(config.Get().Workspace.NewPaneURL) |
| 1226 | newPane.Title = defaultPaneTitle | 1226 | newPane.Title = defaultPaneTitle | ||
| 1227 | 1227 | ||||
| 1228 | stackNode, needsFirstPaneTitleUpdate := c.ensureStackNode(ctx, stackCtx.activeNode, stackCtx.activePaneID, stackCtx.originalTitle) | 1228 | stackNode, needsFirstPaneTitleUpdate := c.ensureStackNode(ctx, stackCtx.activeNode, stackCtx.activePaneID, stackCtx.originalTitle) | ||
| @@ -1273,7 +1273,7 @@ func (c *WorkspaceCoordinator) StackPane(ctx context.Context) error { | |||||
| 1273 | log.Warn().Err(err).Msg("failed to set active pane") | 1273 | log.Warn().Err(err).Msg("failed to set active pane") | ||
| 1274 | } | 1274 | } | ||
| 1275 | 1275 | ||||
| 1276 | - | if config.Get().Defaults.AutoOpenOmniboxOnNewPane { | 1276 | + | if config.Get().Omnibox.AutoOpenOnNewPane { |
| 1277 | stackCtx.wsView.ShowOmnibox(ctx, "") | 1277 | stackCtx.wsView.ShowOmnibox(ctx, "") | ||
| 1278 | } | 1278 | } | ||
| 1279 | 1279 | ||||
M internal/ui/dispatcher/keyboard.go +1 -1
| @@ -101,7 +101,7 @@ func (d *KeyboardDispatcher) initActionHandlers() { | |||
| 101 | 101 | // Tab actions | |
| 102 | 102 | input.ActionNewTab: func(ctx context.Context) error { | |
| 103 | 103 | cfg := config.Get() | |
| 104 | - | _, err := d.tabCoord.Create(ctx, domainurl.Normalize(cfg.Defaults.NewPaneURL)) | |
| 104 | + | _, err := d.tabCoord.Create(ctx, domainurl.Normalize(cfg.Workspace.NewPaneURL)) | |
| 105 | 105 | return err | |
| 106 | 106 | }, | |
| 107 | 107 | input.ActionCloseTab: d.tabCoord.Close, | |
| @@ -101,7 +101,7 @@ func (d *KeyboardDispatcher) initActionHandlers() { | |||||
| 101 | // Tab actions | 101 | // Tab actions | ||
| 102 | input.ActionNewTab: func(ctx context.Context) error { | 102 | input.ActionNewTab: func(ctx context.Context) error { | ||
| 103 | cfg := config.Get() | 103 | cfg := config.Get() | ||
| 104 | - | _, err := d.tabCoord.Create(ctx, domainurl.Normalize(cfg.Defaults.NewPaneURL)) | 104 | + | _, err := d.tabCoord.Create(ctx, domainurl.Normalize(cfg.Workspace.NewPaneURL)) |
| 105 | return err | 105 | return err | ||
| 106 | }, | 106 | }, | ||
| 107 | input.ActionCloseTab: d.tabCoord.Close, | 107 | input.ActionCloseTab: d.tabCoord.Close, | ||