v0.31.0 bf0756b

browser_window_favorites_sidebar.go RawHistoryFolder

1package ui
2
3import (
4	"context"
5	"fmt"
6	"strings"
7
8	"github.com/bnema/dumber/internal/application/port"
9	"github.com/bnema/dumber/internal/application/usecase"
10	"github.com/bnema/dumber/internal/logging"
11	"github.com/bnema/dumber/internal/ui/component"
12)
13
14// initFavoritesSidebar creates the native favorites sidebar when favorites are configured.
15func (bw *browserWindow) initFavoritesSidebar(ctx context.Context, a *App) {
16	if bw == nil || a == nil || bw.mainWindow == nil || a.deps == nil || a.deps.FavoritesUC == nil {
17		return
18	}
19	log := logging.FromContext(ctx)
20
21	cfg := a.buildFavoritesSidebarConfig(bw)
22	sidebar := component.NewFavoritesSidebar(ctx, cfg)
23	if sidebar == nil {
24		log.Warn().Msg("failed to create favorites sidebar")
25		return
26	}
27
28	bw.favoritesSidebar = sidebar
29	bw.favoritesSidebar.Hide()
30	bw.applySidebarWidthConfig(a)
31	log.Debug().Msg("favorites sidebar initialized")
32}
33
34//nolint:dupl // Favorites and history sidebar configs intentionally mirror each other for separate component types.
35func (a *App) buildFavoritesSidebarConfig(bw *browserWindow) component.FavoritesSidebarConfig {
36	var favoritesUC port.FavoritesSidebarFavorites
37	if a.deps != nil {
38		favoritesUC = a.deps.FavoritesUC
39	}
40	return component.FavoritesSidebarConfig{
41		FavoritesUC: favoritesUC,
42		OnNavigate: func(navCtx context.Context, url string) error {
43			return a.navigateFavoritesSidebarSelection(navCtx, bw, url)
44		},
45		OnNavigateKeepOpen: func(navCtx context.Context, url string) error {
46			return a.navigateFavoritesSidebarSelection(navCtx, bw, url)
47		},
48		OnOpenInNewPane: func(splitCtx context.Context, url string) error {
49			if a.wsCoord == nil || !a.hasBrowserWindow(bw) {
50				return nil
51			}
52			a.activateBrowserWindow(bw)
53			return a.wsCoord.SplitWithURL(splitCtx, usecase.SplitRight, url)
54		},
55		OnClose: func() {
56			a.hideAndRestoreFocusForBrowserWindow(bw)
57		},
58	}
59}
60
61func (a *App) navigateFavoritesSidebarSelection(ctx context.Context, bw *browserWindow, url string) error {
62	if a == nil || bw == nil || !a.hasBrowserWindow(bw) {
63		return nil
64	}
65	return a.navigateFromBrowserWindow(ctx, bw, url)
66}
67
68func (bw *browserWindow) toggleFavoritesSidebar() {
69	if bw == nil || bw.favoritesSidebar == nil {
70		return
71	}
72	if bw.sidebarVisible && bw.activeSidebarKind == nativeSidebarFavorites {
73		bw.hideFavoritesSidebar()
74		return
75	}
76	bw.showFavoritesSidebar()
77}
78
79func (bw *browserWindow) showFavoritesSidebar() {
80	if bw == nil || bw.favoritesSidebar == nil || bw.mainWindow == nil {
81		return
82	}
83	bw.mountNativeSidebar(nativeSidebarFavorites)
84	bw.favoritesSidebar.Show()
85	bw.mainWindow.SetSidebarVisible(true)
86	bw.sidebarVisible = true
87	bw.activeSidebarKind = nativeSidebarFavorites
88}
89
90func (bw *browserWindow) hideFavoritesSidebar() {
91	if bw == nil || bw.favoritesSidebar == nil || bw.mainWindow == nil {
92		return
93	}
94	bw.favoritesSidebar.Hide()
95	bw.mainWindow.SetSidebarVisible(false)
96	bw.sidebarVisible = false
97	if bw.activeSidebarKind == nativeSidebarFavorites {
98		bw.activeSidebarKind = nativeSidebarNone
99	}
100}
101
102func (a *App) toggleFavoritesSidebarAction(ctx context.Context) error {
103	bw := a.lastFocusedBrowserWindow()
104	if bw == nil {
105		return fmt.Errorf("favorites sidebar unavailable: no focused browser window")
106	}
107	if bw.favoritesSidebar == nil {
108		return fmt.Errorf("favorites sidebar unavailable: native sidebar not initialized")
109	}
110	if bw.sidebarVisible && bw.activeSidebarKind == nativeSidebarFavorites {
111		a.hideAndRestoreFocusForBrowserWindow(bw)
112		return nil
113	}
114	bw.toggleFavoritesSidebar()
115	return nil
116}
117
118func (a *App) toggleCurrentPageFavoriteAction(ctx context.Context) error {
119	if a == nil || a.deps == nil || a.deps.FavoritesUC == nil {
120		return fmt.Errorf("favorites unavailable: usecase not configured")
121	}
122	bw := a.lastFocusedBrowserWindow()
123	_, wv := a.activeWebViewForBrowserWindow(bw)
124	if wv == nil {
125		return fmt.Errorf("favorites unavailable: no active webview")
126	}
127	uri := strings.TrimSpace(wv.URI())
128	if uri == "" {
129		return fmt.Errorf("favorites unavailable: active page has no URI")
130	}
131	result, err := a.deps.FavoritesUC.Toggle(ctx, uri, wv.Title())
132	if err != nil {
133		return err
134	}
135	if result != nil && strings.TrimSpace(result.Message) != "" {
136		a.showToastOnBrowserWindow(ctx, bw, result.Message, component.ToastSuccess,
137			component.WithDuration(component.ToastBriefDurationMs),
138			component.WithPosition(component.ToastPositionBottomRight),
139		)
140	}
141	a.reloadVisibleFavoritesSidebars("current-page-favorite-toggle")
142	return nil
143}
144
145func (a *App) reloadVisibleFavoritesSidebars(reason string) {
146	if a == nil {
147		return
148	}
149	for _, bw := range a.browserWindows {
150		if bw == nil || bw.favoritesSidebar == nil {
151			continue
152		}
153		bw.favoritesSidebar.RequestReloadIfVisible(reason)
154	}
155}

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