feat(omnibox): add Space key to toggle favorites (add in history, remove in favorites)
- commit
- aa98e12f01b9e69729b56e7f2fa1e0a8e68ad2f7
- author
- brice · · 2025-12-14 21:43 UTC
- committer
- brice · · 2025-12-14 17:44 UTC
- parent
- f3b20f3
- signature
- unsigned
2 files · +90 -0
this diff holds invisible characters (direction controls, zero-width characters). They are shown as <U+XXXX> markers, never applied.
M internal/ui/component/omnibox.go +89 -0
| @@ -7,6 +7,7 @@ import ( | |||
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | 9 | "github.com/bnema/dumber/internal/application/usecase" | |
| 10 | + | "github.com/bnema/dumber/internal/domain/entity" | |
| 10 | 11 | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 11 | 12 | "github.com/bnema/dumber/internal/logging" | |
| 12 | 13 | "github.com/jwijenbergh/puregotk/v4/gdk" | |
| @@ -299,6 +300,11 @@ func (o *Omnibox) handleKeyPress(keyval uint, state gdk.ModifierType) bool { | |||
| 299 | 300 | o.toggleViewMode() | |
| 300 | 301 | return true | |
| 301 | 302 | ||
| 303 | + | case uint(gdk.KEY_space): | |
| 304 | + | // Space toggles favorite: add in history mode, remove in favorites mode | |
| 305 | + | o.toggleFavorite() | |
| 306 | + | return true | |
| 307 | + | ||
| 302 | 308 | case uint(gdk.KEY_1), uint(gdk.KEY_2), uint(gdk.KEY_3), uint(gdk.KEY_4), uint(gdk.KEY_5), | |
| 303 | 309 | uint(gdk.KEY_6), uint(gdk.KEY_7), uint(gdk.KEY_8), uint(gdk.KEY_9): | |
| 304 | 310 | if ctrl { | |
| @@ -723,6 +729,89 @@ func (o *Omnibox) navigateToSelected() { | |||
| 723 | 729 | } | |
| 724 | 730 | } | |
| 725 | 731 | ||
| 732 | + | // toggleFavorite adds or removes the selected item from favorites. | |
| 733 | + | // In History mode: adds the selected item to favorites | |
| 734 | + | // In Favorites mode: removes the selected item from favorites | |
| 735 | + | func (o *Omnibox) toggleFavorite() { | |
| 736 | + | log := logging.FromContext(o.ctx) | |
| 737 | + | ||
| 738 | + | o.mu.RLock() | |
| 739 | + | mode := o.viewMode | |
| 740 | + | idx := o.selectedIndex | |
| 741 | + | suggestions := o.suggestions | |
| 742 | + | favorites := o.favorites | |
| 743 | + | o.mu.RUnlock() | |
| 744 | + | ||
| 745 | + | if o.favoritesUC == nil { | |
| 746 | + | log.Warn().Msg("toggle favorite: favoritesUC is nil") | |
| 747 | + | return | |
| 748 | + | } | |
| 749 | + | ||
| 750 | + | if mode == ViewModeHistory { | |
| 751 | + | // Add to favorites | |
| 752 | + | if idx < 0 || idx >= len(suggestions) { | |
| 753 | + | log.Debug().Int("index", idx).Msg("add favorite: invalid selection") | |
| 754 | + | return | |
| 755 | + | } | |
| 756 | + | ||
| 757 | + | s := suggestions[idx] | |
| 758 | + | if s.URL == "" { | |
| 759 | + | log.Debug().Msg("add favorite: empty URL") | |
| 760 | + | return | |
| 761 | + | } | |
| 762 | + | ||
| 763 | + | go func() { | |
| 764 | + | ctx := o.ctx | |
| 765 | + | log := logging.FromContext(ctx) | |
| 766 | + | ||
| 767 | + | log.Debug().Str("url", s.URL).Str("title", s.Title).Msg("adding to favorites") | |
| 768 | + | ||
| 769 | + | input := usecase.AddFavoriteInput{ | |
| 770 | + | URL: s.URL, | |
| 771 | + | Title: s.Title, | |
| 772 | + | } | |
| 773 | + | ||
| 774 | + | fav, err := o.favoritesUC.Add(ctx, input) | |
| 775 | + | if err != nil { | |
| 776 | + | log.Error().Err(err).Str("url", s.URL).Msg("failed to add favorite") | |
| 777 | + | return | |
| 778 | + | } | |
| 779 | + | ||
| 780 | + | log.Info().Str("url", s.URL).Int64("id", int64(fav.ID)).Msg("favorite added from omnibox") | |
| 781 | + | }() | |
| 782 | + | } else { | |
| 783 | + | // Remove from favorites | |
| 784 | + | if idx < 0 || idx >= len(favorites) { | |
| 785 | + | log.Debug().Int("index", idx).Msg("remove favorite: invalid selection") | |
| 786 | + | return | |
| 787 | + | } | |
| 788 | + | ||
| 789 | + | f := favorites[idx] | |
| 790 | + | if f.ID == 0 { | |
| 791 | + | log.Debug().Msg("remove favorite: invalid ID") | |
| 792 | + | return | |
| 793 | + | } | |
| 794 | + | ||
| 795 | + | go func() { | |
| 796 | + | ctx := o.ctx | |
| 797 | + | log := logging.FromContext(ctx) | |
| 798 | + | ||
| 799 | + | log.Debug().Int64("id", f.ID).Str("url", f.URL).Msg("removing from favorites") | |
| 800 | + | ||
| 801 | + | err := o.favoritesUC.Remove(ctx, entity.FavoriteID(f.ID)) | |
| 802 | + | if err != nil { | |
| 803 | + | log.Error().Err(err).Int64("id", f.ID).Msg("failed to remove favorite") | |
| 804 | + | return | |
| 805 | + | } | |
| 806 | + | ||
| 807 | + | log.Info().Int64("id", f.ID).Str("url", f.URL).Msg("favorite removed from omnibox") | |
| 808 | + | ||
| 809 | + | // Refresh favorites list | |
| 810 | + | o.loadFavorites(o.entry.GetText()) | |
| 811 | + | }() | |
| 812 | + | } | |
| 813 | + | } | |
| 814 | + | ||
| 726 | 815 | // buildURL constructs a URL from input, handling search shortcuts. | |
| 727 | 816 | func (o *Omnibox) buildURL(input string) string { | |
| 728 | 817 | if input == "" { | |
| @@ -7,6 +7,7 @@ import ( | |||||
| 7 | "time" | 7 | "time" | ||
| 8 | 8 | ||||
| 9 | "github.com/bnema/dumber/internal/application/usecase" | 9 | "github.com/bnema/dumber/internal/application/usecase" | ||
| 10 | + | "github.com/bnema/dumber/internal/domain/entity" | |||
| 10 | "github.com/bnema/dumber/internal/infrastructure/config" | 11 | "github.com/bnema/dumber/internal/infrastructure/config" | ||
| 11 | "github.com/bnema/dumber/internal/logging" | 12 | "github.com/bnema/dumber/internal/logging" | ||
| 12 | "github.com/jwijenbergh/puregotk/v4/gdk" | 13 | "github.com/jwijenbergh/puregotk/v4/gdk" | ||
| @@ -299,6 +300,11 @@ func (o *Omnibox) handleKeyPress(keyval uint, state gdk.ModifierType) bool { | |||||
| 299 | o.toggleViewMode() | 300 | o.toggleViewMode() | ||
| 300 | return true | 301 | return true | ||
| 301 | 302 | ||||
| 303 | + | case uint(gdk.KEY_space): | |||
| 304 | + | // Space toggles favorite: add in history mode, remove in favorites mode | |||
| 305 | + | o.toggleFavorite() | |||
| 306 | + | return true | |||
| 307 | + | ||||
| 302 | case uint(gdk.KEY_1), uint(gdk.KEY_2), uint(gdk.KEY_3), uint(gdk.KEY_4), uint(gdk.KEY_5), | 308 | case uint(gdk.KEY_1), uint(gdk.KEY_2), uint(gdk.KEY_3), uint(gdk.KEY_4), uint(gdk.KEY_5), | ||
| 303 | uint(gdk.KEY_6), uint(gdk.KEY_7), uint(gdk.KEY_8), uint(gdk.KEY_9): | 309 | uint(gdk.KEY_6), uint(gdk.KEY_7), uint(gdk.KEY_8), uint(gdk.KEY_9): | ||
| 304 | if ctrl { | 310 | if ctrl { | ||
| @@ -723,6 +729,89 @@ func (o *Omnibox) navigateToSelected() { | |||||
| 723 | } | 729 | } | ||
| 724 | } | 730 | } | ||
| 725 | 731 | ||||
| 732 | + | // toggleFavorite adds or removes the selected item from favorites. | |||
| 733 | + | // In History mode: adds the selected item to favorites | |||
| 734 | + | // In Favorites mode: removes the selected item from favorites | |||
| 735 | + | func (o *Omnibox) toggleFavorite() { | |||
| 736 | + | log := logging.FromContext(o.ctx) | |||
| 737 | + | ||||
| 738 | + | o.mu.RLock() | |||
| 739 | + | mode := o.viewMode | |||
| 740 | + | idx := o.selectedIndex | |||
| 741 | + | suggestions := o.suggestions | |||
| 742 | + | favorites := o.favorites | |||
| 743 | + | o.mu.RUnlock() | |||
| 744 | + | ||||
| 745 | + | if o.favoritesUC == nil { | |||
| 746 | + | log.Warn().Msg("toggle favorite: favoritesUC is nil") | |||
| 747 | + | return | |||
| 748 | + | } | |||
| 749 | + | ||||
| 750 | + | if mode == ViewModeHistory { | |||
| 751 | + | // Add to favorites | |||
| 752 | + | if idx < 0 || idx >= len(suggestions) { | |||
| 753 | + | log.Debug().Int("index", idx).Msg("add favorite: invalid selection") | |||
| 754 | + | return | |||
| 755 | + | } | |||
| 756 | + | ||||
| 757 | + | s := suggestions[idx] | |||
| 758 | + | if s.URL == "" { | |||
| 759 | + | log.Debug().Msg("add favorite: empty URL") | |||
| 760 | + | return | |||
| 761 | + | } | |||
| 762 | + | ||||
| 763 | + | go func() { | |||
| 764 | + | ctx := o.ctx | |||
| 765 | + | log := logging.FromContext(ctx) | |||
| 766 | + | ||||
| 767 | + | log.Debug().Str("url", s.URL).Str("title", s.Title).Msg("adding to favorites") | |||
| 768 | + | ||||
| 769 | + | input := usecase.AddFavoriteInput{ | |||
| 770 | + | URL: s.URL, | |||
| 771 | + | Title: s.Title, | |||
| 772 | + | } | |||
| 773 | + | ||||
| 774 | + | fav, err := o.favoritesUC.Add(ctx, input) | |||
| 775 | + | if err != nil { | |||
| 776 | + | log.Error().Err(err).Str("url", s.URL).Msg("failed to add favorite") | |||
| 777 | + | return | |||
| 778 | + | } | |||
| 779 | + | ||||
| 780 | + | log.Info().Str("url", s.URL).Int64("id", int64(fav.ID)).Msg("favorite added from omnibox") | |||
| 781 | + | }() | |||
| 782 | + | } else { | |||
| 783 | + | // Remove from favorites | |||
| 784 | + | if idx < 0 || idx >= len(favorites) { | |||
| 785 | + | log.Debug().Int("index", idx).Msg("remove favorite: invalid selection") | |||
| 786 | + | return | |||
| 787 | + | } | |||
| 788 | + | ||||
| 789 | + | f := favorites[idx] | |||
| 790 | + | if f.ID == 0 { | |||
| 791 | + | log.Debug().Msg("remove favorite: invalid ID") | |||
| 792 | + | return | |||
| 793 | + | } | |||
| 794 | + | ||||
| 795 | + | go func() { | |||
| 796 | + | ctx := o.ctx | |||
| 797 | + | log := logging.FromContext(ctx) | |||
| 798 | + | ||||
| 799 | + | log.Debug().Int64("id", f.ID).Str("url", f.URL).Msg("removing from favorites") | |||
| 800 | + | ||||
| 801 | + | err := o.favoritesUC.Remove(ctx, entity.FavoriteID(f.ID)) | |||
| 802 | + | if err != nil { | |||
| 803 | + | log.Error().Err(err).Int64("id", f.ID).Msg("failed to remove favorite") | |||
| 804 | + | return | |||
| 805 | + | } | |||
| 806 | + | ||||
| 807 | + | log.Info().Int64("id", f.ID).Str("url", f.URL).Msg("favorite removed from omnibox") | |||
| 808 | + | ||||
| 809 | + | // Refresh favorites list | |||
| 810 | + | o.loadFavorites(o.entry.GetText()) | |||
| 811 | + | }() | |||
| 812 | + | } | |||
| 813 | + | } | |||
| 814 | + | ||||
| 726 | // buildURL constructs a URL from input, handling search shortcuts. | 815 | // buildURL constructs a URL from input, handling search shortcuts. | ||
| 727 | func (o *Omnibox) buildURL(input string) string { | 816 | func (o *Omnibox) buildURL(input string) string { | ||
| 728 | if input == "" { | 817 | if input == "" { | ||
A mediakeys/v1/salt +1 -0
| @@ -0,0 +1,1 @@ | |||
| 1 | + | <U+007F>�v�}K⏎̸ | |
| @@ -0,0 +1,1 @@ | |||||
| 1 | + | <U+007F>�v�}K⏎̸ | |||