v0.30.0 eb3068f

browser_window_history_sidebar.go RawHistoryFolder

1package ui
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/bnema/dumber/internal/application/port"
8	"github.com/bnema/dumber/internal/application/usecase"
9	"github.com/bnema/dumber/internal/logging"
10	"github.com/bnema/dumber/internal/ui/component"
11	"github.com/bnema/dumber/internal/ui/window"
12)
13
14// initHistorySidebar creates and mounts the history sidebar into the
15// browser window's sidebar container. The sidebar is hidden by default.
16func (bw *browserWindow) initHistorySidebar(ctx context.Context, a *App) {
17	if bw == nil || a == nil || bw.mainWindow == nil || a.deps == nil || a.deps.HistoryUC == nil {
18		return
19	}
20	log := logging.FromContext(ctx)
21
22	cfg := a.buildHistorySidebarConfig(bw)
23
24	sidebar := component.NewHistorySidebar(ctx, cfg)
25	if sidebar == nil {
26		log.Warn().Msg("failed to create history sidebar")
27		return
28	}
29
30	bw.historySidebar = sidebar
31
32	// Mount into the main window's sidebar box.
33	bw.mainWindow.SetSidebarWidget(sidebar.Widget())
34	bw.historySidebar.Hide()
35	bw.mainWindow.SetSidebarVisible(false)
36	bw.sidebarVisible = false
37
38	// Apply sidebar width from config, falling back to the default 320px.
39	// The width is clamped to [280, 380] by SetSidebarWidth internally.
40	bw.applySidebarWidthConfig(a)
41
42	log.Debug().Msg("history sidebar initialized")
43}
44
45// buildHistorySidebarConfig constructs the HistorySidebarConfig for the given
46// browser window. Extracted from initHistorySidebar for testability.
47func (a *App) buildHistorySidebarConfig(bw *browserWindow) component.HistorySidebarConfig {
48	var historyUC port.HistorySidebarHistory
49	if a.deps != nil {
50		historyUC = a.deps.HistoryUC
51	}
52
53	return component.HistorySidebarConfig{
54		HistoryUC: historyUC,
55		OnNavigate: func(navCtx context.Context, url string) error {
56			return a.navigateHistorySidebarSelection(navCtx, bw, url)
57		},
58		OnNavigateKeepOpen: func(navCtx context.Context, url string) error {
59			return a.navigateHistorySidebarSelection(navCtx, bw, url)
60		},
61		OnOpenInNewPane: func(splitCtx context.Context, url string) error {
62			if a.wsCoord == nil || !a.hasBrowserWindow(bw) {
63				return nil
64			}
65			a.activateBrowserWindow(bw)
66			return a.wsCoord.SplitWithURL(splitCtx, usecase.SplitRight, url)
67		},
68		OnClose: func() {
69			a.hideAndRestoreFocusForBrowserWindow(bw)
70		},
71	}
72}
73
74func (a *App) navigateHistorySidebarSelection(ctx context.Context, bw *browserWindow, url string) error {
75	if a == nil || bw == nil || !a.hasBrowserWindow(bw) {
76		return nil
77	}
78	return a.navigateFromBrowserWindow(ctx, bw, url)
79}
80
81// toggleHistorySidebar toggles sidebar visibility.
82func (bw *browserWindow) toggleHistorySidebar() {
83	if bw == nil || bw.historySidebar == nil {
84		return
85	}
86
87	if bw.sidebarVisible {
88		bw.hideHistorySidebar()
89	} else {
90		bw.showHistorySidebar()
91	}
92}
93
94// showHistorySidebar makes the sidebar visible and grabs focus for the search
95// entry.
96func (bw *browserWindow) showHistorySidebar() {
97	if bw == nil || bw.historySidebar == nil || bw.mainWindow == nil {
98		return
99	}
100	bw.historySidebar.Show()
101	bw.mainWindow.SetSidebarVisible(true)
102	bw.sidebarVisible = true
103}
104
105// hideHistorySidebar hides the sidebar. Callers should also restore focus
106// to the active content pane after calling this.
107func (bw *browserWindow) hideHistorySidebar() {
108	if bw == nil || bw.historySidebar == nil || bw.mainWindow == nil {
109		return
110	}
111	bw.historySidebar.Hide()
112	bw.mainWindow.SetSidebarVisible(false)
113	bw.sidebarVisible = false
114}
115
116// historySidebarWidthConfig extracts the config-backed sidebar width and
117// applies it via the MainWindow.SetSidebarWidth path. It is called during
118// initialization and can be reused if config is reloaded at runtime.
119func historySidebarWidthConfig(widthPx int) window.SidebarWidthConfig {
120	cfg := window.SidebarDefaultWidth()
121	if widthPx > 0 {
122		cfg.WidthPx = widthPx
123	}
124	return cfg
125}
126
127// applySidebarWidthConfig applies the config-backed sidebar width to the
128// main window's sidebar.
129func (bw *browserWindow) applySidebarWidthConfig(a *App) {
130	if bw == nil || bw.mainWindow == nil || a == nil {
131		return
132	}
133	bw.mainWindow.SetSidebarWidth(historySidebarWidthConfig(a.runtimeConfigSnapshot().UI.SidebarWidth))
134}
135
136// toggleHistorySidebarAction is the keyboard-action handler for toggling the
137// history sidebar on the last focused browser window.
138func (a *App) toggleHistorySidebarAction(ctx context.Context) error {
139	bw := a.lastFocusedBrowserWindow()
140	if bw == nil {
141		return fmt.Errorf("history sidebar unavailable: no focused browser window")
142	}
143	if bw.historySidebar == nil {
144		return fmt.Errorf("history sidebar unavailable: native sidebar not initialized")
145	}
146	if bw.sidebarVisible {
147		a.hideAndRestoreFocusForBrowserWindow(bw)
148		return nil
149	}
150	bw.toggleHistorySidebar()
151	return nil
152}

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