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