detached 79f9593

refactor(services): update browser service for svelte gui integration

commit
79f9593ac5967ea81e34dd870971bb22bf837a54
author
bamen · · 2025-09-15 05:21 UTC
parent
d70b2b0
signature
unsigned
1 file · +65 -18
M services/browser_service.go +65 -18
@@ -7,6 +7,7 @@ import (
77 "embed"
88 "errors"
99 "fmt"
10+ "log"
1011 "math"
1112 neturl "net/url"
1213 "time"
@@ -40,7 +41,7 @@ type BrowserService struct {
4041 windowTitleUpdater WindowTitleUpdater
4142 webView *webkit.WebView
4243 initialURL string
43- injectableScript string
44+ guiBundle string
4445 }
4546
4647 // ServiceName returns the service name for frontend binding
@@ -76,7 +77,7 @@ func NewBrowserService(cfg *config.Config, queries db.DatabaseQuerier) *BrowserS
7677 windowTitleUpdater: nil,
7778 webView: nil,
7879 initialURL: "",
79- injectableScript: "",
80+ guiBundle: "",
8081 }
8182 }
8283
@@ -90,32 +91,78 @@ func (s *BrowserService) AttachWebView(view *webkit.WebView) {
9091 s.webView = view
9192 }
9293
93-// LoadInjectableScript loads the minified injectable controls script from assets
94-func (s *BrowserService) LoadInjectableScript(assets embed.FS) error {
95- scriptBytes, err := assets.ReadFile("frontend/dist/injected-controls.min.js")
94+// LoadGUIBundle loads the unified GUI bundle from assets
95+func (s *BrowserService) LoadGUIBundle(assets embed.FS) error {
96+ log.Printf("[browser] Attempting to load GUI bundle from assets/gui/gui.min.js")
97+ bundleBytes, err := assets.ReadFile("assets/gui/gui.min.js")
9698 if err != nil {
97- return fmt.Errorf("failed to load injectable script: %w", err)
99+ log.Printf("[browser] ERROR: Failed to load GUI bundle: %v", err)
100+ return fmt.Errorf("failed to load GUI bundle: %w", err)
98101 }
99- s.injectableScript = string(scriptBytes)
102+ s.guiBundle = string(bundleBytes)
103+ log.Printf("[browser] GUI bundle loaded successfully into browser service, size: %d bytes", len(bundleBytes))
100104 return nil
101105 }
102106
103-// InjectControlScript injects the global controls script into the current page
104-func (s *BrowserService) InjectControlScript(ctx context.Context) error {
107+// GetGUIBundle returns the loaded GUI bundle string
108+func (s *BrowserService) GetGUIBundle() string {
109+ return s.guiBundle
110+}
111+
112+// InjectGUIBundle injects the unified GUI bundle and initializes controls
113+func (s *BrowserService) InjectGUIBundle(ctx context.Context) error {
105114 _ = ctx
106115 if s.webView == nil {
107116 return fmt.Errorf("webview not attached")
108117 }
109- if s.injectableScript == "" {
110- return fmt.Errorf("injectable script not loaded")
118+ if s.guiBundle == "" {
119+ return fmt.Errorf("GUI bundle not loaded")
111120 }
112- wrappedScript := "(function(){try{" +
113- "if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',function(){try{" +
114- s.injectableScript +
115- "}catch(e){console.warn('Dumber Browser controls failed to load on DOMContentLoaded:', e);}});}else{" +
116- s.injectableScript +
117- "}}catch(e){console.warn('Dumber Browser controls injection failed:', e);}})();"
118- return s.webView.InjectScript(wrappedScript)
121+
122+ // Inject the unified GUI bundle first
123+ bundleScript := s.guiBundle +
124+ ";(function(){" +
125+ "if(window.__dumber_gui && window.__dumber_gui.initializeControls){" +
126+ "window.__dumber_gui.initializeControls();" +
127+ "console.log('✅ Controls initialized via GUI bundle');" +
128+ "}else{console.error('❌ GUI bundle not properly loaded');}" +
129+ "})();"
130+
131+ return s.webView.InjectScript(bundleScript)
132+}
133+
134+// InjectToastSystem injects the GUI bundle and initializes the toast system
135+func (s *BrowserService) InjectToastSystem(ctx context.Context, theme string) error {
136+ _ = ctx
137+ if s.webView == nil {
138+ return fmt.Errorf("webview not attached")
139+ }
140+ if s.guiBundle == "" {
141+ return fmt.Errorf("GUI bundle not loaded")
142+ }
143+
144+ // Set theme if provided
145+ themeScript := ""
146+ if theme != "" {
147+ themeScript = fmt.Sprintf("window.__dumber_initial_theme = '%s';", theme)
148+ }
149+
150+ // Inject the unified GUI bundle and initialize toast system after DOM ready
151+ toastScript := s.guiBundle + ";" + themeScript +
152+ "(function(){" +
153+ "function initToast(){" +
154+ "if(window.__dumber_gui && window.__dumber_gui.initializeToast){" +
155+ "window.__dumber_gui.initializeToast().then(function(){" +
156+ "console.log('✅ Toast system initialized via GUI bundle');" +
157+ "}).catch(function(e){console.error('❌ Toast initialization failed:', e);});" +
158+ "}else{console.error('❌ GUI bundle not properly loaded');}" +
159+ "}" +
160+ "if(document.readyState==='loading'){" +
161+ "document.addEventListener('DOMContentLoaded',initToast);" +
162+ "}else{initToast();}" +
163+ "})();"
164+
165+ return s.webView.InjectScript(toastScript)
119166 }
120167
121168 // Navigate handles navigation to a URL and records it in history.
@@ -7,6 +7,7 @@ import (
7 "embed"7 "embed"
8 "errors"8 "errors"
9 "fmt"9 "fmt"
10+ "log"
10 "math"11 "math"
11 neturl "net/url"12 neturl "net/url"
12 "time"13 "time"
@@ -40,7 +41,7 @@ type BrowserService struct {
40 windowTitleUpdater WindowTitleUpdater41 windowTitleUpdater WindowTitleUpdater
41 webView *webkit.WebView42 webView *webkit.WebView
42 initialURL string43 initialURL string
43- injectableScript string44+ guiBundle string
44 }45 }
45 46
46 // ServiceName returns the service name for frontend binding47 // ServiceName returns the service name for frontend binding
@@ -76,7 +77,7 @@ func NewBrowserService(cfg *config.Config, queries db.DatabaseQuerier) *BrowserS
76 windowTitleUpdater: nil,77 windowTitleUpdater: nil,
77 webView: nil,78 webView: nil,
78 initialURL: "",79 initialURL: "",
79- injectableScript: "",80+ guiBundle: "",
80 }81 }
81 }82 }
82 83
@@ -90,32 +91,78 @@ func (s *BrowserService) AttachWebView(view *webkit.WebView) {
90 s.webView = view91 s.webView = view
91 }92 }
92 93
93-// LoadInjectableScript loads the minified injectable controls script from assets94+// LoadGUIBundle loads the unified GUI bundle from assets
94-func (s *BrowserService) LoadInjectableScript(assets embed.FS) error {95+func (s *BrowserService) LoadGUIBundle(assets embed.FS) error {
95- scriptBytes, err := assets.ReadFile("frontend/dist/injected-controls.min.js")96+ log.Printf("[browser] Attempting to load GUI bundle from assets/gui/gui.min.js")
97+ bundleBytes, err := assets.ReadFile("assets/gui/gui.min.js")
96 if err != nil {98 if err != nil {
97- return fmt.Errorf("failed to load injectable script: %w", err)99+ log.Printf("[browser] ERROR: Failed to load GUI bundle: %v", err)
100+ return fmt.Errorf("failed to load GUI bundle: %w", err)
98 }101 }
99- s.injectableScript = string(scriptBytes)102+ s.guiBundle = string(bundleBytes)
103+ log.Printf("[browser] GUI bundle loaded successfully into browser service, size: %d bytes", len(bundleBytes))
100 return nil104 return nil
101 }105 }
102 106
103-// InjectControlScript injects the global controls script into the current page107+// GetGUIBundle returns the loaded GUI bundle string
104-func (s *BrowserService) InjectControlScript(ctx context.Context) error {108+func (s *BrowserService) GetGUIBundle() string {
109+ return s.guiBundle
110+}
111+
112+// InjectGUIBundle injects the unified GUI bundle and initializes controls
113+func (s *BrowserService) InjectGUIBundle(ctx context.Context) error {
105 _ = ctx114 _ = ctx
106 if s.webView == nil {115 if s.webView == nil {
107 return fmt.Errorf("webview not attached")116 return fmt.Errorf("webview not attached")
108 }117 }
109- if s.injectableScript == "" {118+ if s.guiBundle == "" {
110- return fmt.Errorf("injectable script not loaded")119+ return fmt.Errorf("GUI bundle not loaded")
111 }120 }
112- wrappedScript := "(function(){try{" +121+
113- "if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',function(){try{" +122+ // Inject the unified GUI bundle first
114- s.injectableScript +123+ bundleScript := s.guiBundle +
115- "}catch(e){console.warn('Dumber Browser controls failed to load on DOMContentLoaded:', e);}});}else{" +124+ ";(function(){" +
116- s.injectableScript +125+ "if(window.__dumber_gui && window.__dumber_gui.initializeControls){" +
117- "}}catch(e){console.warn('Dumber Browser controls injection failed:', e);}})();"126+ "window.__dumber_gui.initializeControls();" +
118- return s.webView.InjectScript(wrappedScript)127+ "console.log('✅ Controls initialized via GUI bundle');" +
128+ "}else{console.error('❌ GUI bundle not properly loaded');}" +
129+ "})();"
130+
131+ return s.webView.InjectScript(bundleScript)
132+}
133+
134+// InjectToastSystem injects the GUI bundle and initializes the toast system
135+func (s *BrowserService) InjectToastSystem(ctx context.Context, theme string) error {
136+ _ = ctx
137+ if s.webView == nil {
138+ return fmt.Errorf("webview not attached")
139+ }
140+ if s.guiBundle == "" {
141+ return fmt.Errorf("GUI bundle not loaded")
142+ }
143+
144+ // Set theme if provided
145+ themeScript := ""
146+ if theme != "" {
147+ themeScript = fmt.Sprintf("window.__dumber_initial_theme = '%s';", theme)
148+ }
149+
150+ // Inject the unified GUI bundle and initialize toast system after DOM ready
151+ toastScript := s.guiBundle + ";" + themeScript +
152+ "(function(){" +
153+ "function initToast(){" +
154+ "if(window.__dumber_gui && window.__dumber_gui.initializeToast){" +
155+ "window.__dumber_gui.initializeToast().then(function(){" +
156+ "console.log('✅ Toast system initialized via GUI bundle');" +
157+ "}).catch(function(e){console.error('❌ Toast initialization failed:', e);});" +
158+ "}else{console.error('❌ GUI bundle not properly loaded');}" +
159+ "}" +
160+ "if(document.readyState==='loading'){" +
161+ "document.addEventListener('DOMContentLoaded',initToast);" +
162+ "}else{initToast();}" +
163+ "})();"
164+
165+ return s.webView.InjectScript(toastScript)
119 }166 }
120 167
121 // Navigate handles navigation to a URL and records it in history.168 // Navigate handles navigation to a URL and records it in history.

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