v0.15.0 9006957

pane.go RawHistoryFolder

1package browser
2
3import (
4	"fmt"
5	"time"
6
7	"github.com/bnema/dumber/internal/app/control"
8	"github.com/bnema/dumber/internal/app/messaging"
9	"github.com/bnema/dumber/internal/logging"
10	"github.com/bnema/dumber/pkg/webkit"
11)
12
13// BrowserPane bundles the per-pane WebView and its controllers.
14type BrowserPane struct {
15	webView              *webkit.WebView
16	navigationController *control.NavigationController
17	zoomController       *control.ZoomController
18	clipboardController  *control.ClipboardController
19	messageHandler       *messaging.Handler
20	shortcutHandler      *ShortcutHandler
21
22	// Multi-pane support
23	id            string
24	hasGUI        bool
25	guiComponents map[string]bool
26	lastFocusTime time.Time
27}
28
29func (p *BrowserPane) WebView() *webkit.WebView { return p.webView }
30
31func (p *BrowserPane) MessageHandler() *messaging.Handler { return p.messageHandler }
32
33func (p *BrowserPane) NavigationController() *control.NavigationController {
34	return p.navigationController
35}
36
37func (p *BrowserPane) ZoomController() *control.ZoomController { return p.zoomController }
38
39func (p *BrowserPane) ClipboardController() *control.ClipboardController {
40	return p.clipboardController
41}
42
43func (p *BrowserPane) ShortcutHandler() *ShortcutHandler { return p.shortcutHandler }
44
45// ID returns the unique pane identifier
46func (p *BrowserPane) ID() string { return p.id }
47
48// HasGUI returns whether GUI components are injected
49func (p *BrowserPane) HasGUI() bool { return p.hasGUI }
50
51// SetHasGUI marks GUI components as injected
52func (p *BrowserPane) SetHasGUI(has bool) { p.hasGUI = has }
53
54// SetID sets the pane identifier
55func (p *BrowserPane) SetID(id string) { p.id = id }
56
57// UpdateLastFocus updates the last focus time
58func (p *BrowserPane) UpdateLastFocus() { p.lastFocusTime = time.Now() }
59
60// Initialize GUI component tracking
61func (p *BrowserPane) initializeGUITracking() {
62	if p.guiComponents == nil {
63		p.guiComponents = make(map[string]bool)
64	}
65}
66
67// HasGUIComponent checks if a specific GUI component is loaded
68func (p *BrowserPane) HasGUIComponent(component string) bool {
69	if p.guiComponents == nil {
70		return false
71	}
72	return p.guiComponents[component]
73}
74
75// SetGUIComponent marks a GUI component as loaded
76func (p *BrowserPane) SetGUIComponent(component string, loaded bool) {
77	p.initializeGUITracking()
78	p.guiComponents[component] = loaded
79}
80
81// Cleanup releases pane resources
82func (p *BrowserPane) Cleanup() {
83	if p == nil {
84		return
85	}
86
87	if p.shortcutHandler != nil {
88		p.shortcutHandler.Detach()
89		logging.Debug(fmt.Sprintf("[pane-%s] Cleaned up shortcuts", p.id))
90	}
91
92	if p.navigationController != nil {
93		p.navigationController.Detach()
94	}
95
96	if p.zoomController != nil {
97		p.zoomController.DetachWebView()
98	}
99
100	if p.clipboardController != nil {
101		p.clipboardController.Detach()
102	}
103
104	if p.messageHandler != nil {
105		p.messageHandler.SetWebView(nil)
106	}
107
108	// Destroy WebView to unregister from global registry and release resources
109	if p.webView != nil && !p.webView.IsDestroyed() {
110		if err := p.webView.Destroy(); err != nil {
111			logging.Error(fmt.Sprintf("[pane-%s] failed to destroy webview: %v", p.id, err))
112		} else {
113			logging.Debug(fmt.Sprintf("[pane-%s] successfully destroyed webview", p.id))
114		}
115	}
116
117	// Clear tracking
118	p.guiComponents = nil
119	p.hasGUI = false
120}
121
122// CleanupFromWorkspace removes this pane from workspace tracking maps and app.panes slice
123// This should be called before Cleanup() to ensure proper workspace state management
124func (p *BrowserPane) CleanupFromWorkspace(wm interface{}) {
125	if p == nil || p.webView == nil {
126		return
127	}
128
129	// Type assert to avoid circular import - workspace manager will pass itself
130	if workspaceManager, ok := wm.(interface {
131		removeFromMaps(*webkit.WebView)
132		removeFromAppPanes(*BrowserPane)
133	}); ok {
134		workspaceManager.removeFromMaps(p.webView)
135		workspaceManager.removeFromAppPanes(p)
136		logging.Debug(fmt.Sprintf("[pane-%s] Cleaned up from workspace tracking", p.id))
137	}
138}

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