detached b65c4d9

fix(omnibox): honor configured most-visited window

commit
b65c4d924cd889de509f605f47961c0aef26eb8f
author
brice · · 2026-04-12 06:26 UTC
parent
14d9030
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
11 files · +156 -9
M docs/config/index.md +2 -0
@@ -104,12 +104,14 @@ dumber dmenu --days 14 --most-visited
104104 | Key | Type | Default | Valid Values | Description |
105105 |-----|------|---------|--------------|-------------|
106106 | `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior |
107+| `omnibox.most_visited_days` | int | `30` | `>= 0` | Days of history to consider when `initial_behavior = "most_visited"` (`0` = all history) |
107108 | `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane |
108109
109110 **Example:**
110111 ```toml
111112 [omnibox]
112113 initial_behavior = "recent" # Show recent history when omnibox opens
114+most_visited_days = 30 # Days of history used for most_visited
113115 auto_open_on_new_pane = false
114116
115117 # Alternative options:
@@ -104,12 +104,14 @@ dumber dmenu --days 14 --most-visited
104 | Key | Type | Default | Valid Values | Description |104 | Key | Type | Default | Valid Values | Description |
105 |-----|------|---------|--------------|-------------|105 |-----|------|---------|--------------|-------------|
106 | `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior |106 | `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior |
107+| `omnibox.most_visited_days` | int | `30` | `>= 0` | Days of history to consider when `initial_behavior = "most_visited"` (`0` = all history) |
107 | `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane |108 | `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane |
108 109
109 **Example:**110 **Example:**
110 ```toml111 ```toml
111 [omnibox]112 [omnibox]
112 initial_behavior = "recent" # Show recent history when omnibox opens113 initial_behavior = "recent" # Show recent history when omnibox opens
114+most_visited_days = 30 # Days of history used for most_visited
113 auto_open_on_new_pane = false115 auto_open_on_new_pane = false
114 116
115 # Alternative options:117 # Alternative options:
M docs/reference/configuration.md +1 -0
@@ -18,6 +18,7 @@
1818 | `dmenu.date_format` | string | `2006-01-02 15:04` | Go time format |
1919 | `dmenu.sort_by_visit_count` | bool | `true` | |
2020 | `omnibox.initial_behavior` | string | `recent` | `recent`, `most_visited`, `none` |
21+| `omnibox.most_visited_days` | int | `30` | `>= 0` |
2122 | `omnibox.auto_open_on_new_pane` | bool | `false` | |
2223 | `logging.level` | string | `info` | `debug`, `info`, `warn`, `error` |
2324 | `logging.format` | string | `text` | `text`, `json` |
@@ -18,6 +18,7 @@
18 | `dmenu.date_format` | string | `2006-01-02 15:04` | Go time format |18 | `dmenu.date_format` | string | `2006-01-02 15:04` | Go time format |
19 | `dmenu.sort_by_visit_count` | bool | `true` | |19 | `dmenu.sort_by_visit_count` | bool | `true` | |
20 | `omnibox.initial_behavior` | string | `recent` | `recent`, `most_visited`, `none` |20 | `omnibox.initial_behavior` | string | `recent` | `recent`, `most_visited`, `none` |
21+| `omnibox.most_visited_days` | int | `30` | `>= 0` |
21 | `omnibox.auto_open_on_new_pane` | bool | `false` | |22 | `omnibox.auto_open_on_new_pane` | bool | `false` | |
22 | `logging.level` | string | `info` | `debug`, `info`, `warn`, `error` |23 | `logging.level` | string | `info` | `debug`, `info`, `warn`, `error` |
23 | `logging.format` | string | `text` | `text`, `json` |24 | `logging.format` | string | `text` | `text`, `json` |
M internal/infrastructure/config/defaults.go +2 -0
@@ -20,6 +20,7 @@ const (
2020
2121 // Omnibox defaults
2222 defaultOmniboxInitialBehavior = OmniboxInitialBehaviorRecent
23+ defaultOmniboxMostVisitedDays = 30
2324 defaultOmniboxAutoOpenOnNewPane = false
2425
2526 // Workspace defaults
@@ -284,6 +285,7 @@ func DefaultConfig() *Config {
284285 },
285286 Omnibox: OmniboxConfig{
286287 InitialBehavior: defaultOmniboxInitialBehavior,
288+ MostVisitedDays: defaultOmniboxMostVisitedDays,
287289 AutoOpenOnNewPane: defaultOmniboxAutoOpenOnNewPane,
288290 },
289291 Session: SessionConfig{
@@ -20,6 +20,7 @@ const (
20 20
21 // Omnibox defaults21 // Omnibox defaults
22 defaultOmniboxInitialBehavior = OmniboxInitialBehaviorRecent22 defaultOmniboxInitialBehavior = OmniboxInitialBehaviorRecent
23+ defaultOmniboxMostVisitedDays = 30
23 defaultOmniboxAutoOpenOnNewPane = false24 defaultOmniboxAutoOpenOnNewPane = false
24 25
25 // Workspace defaults26 // Workspace defaults
@@ -284,6 +285,7 @@ func DefaultConfig() *Config {
284 },285 },
285 Omnibox: OmniboxConfig{286 Omnibox: OmniboxConfig{
286 InitialBehavior: defaultOmniboxInitialBehavior,287 InitialBehavior: defaultOmniboxInitialBehavior,
288+ MostVisitedDays: defaultOmniboxMostVisitedDays,
287 AutoOpenOnNewPane: defaultOmniboxAutoOpenOnNewPane,289 AutoOpenOnNewPane: defaultOmniboxAutoOpenOnNewPane,
288 },290 },
289 Session: SessionConfig{291 Session: SessionConfig{
M internal/infrastructure/config/loader.go +1 -0
@@ -526,6 +526,7 @@ func (m *Manager) setClipboardDefaults(defaults *Config) {
526526
527527 func (m *Manager) setOmniboxDefaults(defaults *Config) {
528528 m.viper.SetDefault("omnibox.initial_behavior", defaults.Omnibox.InitialBehavior)
529+ m.viper.SetDefault("omnibox.most_visited_days", defaults.Omnibox.MostVisitedDays)
529530 m.viper.SetDefault("omnibox.auto_open_on_new_pane", defaults.Omnibox.AutoOpenOnNewPane)
530531 }
531532
@@ -526,6 +526,7 @@ func (m *Manager) setClipboardDefaults(defaults *Config) {
526 526
527 func (m *Manager) setOmniboxDefaults(defaults *Config) {527 func (m *Manager) setOmniboxDefaults(defaults *Config) {
528 m.viper.SetDefault("omnibox.initial_behavior", defaults.Omnibox.InitialBehavior)528 m.viper.SetDefault("omnibox.initial_behavior", defaults.Omnibox.InitialBehavior)
529+ m.viper.SetDefault("omnibox.most_visited_days", defaults.Omnibox.MostVisitedDays)
529 m.viper.SetDefault("omnibox.auto_open_on_new_pane", defaults.Omnibox.AutoOpenOnNewPane)530 m.viper.SetDefault("omnibox.auto_open_on_new_pane", defaults.Omnibox.AutoOpenOnNewPane)
530 }531 }
531 532
M internal/infrastructure/config/schema.go +4 -0
@@ -359,6 +359,10 @@ type OmniboxConfig struct {
359359 // InitialBehavior controls what to show when omnibox opens with empty input
360360 // Values: "recent" (recent visits), "most_visited" (most visited sites), "none" (nothing)
361361 InitialBehavior OmniboxInitialBehavior `mapstructure:"initial_behavior" yaml:"initial_behavior" toml:"initial_behavior"`
362+ // MostVisitedDays controls how many days of history are considered when initial behavior is most_visited.
363+ // 0 means all history.
364+ // Default: 30.
365+ MostVisitedDays int `mapstructure:"most_visited_days" yaml:"most_visited_days" toml:"most_visited_days"`
362366 // AutoOpenOnNewPane opens the omnibox automatically when a new pane is created.
363367 // Default: false
364368 AutoOpenOnNewPane bool `mapstructure:"auto_open_on_new_pane" yaml:"auto_open_on_new_pane" toml:"auto_open_on_new_pane"`
@@ -359,6 +359,10 @@ type OmniboxConfig struct {
359 // InitialBehavior controls what to show when omnibox opens with empty input359 // InitialBehavior controls what to show when omnibox opens with empty input
360 // Values: "recent" (recent visits), "most_visited" (most visited sites), "none" (nothing)360 // Values: "recent" (recent visits), "most_visited" (most visited sites), "none" (nothing)
361 InitialBehavior OmniboxInitialBehavior `mapstructure:"initial_behavior" yaml:"initial_behavior" toml:"initial_behavior"`361 InitialBehavior OmniboxInitialBehavior `mapstructure:"initial_behavior" yaml:"initial_behavior" toml:"initial_behavior"`
362+ // MostVisitedDays controls how many days of history are considered when initial behavior is most_visited.
363+ // 0 means all history.
364+ // Default: 30.
365+ MostVisitedDays int `mapstructure:"most_visited_days" yaml:"most_visited_days" toml:"most_visited_days"`
362 // AutoOpenOnNewPane opens the omnibox automatically when a new pane is created.366 // AutoOpenOnNewPane opens the omnibox automatically when a new pane is created.
363 // Default: false367 // Default: false
364 AutoOpenOnNewPane bool `mapstructure:"auto_open_on_new_pane" yaml:"auto_open_on_new_pane" toml:"auto_open_on_new_pane"`368 AutoOpenOnNewPane bool `mapstructure:"auto_open_on_new_pane" yaml:"auto_open_on_new_pane" toml:"auto_open_on_new_pane"`
M internal/infrastructure/config/schema_provider.go +8 -0
@@ -672,6 +672,14 @@ func (*SchemaProvider) getOmniboxKeys(defaults *Config) []entity.ConfigKeyInfo {
672672 },
673673 Section: SectionOmnibox,
674674 },
675+ {
676+ Key: "omnibox.most_visited_days",
677+ Type: "int",
678+ Default: fmt.Sprintf("%d", defaults.Omnibox.MostVisitedDays),
679+ Description: "Days of history to consider for most_visited (0 = all history)",
680+ Range: ">=0",
681+ Section: SectionOmnibox,
682+ },
675683 {
676684 Key: "omnibox.auto_open_on_new_pane",
677685 Type: "bool",
@@ -672,6 +672,14 @@ func (*SchemaProvider) getOmniboxKeys(defaults *Config) []entity.ConfigKeyInfo {
672 },672 },
673 Section: SectionOmnibox,673 Section: SectionOmnibox,
674 },674 },
675+ {
676+ Key: "omnibox.most_visited_days",
677+ Type: "int",
678+ Default: fmt.Sprintf("%d", defaults.Omnibox.MostVisitedDays),
679+ Description: "Days of history to consider for most_visited (0 = all history)",
680+ Range: ">=0",
681+ Section: SectionOmnibox,
682+ },
675 {683 {
676 Key: "omnibox.auto_open_on_new_pane",684 Key: "omnibox.auto_open_on_new_pane",
677 Type: "bool",685 Type: "bool",
M internal/infrastructure/config/validation.go +7 -3
@@ -388,18 +388,22 @@ func validateWorkspaceURLValue(fieldPath, value string) []string {
388388 }
389389
390390 func validateOmnibox(config *Config) []string {
391+ var validationErrors []string
392+ if config.Omnibox.MostVisitedDays < 0 {
393+ validationErrors = append(validationErrors, "omnibox.most_visited_days must be non-negative")
394+ }
391395 switch config.Omnibox.InitialBehavior {
392396 case OmniboxInitialBehaviorRecent, OmniboxInitialBehaviorMostVisited, OmniboxInitialBehaviorNone:
393- return nil
394397 default:
395- return []string{fmt.Sprintf(
398+ validationErrors = append(validationErrors, fmt.Sprintf(
396399 "omnibox.initial_behavior must be one of: %s, %s, %s (got: %s)",
397400 OmniboxInitialBehaviorRecent,
398401 OmniboxInitialBehaviorMostVisited,
399402 OmniboxInitialBehaviorNone,
400403 config.Omnibox.InitialBehavior,
401- )}
404+ ))
402405 }
406+ return validationErrors
403407 }
404408
405409 func validateRendering(config *Config) []string {
@@ -388,18 +388,22 @@ func validateWorkspaceURLValue(fieldPath, value string) []string {
388 }388 }
389 389
390 func validateOmnibox(config *Config) []string {390 func validateOmnibox(config *Config) []string {
391+ var validationErrors []string
392+ if config.Omnibox.MostVisitedDays < 0 {
393+ validationErrors = append(validationErrors, "omnibox.most_visited_days must be non-negative")
394+ }
391 switch config.Omnibox.InitialBehavior {395 switch config.Omnibox.InitialBehavior {
392 case OmniboxInitialBehaviorRecent, OmniboxInitialBehaviorMostVisited, OmniboxInitialBehaviorNone:396 case OmniboxInitialBehaviorRecent, OmniboxInitialBehaviorMostVisited, OmniboxInitialBehaviorNone:
393- return nil
394 default:397 default:
395- return []string{fmt.Sprintf(398+ validationErrors = append(validationErrors, fmt.Sprintf(
396 "omnibox.initial_behavior must be one of: %s, %s, %s (got: %s)",399 "omnibox.initial_behavior must be one of: %s, %s, %s (got: %s)",
397 OmniboxInitialBehaviorRecent,400 OmniboxInitialBehaviorRecent,
398 OmniboxInitialBehaviorMostVisited,401 OmniboxInitialBehaviorMostVisited,
399 OmniboxInitialBehaviorNone,402 OmniboxInitialBehaviorNone,
400 config.Omnibox.InitialBehavior,403 config.Omnibox.InitialBehavior,
401- )}404+ ))
402 }405 }
406+ return validationErrors
403 }407 }
404 408
405 func validateRendering(config *Config) []string {409 func validateRendering(config *Config) []string {
M internal/ui/app.go +2 -0
@@ -802,6 +802,8 @@ func buildOmniboxConfig(
802802 ShortcutsUC: usecase.NewSearchShortcutsUseCase(shortcuts),
803803 DefaultSearch: deps.Config.DefaultSearchEngine,
804804 InitialBehavior: deps.Config.Omnibox.InitialBehavior,
805+ MostVisitedDays: deps.Config.Omnibox.MostVisitedDays,
806+ MostVisitedDaysSet: true,
805807 SaveInitialBehavior: deps.HandlerDeps.SaveOmniboxInitialBehavior,
806808 UIScale: deps.Config.DefaultUIScale,
807809 OnNavigate: callbacks.OnNavigate,
@@ -802,6 +802,8 @@ func buildOmniboxConfig(
802 ShortcutsUC: usecase.NewSearchShortcutsUseCase(shortcuts),802 ShortcutsUC: usecase.NewSearchShortcutsUseCase(shortcuts),
803 DefaultSearch: deps.Config.DefaultSearchEngine,803 DefaultSearch: deps.Config.DefaultSearchEngine,
804 InitialBehavior: deps.Config.Omnibox.InitialBehavior,804 InitialBehavior: deps.Config.Omnibox.InitialBehavior,
805+ MostVisitedDays: deps.Config.Omnibox.MostVisitedDays,
806+ MostVisitedDaysSet: true,
805 SaveInitialBehavior: deps.HandlerDeps.SaveOmniboxInitialBehavior,807 SaveInitialBehavior: deps.HandlerDeps.SaveOmniboxInitialBehavior,
806 UIScale: deps.Config.DefaultUIScale,808 UIScale: deps.Config.DefaultUIScale,
807 OnNavigate: callbacks.OnNavigate,809 OnNavigate: callbacks.OnNavigate,
A internal/ui/app_omnibox_config_test.go +25 -0
@@ -0,0 +1,25 @@
1+package ui
2+
3+import (
4+ "testing"
5+
6+ configpkg "github.com/bnema/dumber/internal/infrastructure/config"
7+)
8+
9+func TestBuildOmniboxConfig_UsesConfiguredMostVisitedDays(t *testing.T) {
10+ deps := &Dependencies{
11+ Config: &configpkg.Config{
12+ Omnibox: configpkg.OmniboxConfig{
13+ MostVisitedDays: 7,
14+ },
15+ },
16+ }
17+
18+ got := buildOmniboxConfig(deps, nil, omniboxCallbacks{})
19+ if got.MostVisitedDays != 7 {
20+ t.Fatalf("MostVisitedDays = %d, want 7", got.MostVisitedDays)
21+ }
22+ if !got.MostVisitedDaysSet {
23+ t.Fatal("MostVisitedDaysSet = false, want true")
24+ }
25+}
@@ -0,0 +1,25 @@
1+package ui
2+
3+import (
4+ "testing"
5+
6+ configpkg "github.com/bnema/dumber/internal/infrastructure/config"
7+)
8+
9+func TestBuildOmniboxConfig_UsesConfiguredMostVisitedDays(t *testing.T) {
10+ deps := &Dependencies{
11+ Config: &configpkg.Config{
12+ Omnibox: configpkg.OmniboxConfig{
13+ MostVisitedDays: 7,
14+ },
15+ },
16+ }
17+
18+ got := buildOmniboxConfig(deps, nil, omniboxCallbacks{})
19+ if got.MostVisitedDays != 7 {
20+ t.Fatalf("MostVisitedDays = %d, want 7", got.MostVisitedDays)
21+ }
22+ if !got.MostVisitedDaysSet {
23+ t.Fatal("MostVisitedDaysSet = false, want true")
24+ }
25+}
M internal/ui/component/omnibox.go +25 -6
@@ -27,11 +27,12 @@ import (
2727 )
2828
2929 const (
30- debounceDelayMs = 50
31- endBoxSpacing = 6
32- defaultOmniboxPlaceholder = "Search history or enter URL… (! lists bangs)"
33- minGhostInputLength = 1
34- initialBehaviorBadgeTooltip = "Toggle default history order (Ctrl+R)"
30+ debounceDelayMs = 50
31+ defaultOmniboxMostVisitedDays = 30
32+ endBoxSpacing = 6
33+ defaultOmniboxPlaceholder = "Search history or enter URL… (! lists bangs)"
34+ minGhostInputLength = 1
35+ initialBehaviorBadgeTooltip = "Toggle default history order (Ctrl+R)"
3536 )
3637
3738 type favoriteRowIndicatorUpdate struct {
@@ -115,6 +116,8 @@ type Omnibox struct {
115116 shortcutsUC *usecase.SearchShortcutsUseCase
116117 defaultSearch string
117118 initialBehavior entity.OmniboxInitialBehavior
119+ mostVisitedDays int
120+ mostVisitedDaysSet bool
118121 saveInitialBehaviorFn func(context.Context, entity.OmniboxInitialBehavior) error
119122 ctx context.Context
120123
@@ -161,6 +164,8 @@ type OmniboxConfig struct {
161164 ShortcutsUC *usecase.SearchShortcutsUseCase
162165 DefaultSearch string
163166 InitialBehavior entity.OmniboxInitialBehavior
167+ MostVisitedDays int
168+ MostVisitedDaysSet bool
164169 SaveInitialBehavior func(ctx context.Context, behavior entity.OmniboxInitialBehavior) error
165170 UIScale float64 // UI scale for favicon sizing
166171 OnNavigate func(url string) // Callback when user navigates via omnibox
@@ -181,6 +186,10 @@ func NewOmnibox(ctx context.Context, cfg OmniboxConfig) *Omnibox {
181186 if uiScale <= 0 {
182187 uiScale = 1.0
183188 }
189+ mostVisitedDays := cfg.MostVisitedDays
190+ if !cfg.MostVisitedDaysSet {
191+ mostVisitedDays = defaultOmniboxMostVisitedDays
192+ }
184193
185194 sizeCfg := ResolveModalSizeConfig(cfg.SizeConfig, OmniboxSizeDefaults)
186195
@@ -194,6 +203,8 @@ func NewOmnibox(ctx context.Context, cfg OmniboxConfig) *Omnibox {
194203 shortcutsUC: cfg.ShortcutsUC,
195204 defaultSearch: cfg.DefaultSearch,
196205 initialBehavior: cfg.InitialBehavior,
206+ mostVisitedDays: mostVisitedDays,
207+ mostVisitedDaysSet: cfg.MostVisitedDaysSet,
197208 saveInitialBehaviorFn: cfg.SaveInitialBehavior,
198209 onToast: cfg.OnToast,
199210 onAccentKeyPress: cfg.OnAccentKeyPress,
@@ -1590,6 +1601,8 @@ func (o *Omnibox) loadInitialHistory(token uint64) {
15901601 // Capture effective result limit on the GTK main thread before spawning goroutine
15911602 initialLimit := o.effectiveMaxRows()
15921603 initialBehavior := o.initialBehavior
1604+ mostVisitedDays := o.mostVisitedDays
1605+ mostVisitedDaysSet := o.mostVisitedDaysSet
15931606
15941607 go func() {
15951608 ctx := o.ctx
@@ -1616,7 +1629,13 @@ func (o *Omnibox) loadInitialHistory(token uint64) {
16161629 err error
16171630 )
16181631 if initialBehavior == entity.OmniboxInitialBehaviorMostVisited {
1619- results, err = o.historyUC.GetMostVisited(ctx, 0)
1632+ if !mostVisitedDaysSet {
1633+ results, err = o.historyUC.GetMostVisited(ctx, defaultOmniboxMostVisitedDays)
1634+ } else if mostVisitedDays == 0 {
1635+ results, err = o.historyUC.GetMostVisited(ctx, 0)
1636+ } else {
1637+ results, err = o.historyUC.GetMostVisited(ctx, mostVisitedDays)
1638+ }
16201639 if err == nil && len(results) > initialLimit {
16211640 results = results[:initialLimit]
16221641 }
@@ -27,11 +27,12 @@ import (
27 )27 )
28 28
29 const (29 const (
30- debounceDelayMs = 5030+ debounceDelayMs = 50
31- endBoxSpacing = 631+ defaultOmniboxMostVisitedDays = 30
32- defaultOmniboxPlaceholder = "Search history or enter URL… (! lists bangs)"32+ endBoxSpacing = 6
33- minGhostInputLength = 133+ defaultOmniboxPlaceholder = "Search history or enter URL… (! lists bangs)"
34- initialBehaviorBadgeTooltip = "Toggle default history order (Ctrl+R)"34+ minGhostInputLength = 1
35+ initialBehaviorBadgeTooltip = "Toggle default history order (Ctrl+R)"
35 )36 )
36 37
37 type favoriteRowIndicatorUpdate struct {38 type favoriteRowIndicatorUpdate struct {
@@ -115,6 +116,8 @@ type Omnibox struct {
115 shortcutsUC *usecase.SearchShortcutsUseCase116 shortcutsUC *usecase.SearchShortcutsUseCase
116 defaultSearch string117 defaultSearch string
117 initialBehavior entity.OmniboxInitialBehavior118 initialBehavior entity.OmniboxInitialBehavior
119+ mostVisitedDays int
120+ mostVisitedDaysSet bool
118 saveInitialBehaviorFn func(context.Context, entity.OmniboxInitialBehavior) error121 saveInitialBehaviorFn func(context.Context, entity.OmniboxInitialBehavior) error
119 ctx context.Context122 ctx context.Context
120 123
@@ -161,6 +164,8 @@ type OmniboxConfig struct {
161 ShortcutsUC *usecase.SearchShortcutsUseCase164 ShortcutsUC *usecase.SearchShortcutsUseCase
162 DefaultSearch string165 DefaultSearch string
163 InitialBehavior entity.OmniboxInitialBehavior166 InitialBehavior entity.OmniboxInitialBehavior
167+ MostVisitedDays int
168+ MostVisitedDaysSet bool
164 SaveInitialBehavior func(ctx context.Context, behavior entity.OmniboxInitialBehavior) error169 SaveInitialBehavior func(ctx context.Context, behavior entity.OmniboxInitialBehavior) error
165 UIScale float64 // UI scale for favicon sizing170 UIScale float64 // UI scale for favicon sizing
166 OnNavigate func(url string) // Callback when user navigates via omnibox171 OnNavigate func(url string) // Callback when user navigates via omnibox
@@ -181,6 +186,10 @@ func NewOmnibox(ctx context.Context, cfg OmniboxConfig) *Omnibox {
181 if uiScale <= 0 {186 if uiScale <= 0 {
182 uiScale = 1.0187 uiScale = 1.0
183 }188 }
189+ mostVisitedDays := cfg.MostVisitedDays
190+ if !cfg.MostVisitedDaysSet {
191+ mostVisitedDays = defaultOmniboxMostVisitedDays
192+ }
184 193
185 sizeCfg := ResolveModalSizeConfig(cfg.SizeConfig, OmniboxSizeDefaults)194 sizeCfg := ResolveModalSizeConfig(cfg.SizeConfig, OmniboxSizeDefaults)
186 195
@@ -194,6 +203,8 @@ func NewOmnibox(ctx context.Context, cfg OmniboxConfig) *Omnibox {
194 shortcutsUC: cfg.ShortcutsUC,203 shortcutsUC: cfg.ShortcutsUC,
195 defaultSearch: cfg.DefaultSearch,204 defaultSearch: cfg.DefaultSearch,
196 initialBehavior: cfg.InitialBehavior,205 initialBehavior: cfg.InitialBehavior,
206+ mostVisitedDays: mostVisitedDays,
207+ mostVisitedDaysSet: cfg.MostVisitedDaysSet,
197 saveInitialBehaviorFn: cfg.SaveInitialBehavior,208 saveInitialBehaviorFn: cfg.SaveInitialBehavior,
198 onToast: cfg.OnToast,209 onToast: cfg.OnToast,
199 onAccentKeyPress: cfg.OnAccentKeyPress,210 onAccentKeyPress: cfg.OnAccentKeyPress,
@@ -1590,6 +1601,8 @@ func (o *Omnibox) loadInitialHistory(token uint64) {
1590 // Capture effective result limit on the GTK main thread before spawning goroutine1601 // Capture effective result limit on the GTK main thread before spawning goroutine
1591 initialLimit := o.effectiveMaxRows()1602 initialLimit := o.effectiveMaxRows()
1592 initialBehavior := o.initialBehavior1603 initialBehavior := o.initialBehavior
1604+ mostVisitedDays := o.mostVisitedDays
1605+ mostVisitedDaysSet := o.mostVisitedDaysSet
1593 1606
1594 go func() {1607 go func() {
1595 ctx := o.ctx1608 ctx := o.ctx
@@ -1616,7 +1629,13 @@ func (o *Omnibox) loadInitialHistory(token uint64) {
1616 err error1629 err error
1617 )1630 )
1618 if initialBehavior == entity.OmniboxInitialBehaviorMostVisited {1631 if initialBehavior == entity.OmniboxInitialBehaviorMostVisited {
1619- results, err = o.historyUC.GetMostVisited(ctx, 0)1632+ if !mostVisitedDaysSet {
1633+ results, err = o.historyUC.GetMostVisited(ctx, defaultOmniboxMostVisitedDays)
1634+ } else if mostVisitedDays == 0 {
1635+ results, err = o.historyUC.GetMostVisited(ctx, 0)
1636+ } else {
1637+ results, err = o.historyUC.GetMostVisited(ctx, mostVisitedDays)
1638+ }
1620 if err == nil && len(results) > initialLimit {1639 if err == nil && len(results) > initialLimit {
1621 results = results[:initialLimit]1640 results = results[:initialLimit]
1622 }1641 }
M internal/ui/component/omnibox_initial_behavior_test.go +79 -0
@@ -178,3 +178,82 @@ func TestOmniboxLoadInitialHistory_UsesCapturedInitialBehavior(t *testing.T) {
178178 t.Fatal("timed out waiting for recent-history load")
179179 }
180180 }
181+
182+func TestOmniboxLoadInitialHistory_MostVisitedUsesThirtyDayWindow(t *testing.T) {
183+ repo := repomocks.NewMockHistoryRepository(t)
184+ done := make(chan struct{})
185+ repo.EXPECT().GetMostVisited(mock.Anything, 30).RunAndReturn(
186+ func(context.Context, int) ([]*entity.HistoryEntry, error) {
187+ close(done)
188+ return []*entity.HistoryEntry{{URL: "https://example.com"}}, nil
189+ },
190+ )
191+
192+ o := &Omnibox{
193+ historyUC: appusecase.NewSearchHistoryUseCase(repo),
194+ initialBehavior: entity.OmniboxInitialBehaviorMostVisited,
195+ ctx: context.Background(),
196+ }
197+
198+ o.loadInitialHistory(1)
199+
200+ select {
201+ case <-done:
202+ case <-time.After(2 * time.Second):
203+ t.Fatal("timed out waiting for most-visited load")
204+ }
205+}
206+
207+func TestOmniboxLoadInitialHistory_MostVisitedUsesConfiguredWindow(t *testing.T) {
208+ repo := repomocks.NewMockHistoryRepository(t)
209+ done := make(chan struct{})
210+ repo.EXPECT().GetMostVisited(mock.Anything, 7).RunAndReturn(
211+ func(context.Context, int) ([]*entity.HistoryEntry, error) {
212+ close(done)
213+ return []*entity.HistoryEntry{{URL: "https://example.com"}}, nil
214+ },
215+ )
216+
217+ o := &Omnibox{
218+ historyUC: appusecase.NewSearchHistoryUseCase(repo),
219+ initialBehavior: entity.OmniboxInitialBehaviorMostVisited,
220+ mostVisitedDays: 7,
221+ mostVisitedDaysSet: true,
222+ ctx: context.Background(),
223+ }
224+
225+ o.loadInitialHistory(1)
226+
227+ select {
228+ case <-done:
229+ case <-time.After(2 * time.Second):
230+ t.Fatal("timed out waiting for configured most-visited load")
231+ }
232+}
233+
234+func TestOmniboxLoadInitialHistory_MostVisitedZeroWindowUsesAllHistory(t *testing.T) {
235+ repo := repomocks.NewMockHistoryRepository(t)
236+ done := make(chan struct{})
237+ repo.EXPECT().GetAllMostVisited(mock.Anything).RunAndReturn(
238+ func(context.Context) ([]*entity.HistoryEntry, error) {
239+ close(done)
240+ return []*entity.HistoryEntry{{URL: "https://example.com"}}, nil
241+ },
242+ )
243+
244+ o := &Omnibox{
245+ historyUC: appusecase.NewSearchHistoryUseCase(repo),
246+ initialBehavior: entity.OmniboxInitialBehaviorMostVisited,
247+ mostVisitedDays: 0,
248+ mostVisitedDaysSet: true,
249+ ctx: context.Background(),
250+ }
251+
252+ o.loadInitialHistory(1)
253+
254+ select {
255+ case <-done:
256+ case <-time.After(2 * time.Second):
257+ t.Fatal("timed out waiting for all-history most-visited load")
258+ }
259+}
@@ -178,3 +178,82 @@ func TestOmniboxLoadInitialHistory_UsesCapturedInitialBehavior(t *testing.T) {
178 t.Fatal("timed out waiting for recent-history load")178 t.Fatal("timed out waiting for recent-history load")
179 }179 }
180 }180 }
181+
182+func TestOmniboxLoadInitialHistory_MostVisitedUsesThirtyDayWindow(t *testing.T) {
183+ repo := repomocks.NewMockHistoryRepository(t)
184+ done := make(chan struct{})
185+ repo.EXPECT().GetMostVisited(mock.Anything, 30).RunAndReturn(
186+ func(context.Context, int) ([]*entity.HistoryEntry, error) {
187+ close(done)
188+ return []*entity.HistoryEntry{{URL: "https://example.com"}}, nil
189+ },
190+ )
191+
192+ o := &Omnibox{
193+ historyUC: appusecase.NewSearchHistoryUseCase(repo),
194+ initialBehavior: entity.OmniboxInitialBehaviorMostVisited,
195+ ctx: context.Background(),
196+ }
197+
198+ o.loadInitialHistory(1)
199+
200+ select {
201+ case <-done:
202+ case <-time.After(2 * time.Second):
203+ t.Fatal("timed out waiting for most-visited load")
204+ }
205+}
206+
207+func TestOmniboxLoadInitialHistory_MostVisitedUsesConfiguredWindow(t *testing.T) {
208+ repo := repomocks.NewMockHistoryRepository(t)
209+ done := make(chan struct{})
210+ repo.EXPECT().GetMostVisited(mock.Anything, 7).RunAndReturn(
211+ func(context.Context, int) ([]*entity.HistoryEntry, error) {
212+ close(done)
213+ return []*entity.HistoryEntry{{URL: "https://example.com"}}, nil
214+ },
215+ )
216+
217+ o := &Omnibox{
218+ historyUC: appusecase.NewSearchHistoryUseCase(repo),
219+ initialBehavior: entity.OmniboxInitialBehaviorMostVisited,
220+ mostVisitedDays: 7,
221+ mostVisitedDaysSet: true,
222+ ctx: context.Background(),
223+ }
224+
225+ o.loadInitialHistory(1)
226+
227+ select {
228+ case <-done:
229+ case <-time.After(2 * time.Second):
230+ t.Fatal("timed out waiting for configured most-visited load")
231+ }
232+}
233+
234+func TestOmniboxLoadInitialHistory_MostVisitedZeroWindowUsesAllHistory(t *testing.T) {
235+ repo := repomocks.NewMockHistoryRepository(t)
236+ done := make(chan struct{})
237+ repo.EXPECT().GetAllMostVisited(mock.Anything).RunAndReturn(
238+ func(context.Context) ([]*entity.HistoryEntry, error) {
239+ close(done)
240+ return []*entity.HistoryEntry{{URL: "https://example.com"}}, nil
241+ },
242+ )
243+
244+ o := &Omnibox{
245+ historyUC: appusecase.NewSearchHistoryUseCase(repo),
246+ initialBehavior: entity.OmniboxInitialBehaviorMostVisited,
247+ mostVisitedDays: 0,
248+ mostVisitedDaysSet: true,
249+ ctx: context.Background(),
250+ }
251+
252+ o.loadInitialHistory(1)
253+
254+ select {
255+ case <-done:
256+ case <-time.After(2 * time.Second):
257+ t.Fatal("timed out waiting for all-history most-visited load")
258+ }
259+}

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