1package webkit
2
3import (
4 "fmt"
5 "log"
6
7 "github.com/bnema/dumber/assets"
8 "github.com/bnema/dumber/internal/config"
9 webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6"
10)
11
12// SetupUserContentManager configures UserContentManager for the WebView
13// This injects GUI scripts at document-start and registers message handlers
14func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, webviewID uint64) error {
15 if view == nil {
16 return nil
17 }
18
19 // Get the UserContentManager from the WebView
20 ucm := view.UserContentManager()
21 if ucm == nil {
22 log.Printf("[webkit] UserContentManager is nil, skipping script injection")
23 return nil
24 }
25
26 // Inject webview ID FIRST, so GUI scripts can access it immediately
27 // Note: webviewID is uint64, formatted as number (not string) to avoid any injection concerns
28 webviewIDScript := fmt.Sprintf(`
29 window.__dumber_webview_id = %d;
30 console.log('[webkit] WebView ID set in JavaScript:', window.__dumber_webview_id);
31 `, webviewID)
32 ucm.AddScript(webkit.NewUserScript(
33 webviewIDScript,
34 webkit.UserContentInjectTopFrame, // Only top frame needs the webview ID
35 webkit.UserScriptInjectAtDocumentStart,
36 nil,
37 nil,
38 ))
39 log.Printf("[webkit] Injected webview ID script for ID: %d", webviewID)
40
41 // Inject GTK theme detection SECOND, before color-scheme script
42 // The color-scheme.ts expects window.__dumber_gtk_prefers_dark to be set
43 // Respect the ColorScheme config setting
44 cfg := config.Get()
45 var prefersDark bool
46 switch cfg.Appearance.ColorScheme {
47 case "prefer-dark":
48 prefersDark = true
49 log.Printf("[webkit] Using config-forced dark theme (color_scheme: prefer-dark)")
50 case "prefer-light":
51 prefersDark = false
52 log.Printf("[webkit] Using config-forced light theme (color_scheme: prefer-light)")
53 default:
54 // "default" or empty - follow system GTK preference
55 prefersDark = PrefersDarkTheme()
56 log.Printf("[webkit] Using system GTK theme preference (color_scheme: %s)", cfg.Appearance.ColorScheme)
57 }
58
59 gtkThemeScript := fmt.Sprintf(`window.__dumber_gtk_prefers_dark = %t;`, prefersDark)
60 ucm.AddScript(webkit.NewUserScript(
61 gtkThemeScript,
62 webkit.UserContentInjectTopFrame, // Only top frame needs theme preference
63 webkit.UserScriptInjectAtDocumentStart,
64 nil,
65 nil,
66 ))
67 log.Printf("[webkit] Injected theme preference: prefersDark=%t", prefersDark)
68
69 // Inject palette config SECOND, before GUI scripts
70 // The GUI expects window.__dumber_palette = { "light": {...}, "dark": {...} }
71 if appearanceConfigJSON != "" {
72 paletteScript := fmt.Sprintf(`window.__dumber_palette = %s;`, appearanceConfigJSON)
73 ucm.AddScript(webkit.NewUserScript(
74 paletteScript,
75 webkit.UserContentInjectTopFrame, // Only top frame needs palette config
76 webkit.UserScriptInjectAtDocumentStart,
77 nil,
78 nil,
79 ))
80 log.Printf("[webkit] Injected palette config at document-start (%d bytes)", len(paletteScript))
81 }
82
83 // Inject color-scheme script at document-start
84 if assets.ColorSchemeScript != "" {
85 ucm.AddScript(webkit.NewUserScript(
86 assets.ColorSchemeScript,
87 webkit.UserContentInjectTopFrame, // Only top frame needs color scheme detection
88 webkit.UserScriptInjectAtDocumentStart,
89 nil, // whitelist (nil = all)
90 nil, // blacklist (nil = none)
91 ))
92 log.Printf("[webkit] Injected color-scheme script (%d bytes)", len(assets.ColorSchemeScript))
93 }
94
95 // Inject main-world script at document-start
96 if assets.MainWorldScript != "" {
97 ucm.AddScript(webkit.NewUserScript(
98 assets.MainWorldScript,
99 webkit.UserContentInjectTopFrame, // Only top frame needs bridge/UI functionality
100 webkit.UserScriptInjectAtDocumentStart,
101 nil,
102 nil,
103 ))
104 log.Printf("[webkit] Injected main-world script (%d bytes)", len(assets.MainWorldScript))
105 }
106
107 // Inject GUI controls script at document-start
108 if assets.GUIScript != "" {
109 ucm.AddScript(webkit.NewUserScript(
110 assets.GUIScript,
111 webkit.UserContentInjectTopFrame, // Only top frame needs GUI controls
112 webkit.UserScriptInjectAtDocumentStart,
113 nil,
114 nil,
115 ))
116 log.Printf("[webkit] Injected GUI controls script (%d bytes)", len(assets.GUIScript))
117 }
118
119 // Register script message handler "dumber" for communication from JS
120 // Pass empty string for worldName to use the default world
121 if !ucm.RegisterScriptMessageHandler("dumber", "") {
122 log.Printf("[webkit] Warning: failed to register 'dumber' script message handler")
123 } else {
124 log.Printf("[webkit] Registered 'dumber' script message handler")
125 }
126
127 return nil
128}