feat(config): use messaging bridge for config instead of fetch/CORS
- Add get_search_shortcuts and get_color_palettes message handlers - Request config via webkit.messageHandlers.dumber bridge - Remove /api/config endpoint (avoids CORS issues with custom schemes) - Remove __PALETTE_JSON__ placeholder injection - Frontend requests config on-demand via messaging callbacks
- commit
- 2cbda566a2676f98cbd4dbecdf81d764a5694db0
- author
- bamen · · 2025-10-06 17:04 UTC
- committer
- bamen · · 2025-10-06 18:48 UTC
- parent
- a01c57b
- signature
- unsigned
7 files · +179 -99
M gui/src/injected/main-world.ts +54 -30
| @@ -20,7 +20,11 @@ declare global { | |||
| 20 | 20 | __dumber_initial_theme?: string; | |
| 21 | 21 | __dumber_setTheme?: (theme: "light" | "dark") => void; | |
| 22 | 22 | __dumber_applyPalette?: (theme: "light" | "dark") => void; | |
| 23 | - | __dumber_palette?: { light?: Record<string, string>; dark?: Record<string, string> }; | |
| 23 | + | __dumber_palette?: Record<string, Record<string, string>>; | |
| 24 | + | __dumber_color_palettes?: (palettes: unknown) => void; | |
| 25 | + | __dumber_color_palettes_error?: (error: string) => void; | |
| 26 | + | __dumber_search_shortcuts?: (data: unknown) => void; | |
| 27 | + | __dumber_search_shortcuts_error?: (error: string) => void; | |
| 24 | 28 | __dumber_applyDomZoom?: (level: number) => void; | |
| 25 | 29 | __dumber_showToast?: ( | |
| 26 | 30 | message: string, | |
| @@ -120,32 +124,58 @@ function detectWindowType(features?: string | null): string { | |||
| 120 | 124 | window.__dumber_webview_id = "__WEBVIEW_ID__"; | |
| 121 | 125 | window.__dumber_is_active = "__WEBVIEW_ACTIVE__" as unknown as boolean; | |
| 122 | 126 | ||
| 123 | - | // Initialize color palette (will be replaced by Go with actual palette JSON) | |
| 124 | - | // Placeholder: __PALETTE_JSON__ | |
| 127 | + | // Request color palettes from Go backend | |
| 125 | 128 | try { | |
| 126 | - | const paletteData = "__PALETTE_JSON__"; | |
| 127 | - | if (paletteData && paletteData !== "__PALETTE_JSON__") { | |
| 128 | - | window.__dumber_palette = JSON.parse(paletteData); | |
| 129 | - | ||
| 130 | - | // Normalize palette keys to lowercase | |
| 131 | - | const normalized: typeof window.__dumber_palette = {}; | |
| 132 | - | if (window.__dumber_palette) { | |
| 133 | - | for (const key in window.__dumber_palette) { | |
| 134 | - | if (Object.prototype.hasOwnProperty.call(window.__dumber_palette, key)) { | |
| 135 | - | normalized[key.toLowerCase()] = window.__dumber_palette[key]; | |
| 129 | + | const bridge = window.webkit?.messageHandlers?.dumber; | |
| 130 | + | if (bridge && typeof bridge.postMessage === "function") { | |
| 131 | + | // Set up callback handler before requesting | |
| 132 | + | window.__dumber_color_palettes = (palettes: unknown) => { | |
| 133 | + | try { | |
| 134 | + | // Type guard to ensure palettes is a valid object | |
| 135 | + | if (typeof palettes !== "object" || palettes === null) { | |
| 136 | + | console.error("[dumber] Invalid palettes received"); | |
| 137 | + | return; | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | window.__dumber_palette = palettes as Record<string, Record<string, string>>; | |
| 141 | + | ||
| 142 | + | // Normalize palette keys to lowercase | |
| 143 | + | const normalized: Record<string, Record<string, string>> = {}; | |
| 144 | + | if (window.__dumber_palette) { | |
| 145 | + | for (const key in window.__dumber_palette) { | |
| 146 | + | if (Object.prototype.hasOwnProperty.call(window.__dumber_palette, key)) { | |
| 147 | + | normalized[key.toLowerCase()] = window.__dumber_palette[key]; | |
| 148 | + | } | |
| 149 | + | } | |
| 150 | + | window.__dumber_palette = normalized; | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | // Apply palette now that we have it | |
| 154 | + | const initialTheme: "light" | "dark" = document.documentElement.classList.contains("dark") ? "dark" : "light"; | |
| 155 | + | window.__dumber_initial_theme = initialTheme; | |
| 156 | + | if (window.__dumber_applyPalette) { | |
| 157 | + | window.__dumber_applyPalette(initialTheme); | |
| 136 | 158 | } | |
| 159 | + | } catch (err) { | |
| 160 | + | console.error("[dumber] Failed to apply received palette", err); | |
| 137 | 161 | } | |
| 138 | - | window.__dumber_palette = normalized; | |
| 139 | - | } | |
| 162 | + | }; | |
| 163 | + | ||
| 164 | + | window.__dumber_color_palettes_error = (error: string) => { | |
| 165 | + | console.error("[dumber] Failed to get color palettes:", error); | |
| 166 | + | }; | |
| 167 | + | ||
| 168 | + | // Request color palettes | |
| 169 | + | bridge.postMessage(JSON.stringify({ type: "get_color_palettes" })); | |
| 140 | 170 | } | |
| 141 | 171 | } catch (err) { | |
| 142 | - | console.error("[dumber] Failed to parse palette JSON", err); | |
| 172 | + | console.error("[dumber] Failed to request color palettes", err); | |
| 143 | 173 | } | |
| 144 | 174 | ||
| 145 | 175 | // Palette application function | |
| 146 | 176 | window.__dumber_applyPalette = (theme: "light" | "dark") => { | |
| 147 | 177 | try { | |
| 148 | - | const palette = window.__dumber_palette?.[theme] || window.__dumber_palette?.light; | |
| 178 | + | const palette = window.__dumber_palette?.[theme] || window.__dumber_palette?.["light"]; | |
| 149 | 179 | if (!palette) { | |
| 150 | 180 | return; | |
| 151 | 181 | } | |
| @@ -172,10 +202,13 @@ function detectWindowType(features?: string | null): string { | |||
| 172 | 202 | ||
| 173 | 203 | const root = document.documentElement; | |
| 174 | 204 | for (const key in tokenMap) { | |
| 175 | - | if (Object.prototype.hasOwnProperty.call(tokenMap, key) && palette[key]) { | |
| 176 | - | root.style.setProperty(tokenMap[key], palette[key]); | |
| 177 | - | if (Object.prototype.hasOwnProperty.call(dynamicMap, key)) { | |
| 178 | - | root.style.setProperty(dynamicMap[key], palette[key]); | |
| 205 | + | if (Object.prototype.hasOwnProperty.call(tokenMap, key)) { | |
| 206 | + | const colorValue = palette[key]; | |
| 207 | + | if (colorValue) { | |
| 208 | + | root.style.setProperty(tokenMap[key], colorValue); | |
| 209 | + | if (Object.prototype.hasOwnProperty.call(dynamicMap, key)) { | |
| 210 | + | root.style.setProperty(dynamicMap[key], colorValue); | |
| 211 | + | } | |
| 179 | 212 | } | |
| 180 | 213 | } | |
| 181 | 214 | } | |
| @@ -188,15 +221,6 @@ function detectWindowType(features?: string | null): string { | |||
| 188 | 221 | } | |
| 189 | 222 | }; | |
| 190 | 223 | ||
| 191 | - | // Apply initial palette based on theme | |
| 192 | - | try { | |
| 193 | - | const initialTheme = document.documentElement.classList.contains("dark") ? "dark" : "light"; | |
| 194 | - | window.__dumber_initial_theme = window.__dumber_initial_theme || initialTheme; | |
| 195 | - | window.__dumber_applyPalette(window.__dumber_initial_theme); | |
| 196 | - | } catch (err) { | |
| 197 | - | console.error("[dumber] Failed to apply initial palette", err); | |
| 198 | - | } | |
| 199 | - | ||
| 200 | 224 | // Theme setter function for GTK theme integration | |
| 201 | 225 | window.__dumber_setTheme = (theme: "light" | "dark") => { | |
| 202 | 226 | window.__dumber_initial_theme = theme; | |
| @@ -20,7 +20,11 @@ declare global { | |||||
| 20 | __dumber_initial_theme?: string; | 20 | __dumber_initial_theme?: string; | ||
| 21 | __dumber_setTheme?: (theme: "light" | "dark") => void; | 21 | __dumber_setTheme?: (theme: "light" | "dark") => void; | ||
| 22 | __dumber_applyPalette?: (theme: "light" | "dark") => void; | 22 | __dumber_applyPalette?: (theme: "light" | "dark") => void; | ||
| 23 | - | __dumber_palette?: { light?: Record<string, string>; dark?: Record<string, string> }; | 23 | + | __dumber_palette?: Record<string, Record<string, string>>; |
| 24 | + | __dumber_color_palettes?: (palettes: unknown) => void; | |||
| 25 | + | __dumber_color_palettes_error?: (error: string) => void; | |||
| 26 | + | __dumber_search_shortcuts?: (data: unknown) => void; | |||
| 27 | + | __dumber_search_shortcuts_error?: (error: string) => void; | |||
| 24 | __dumber_applyDomZoom?: (level: number) => void; | 28 | __dumber_applyDomZoom?: (level: number) => void; | ||
| 25 | __dumber_showToast?: ( | 29 | __dumber_showToast?: ( | ||
| 26 | message: string, | 30 | message: string, | ||
| @@ -120,32 +124,58 @@ function detectWindowType(features?: string | null): string { | |||||
| 120 | window.__dumber_webview_id = "__WEBVIEW_ID__"; | 124 | window.__dumber_webview_id = "__WEBVIEW_ID__"; | ||
| 121 | window.__dumber_is_active = "__WEBVIEW_ACTIVE__" as unknown as boolean; | 125 | window.__dumber_is_active = "__WEBVIEW_ACTIVE__" as unknown as boolean; | ||
| 122 | 126 | ||||
| 123 | - | // Initialize color palette (will be replaced by Go with actual palette JSON) | 127 | + | // Request color palettes from Go backend |
| 124 | - | // Placeholder: __PALETTE_JSON__ | |||
| 125 | try { | 128 | try { | ||
| 126 | - | const paletteData = "__PALETTE_JSON__"; | 129 | + | const bridge = window.webkit?.messageHandlers?.dumber; |
| 127 | - | if (paletteData && paletteData !== "__PALETTE_JSON__") { | 130 | + | if (bridge && typeof bridge.postMessage === "function") { |
| 128 | - | window.__dumber_palette = JSON.parse(paletteData); | 131 | + | // Set up callback handler before requesting |
| 129 | - | 132 | + | window.__dumber_color_palettes = (palettes: unknown) => { | |
| 130 | - | // Normalize palette keys to lowercase | 133 | + | try { |
| 131 | - | const normalized: typeof window.__dumber_palette = {}; | 134 | + | // Type guard to ensure palettes is a valid object |
| 132 | - | if (window.__dumber_palette) { | 135 | + | if (typeof palettes !== "object" || palettes === null) { |
| 133 | - | for (const key in window.__dumber_palette) { | 136 | + | console.error("[dumber] Invalid palettes received"); |
| 134 | - | if (Object.prototype.hasOwnProperty.call(window.__dumber_palette, key)) { | 137 | + | return; |
| 135 | - | normalized[key.toLowerCase()] = window.__dumber_palette[key]; | 138 | + | } |
| 139 | + | ||||
| 140 | + | window.__dumber_palette = palettes as Record<string, Record<string, string>>; | |||
| 141 | + | ||||
| 142 | + | // Normalize palette keys to lowercase | |||
| 143 | + | const normalized: Record<string, Record<string, string>> = {}; | |||
| 144 | + | if (window.__dumber_palette) { | |||
| 145 | + | for (const key in window.__dumber_palette) { | |||
| 146 | + | if (Object.prototype.hasOwnProperty.call(window.__dumber_palette, key)) { | |||
| 147 | + | normalized[key.toLowerCase()] = window.__dumber_palette[key]; | |||
| 148 | + | } | |||
| 149 | + | } | |||
| 150 | + | window.__dumber_palette = normalized; | |||
| 151 | + | } | |||
| 152 | + | ||||
| 153 | + | // Apply palette now that we have it | |||
| 154 | + | const initialTheme: "light" | "dark" = document.documentElement.classList.contains("dark") ? "dark" : "light"; | |||
| 155 | + | window.__dumber_initial_theme = initialTheme; | |||
| 156 | + | if (window.__dumber_applyPalette) { | |||
| 157 | + | window.__dumber_applyPalette(initialTheme); | |||
| 136 | } | 158 | } | ||
| 159 | + | } catch (err) { | |||
| 160 | + | console.error("[dumber] Failed to apply received palette", err); | |||
| 137 | } | 161 | } | ||
| 138 | - | window.__dumber_palette = normalized; | 162 | + | }; |
| 139 | - | } | 163 | + | |
| 164 | + | window.__dumber_color_palettes_error = (error: string) => { | |||
| 165 | + | console.error("[dumber] Failed to get color palettes:", error); | |||
| 166 | + | }; | |||
| 167 | + | ||||
| 168 | + | // Request color palettes | |||
| 169 | + | bridge.postMessage(JSON.stringify({ type: "get_color_palettes" })); | |||
| 140 | } | 170 | } | ||
| 141 | } catch (err) { | 171 | } catch (err) { | ||
| 142 | - | console.error("[dumber] Failed to parse palette JSON", err); | 172 | + | console.error("[dumber] Failed to request color palettes", err); |
| 143 | } | 173 | } | ||
| 144 | 174 | ||||
| 145 | // Palette application function | 175 | // Palette application function | ||
| 146 | window.__dumber_applyPalette = (theme: "light" | "dark") => { | 176 | window.__dumber_applyPalette = (theme: "light" | "dark") => { | ||
| 147 | try { | 177 | try { | ||
| 148 | - | const palette = window.__dumber_palette?.[theme] || window.__dumber_palette?.light; | 178 | + | const palette = window.__dumber_palette?.[theme] || window.__dumber_palette?.["light"]; |
| 149 | if (!palette) { | 179 | if (!palette) { | ||
| 150 | return; | 180 | return; | ||
| 151 | } | 181 | } | ||
| @@ -172,10 +202,13 @@ function detectWindowType(features?: string | null): string { | |||||
| 172 | 202 | ||||
| 173 | const root = document.documentElement; | 203 | const root = document.documentElement; | ||
| 174 | for (const key in tokenMap) { | 204 | for (const key in tokenMap) { | ||
| 175 | - | if (Object.prototype.hasOwnProperty.call(tokenMap, key) && palette[key]) { | 205 | + | if (Object.prototype.hasOwnProperty.call(tokenMap, key)) { |
| 176 | - | root.style.setProperty(tokenMap[key], palette[key]); | 206 | + | const colorValue = palette[key]; |
| 177 | - | if (Object.prototype.hasOwnProperty.call(dynamicMap, key)) { | 207 | + | if (colorValue) { |
| 178 | - | root.style.setProperty(dynamicMap[key], palette[key]); | 208 | + | root.style.setProperty(tokenMap[key], colorValue); |
| 209 | + | if (Object.prototype.hasOwnProperty.call(dynamicMap, key)) { | |||
| 210 | + | root.style.setProperty(dynamicMap[key], colorValue); | |||
| 211 | + | } | |||
| 179 | } | 212 | } | ||
| 180 | } | 213 | } | ||
| 181 | } | 214 | } | ||
| @@ -188,15 +221,6 @@ function detectWindowType(features?: string | null): string { | |||||
| 188 | } | 221 | } | ||
| 189 | }; | 222 | }; | ||
| 190 | 223 | ||||
| 191 | - | // Apply initial palette based on theme | |||
| 192 | - | try { | |||
| 193 | - | const initialTheme = document.documentElement.classList.contains("dark") ? "dark" : "light"; | |||
| 194 | - | window.__dumber_initial_theme = window.__dumber_initial_theme || initialTheme; | |||
| 195 | - | window.__dumber_applyPalette(window.__dumber_initial_theme); | |||
| 196 | - | } catch (err) { | |||
| 197 | - | console.error("[dumber] Failed to apply initial palette", err); | |||
| 198 | - | } | |||
| 199 | - | ||||
| 200 | // Theme setter function for GTK theme integration | 224 | // Theme setter function for GTK theme integration | ||
| 201 | window.__dumber_setTheme = (theme: "light" | "dark") => { | 225 | window.__dumber_setTheme = (theme: "light" | "dark") => { | ||
| 202 | window.__dumber_initial_theme = theme; | 226 | window.__dumber_initial_theme = theme; | ||
M gui/src/services/app.ts +41 -14
| @@ -79,21 +79,48 @@ export class AppService { | |||
| 79 | 79 | } | |
| 80 | 80 | ||
| 81 | 81 | async loadShortcuts(): Promise<void> { | |
| 82 | - | const cfg = await fetch("/api/config").then((r) => r.json()); | |
| 83 | - | const raw = cfg?.search_shortcuts || {}; | |
| 84 | - | // Normalize field casing from backend (supports URL/Description and url/description) | |
| 85 | - | const normalized: Record<string, SearchShortcut> = {}; | |
| 86 | - | for (const [key, value] of Object.entries(raw)) { | |
| 87 | - | const v = value as Record<string, unknown>; | |
| 88 | - | normalized[key] = { | |
| 89 | - | id: 0, | |
| 90 | - | shortcut: key, | |
| 91 | - | url_template: (v.url ?? v.URL ?? "") as string, | |
| 92 | - | description: (v.description ?? v.Description ?? "") as string, | |
| 93 | - | created_at: null, | |
| 82 | + | return new Promise((resolve, reject) => { | |
| 83 | + | // Set up response handler | |
| 84 | + | window.__dumber_search_shortcuts = (data: unknown) => { | |
| 85 | + | // Type guard | |
| 86 | + | if (typeof data !== "object" || data === null) { | |
| 87 | + | reject(new Error("Invalid shortcuts data")); | |
| 88 | + | return; | |
| 89 | + | } | |
| 90 | + | const dataObj = data as Record<string, unknown>; | |
| 91 | + | // Normalize field casing from backend (supports URL/Description and url/description) | |
| 92 | + | const normalized: Record<string, SearchShortcut> = {}; | |
| 93 | + | for (const [key, value] of Object.entries(dataObj)) { | |
| 94 | + | const v = value as Record<string, unknown>; | |
| 95 | + | normalized[key] = { | |
| 96 | + | id: 0, | |
| 97 | + | shortcut: key, | |
| 98 | + | url_template: (v.url ?? v.URL ?? "") as string, | |
| 99 | + | description: (v.description ?? v.Description ?? "") as string, | |
| 100 | + | created_at: null, | |
| 101 | + | }; | |
| 102 | + | } | |
| 103 | + | this.shortcuts = normalized; | |
| 104 | + | resolve(); | |
| 94 | 105 | }; | |
| 95 | - | } | |
| 96 | - | this.shortcuts = normalized; | |
| 106 | + | ||
| 107 | + | window.__dumber_search_shortcuts_error = (error: string) => { | |
| 108 | + | console.error("Failed to load shortcuts:", error); | |
| 109 | + | reject(new Error(error)); | |
| 110 | + | }; | |
| 111 | + | ||
| 112 | + | // Send message to Go backend | |
| 113 | + | const bridge = window.webkit?.messageHandlers?.dumber; | |
| 114 | + | if (bridge && typeof bridge.postMessage === "function") { | |
| 115 | + | bridge.postMessage( | |
| 116 | + | JSON.stringify({ | |
| 117 | + | type: "get_search_shortcuts", | |
| 118 | + | }), | |
| 119 | + | ); | |
| 120 | + | } else { | |
| 121 | + | reject(new Error("WebKit message handler not available")); | |
| 122 | + | } | |
| 123 | + | }); | |
| 97 | 124 | } | |
| 98 | 125 | ||
| 99 | 126 | getHistory(): HistoryEntry[] { | |
| @@ -79,21 +79,48 @@ export class AppService { | |||||
| 79 | } | 79 | } | ||
| 80 | 80 | ||||
| 81 | async loadShortcuts(): Promise<void> { | 81 | async loadShortcuts(): Promise<void> { | ||
| 82 | - | const cfg = await fetch("/api/config").then((r) => r.json()); | 82 | + | return new Promise((resolve, reject) => { |
| 83 | - | const raw = cfg?.search_shortcuts || {}; | 83 | + | // Set up response handler |
| 84 | - | // Normalize field casing from backend (supports URL/Description and url/description) | 84 | + | window.__dumber_search_shortcuts = (data: unknown) => { |
| 85 | - | const normalized: Record<string, SearchShortcut> = {}; | 85 | + | // Type guard |
| 86 | - | for (const [key, value] of Object.entries(raw)) { | 86 | + | if (typeof data !== "object" || data === null) { |
| 87 | - | const v = value as Record<string, unknown>; | 87 | + | reject(new Error("Invalid shortcuts data")); |
| 88 | - | normalized[key] = { | 88 | + | return; |
| 89 | - | id: 0, | 89 | + | } |
| 90 | - | shortcut: key, | 90 | + | const dataObj = data as Record<string, unknown>; |
| 91 | - | url_template: (v.url ?? v.URL ?? "") as string, | 91 | + | // Normalize field casing from backend (supports URL/Description and url/description) |
| 92 | - | description: (v.description ?? v.Description ?? "") as string, | 92 | + | const normalized: Record<string, SearchShortcut> = {}; |
| 93 | - | created_at: null, | 93 | + | for (const [key, value] of Object.entries(dataObj)) { |
| 94 | + | const v = value as Record<string, unknown>; | |||
| 95 | + | normalized[key] = { | |||
| 96 | + | id: 0, | |||
| 97 | + | shortcut: key, | |||
| 98 | + | url_template: (v.url ?? v.URL ?? "") as string, | |||
| 99 | + | description: (v.description ?? v.Description ?? "") as string, | |||
| 100 | + | created_at: null, | |||
| 101 | + | }; | |||
| 102 | + | } | |||
| 103 | + | this.shortcuts = normalized; | |||
| 104 | + | resolve(); | |||
| 94 | }; | 105 | }; | ||
| 95 | - | } | 106 | + | |
| 96 | - | this.shortcuts = normalized; | 107 | + | window.__dumber_search_shortcuts_error = (error: string) => { |
| 108 | + | console.error("Failed to load shortcuts:", error); | |||
| 109 | + | reject(new Error(error)); | |||
| 110 | + | }; | |||
| 111 | + | ||||
| 112 | + | // Send message to Go backend | |||
| 113 | + | const bridge = window.webkit?.messageHandlers?.dumber; | |||
| 114 | + | if (bridge && typeof bridge.postMessage === "function") { | |||
| 115 | + | bridge.postMessage( | |||
| 116 | + | JSON.stringify({ | |||
| 117 | + | type: "get_search_shortcuts", | |||
| 118 | + | }), | |||
| 119 | + | ); | |||
| 120 | + | } else { | |||
| 121 | + | reject(new Error("WebKit message handler not available")); | |||
| 122 | + | } | |||
| 123 | + | }); | |||
| 97 | } | 124 | } | ||
| 98 | 125 | ||||
| 99 | getHistory(): HistoryEntry[] { | 126 | getHistory(): HistoryEntry[] { | ||
M internal/app/api/scheme.go +0 -41
| @@ -2,7 +2,6 @@ package api | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "embed" | |
| 5 | - | "encoding/json" | |
| 6 | 5 | "log" | |
| 7 | 6 | "mime" | |
| 8 | 7 | neturl "net/url" | |
| @@ -19,7 +18,6 @@ type SchemeHandler struct { | |||
| 19 | 18 | assets embed.FS | |
| 20 | 19 | parserService *services.ParserService | |
| 21 | 20 | browserService *services.BrowserService | |
| 22 | - | config *config.Config | |
| 23 | 21 | } | |
| 24 | 22 | ||
| 25 | 23 | // NewSchemeHandler creates a new scheme handler | |
| @@ -27,13 +25,11 @@ func NewSchemeHandler( | |||
| 27 | 25 | assets embed.FS, | |
| 28 | 26 | parserService *services.ParserService, | |
| 29 | 27 | browserService *services.BrowserService, | |
| 30 | - | cfg *config.Config, | |
| 31 | 28 | ) *SchemeHandler { | |
| 32 | 29 | return &SchemeHandler{ | |
| 33 | 30 | assets: assets, | |
| 34 | 31 | parserService: parserService, | |
| 35 | 32 | browserService: browserService, | |
| 36 | - | config: cfg, | |
| 37 | 33 | } | |
| 38 | 34 | } | |
| 39 | 35 | ||
| @@ -56,11 +52,6 @@ func (s *SchemeHandler) Handle(uri string, cfg *config.Config) (string, []byte, | |||
| 56 | 52 | ||
| 57 | 53 | // handleAsset serves static assets from embedded filesystem | |
| 58 | 54 | func (s *SchemeHandler) handleAsset(u *neturl.URL) (string, []byte, bool) { | |
| 59 | - | // Check if this is an API route | |
| 60 | - | if strings.HasPrefix(u.Path, "/api/") { | |
| 61 | - | return s.handleAPI(u) | |
| 62 | - | } | |
| 63 | - | ||
| 64 | 55 | // Resolve target path inside embed FS | |
| 65 | 56 | var rel string | |
| 66 | 57 | if u.Opaque == constants.HomepagePath || (u.Host == constants.HomepagePath && (u.Path == "" || u.Path == "/")) || (u.Host == "" && (u.Path == "" || u.Path == "/")) { | |
| @@ -110,38 +101,6 @@ func (s *SchemeHandler) handleAsset(u *neturl.URL) (string, []byte, bool) { | |||
| 110 | 101 | return mt, data, true | |
| 111 | 102 | } | |
| 112 | 103 | ||
| 113 | - | // handleAPI handles API routes | |
| 114 | - | func (s *SchemeHandler) handleAPI(u *neturl.URL) (string, []byte, bool) { | |
| 115 | - | switch u.Path { | |
| 116 | - | case "/api/config": | |
| 117 | - | return s.handleConfigAPI() | |
| 118 | - | default: | |
| 119 | - | log.Printf("[scheme] API route not found: %s", u.Path) | |
| 120 | - | return "", nil, false | |
| 121 | - | } | |
| 122 | - | } | |
| 123 | - | ||
| 124 | - | // handleConfigAPI returns the application configuration as JSON | |
| 125 | - | func (s *SchemeHandler) handleConfigAPI() (string, []byte, bool) { | |
| 126 | - | if s.config == nil { | |
| 127 | - | log.Printf("[scheme] config is nil, cannot serve /api/config") | |
| 128 | - | return "", nil, false | |
| 129 | - | } | |
| 130 | - | ||
| 131 | - | // Create a response with search shortcuts | |
| 132 | - | response := map[string]interface{}{ | |
| 133 | - | "search_shortcuts": s.config.SearchShortcuts, | |
| 134 | - | } | |
| 135 | - | ||
| 136 | - | data, err := json.Marshal(response) | |
| 137 | - | if err != nil { | |
| 138 | - | log.Printf("[scheme] failed to marshal config: %v", err) | |
| 139 | - | return "", nil, false | |
| 140 | - | } | |
| 141 | - | ||
| 142 | - | return "application/json", data, true | |
| 143 | - | } | |
| 144 | - | ||
| 145 | 104 | // getMimeType determines the MIME type for a given file path | |
| 146 | 105 | func (s *SchemeHandler) getMimeType(filename string) string { | |
| 147 | 106 | ext := strings.ToLower(filepath.Ext(filename)) | |
| @@ -2,7 +2,6 @@ package api | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "embed" | 4 | "embed" | ||
| 5 | - | "encoding/json" | |||
| 6 | "log" | 5 | "log" | ||
| 7 | "mime" | 6 | "mime" | ||
| 8 | neturl "net/url" | 7 | neturl "net/url" | ||
| @@ -19,7 +18,6 @@ type SchemeHandler struct { | |||||
| 19 | assets embed.FS | 18 | assets embed.FS | ||
| 20 | parserService *services.ParserService | 19 | parserService *services.ParserService | ||
| 21 | browserService *services.BrowserService | 20 | browserService *services.BrowserService | ||
| 22 | - | config *config.Config | |||
| 23 | } | 21 | } | ||
| 24 | 22 | ||||
| 25 | // NewSchemeHandler creates a new scheme handler | 23 | // NewSchemeHandler creates a new scheme handler | ||
| @@ -27,13 +25,11 @@ func NewSchemeHandler( | |||||
| 27 | assets embed.FS, | 25 | assets embed.FS, | ||
| 28 | parserService *services.ParserService, | 26 | parserService *services.ParserService, | ||
| 29 | browserService *services.BrowserService, | 27 | browserService *services.BrowserService, | ||
| 30 | - | cfg *config.Config, | |||
| 31 | ) *SchemeHandler { | 28 | ) *SchemeHandler { | ||
| 32 | return &SchemeHandler{ | 29 | return &SchemeHandler{ | ||
| 33 | assets: assets, | 30 | assets: assets, | ||
| 34 | parserService: parserService, | 31 | parserService: parserService, | ||
| 35 | browserService: browserService, | 32 | browserService: browserService, | ||
| 36 | - | config: cfg, | |||
| 37 | } | 33 | } | ||
| 38 | } | 34 | } | ||
| 39 | 35 | ||||
| @@ -56,11 +52,6 @@ func (s *SchemeHandler) Handle(uri string, cfg *config.Config) (string, []byte, | |||||
| 56 | 52 | ||||
| 57 | // handleAsset serves static assets from embedded filesystem | 53 | // handleAsset serves static assets from embedded filesystem | ||
| 58 | func (s *SchemeHandler) handleAsset(u *neturl.URL) (string, []byte, bool) { | 54 | func (s *SchemeHandler) handleAsset(u *neturl.URL) (string, []byte, bool) { | ||
| 59 | - | // Check if this is an API route | |||
| 60 | - | if strings.HasPrefix(u.Path, "/api/") { | |||
| 61 | - | return s.handleAPI(u) | |||
| 62 | - | } | |||
| 63 | - | ||||
| 64 | // Resolve target path inside embed FS | 55 | // Resolve target path inside embed FS | ||
| 65 | var rel string | 56 | var rel string | ||
| 66 | if u.Opaque == constants.HomepagePath || (u.Host == constants.HomepagePath && (u.Path == "" || u.Path == "/")) || (u.Host == "" && (u.Path == "" || u.Path == "/")) { | 57 | if u.Opaque == constants.HomepagePath || (u.Host == constants.HomepagePath && (u.Path == "" || u.Path == "/")) || (u.Host == "" && (u.Path == "" || u.Path == "/")) { | ||
| @@ -110,38 +101,6 @@ func (s *SchemeHandler) handleAsset(u *neturl.URL) (string, []byte, bool) { | |||||
| 110 | return mt, data, true | 101 | return mt, data, true | ||
| 111 | } | 102 | } | ||
| 112 | 103 | ||||
| 113 | - | // handleAPI handles API routes | |||
| 114 | - | func (s *SchemeHandler) handleAPI(u *neturl.URL) (string, []byte, bool) { | |||
| 115 | - | switch u.Path { | |||
| 116 | - | case "/api/config": | |||
| 117 | - | return s.handleConfigAPI() | |||
| 118 | - | default: | |||
| 119 | - | log.Printf("[scheme] API route not found: %s", u.Path) | |||
| 120 | - | return "", nil, false | |||
| 121 | - | } | |||
| 122 | - | } | |||
| 123 | - | ||||
| 124 | - | // handleConfigAPI returns the application configuration as JSON | |||
| 125 | - | func (s *SchemeHandler) handleConfigAPI() (string, []byte, bool) { | |||
| 126 | - | if s.config == nil { | |||
| 127 | - | log.Printf("[scheme] config is nil, cannot serve /api/config") | |||
| 128 | - | return "", nil, false | |||
| 129 | - | } | |||
| 130 | - | ||||
| 131 | - | // Create a response with search shortcuts | |||
| 132 | - | response := map[string]interface{}{ | |||
| 133 | - | "search_shortcuts": s.config.SearchShortcuts, | |||
| 134 | - | } | |||
| 135 | - | ||||
| 136 | - | data, err := json.Marshal(response) | |||
| 137 | - | if err != nil { | |||
| 138 | - | log.Printf("[scheme] failed to marshal config: %v", err) | |||
| 139 | - | return "", nil, false | |||
| 140 | - | } | |||
| 141 | - | ||||
| 142 | - | return "application/json", data, true | |||
| 143 | - | } | |||
| 144 | - | ||||
| 145 | // getMimeType determines the MIME type for a given file path | 104 | // getMimeType determines the MIME type for a given file path | ||
| 146 | func (s *SchemeHandler) getMimeType(filename string) string { | 105 | func (s *SchemeHandler) getMimeType(filename string) string { | ||
| 147 | ext := strings.ToLower(filepath.Ext(filename)) | 106 | ext := strings.ToLower(filepath.Ext(filename)) | ||
M internal/app/browser/browser.go +1 -1
| @@ -139,7 +139,7 @@ func (app *BrowserApp) Initialize() error { | |||
| 139 | 139 | app.browserService = services.NewBrowserService(app.config, app.queries) | |
| 140 | 140 | ||
| 141 | 141 | // Initialize handlers | |
| 142 | - | app.schemeHandler = api.NewSchemeHandler(app.assets, app.parserService, app.browserService, app.config) | |
| 142 | + | app.schemeHandler = api.NewSchemeHandler(app.assets, app.parserService, app.browserService) | |
| 143 | 143 | app.messageHandler = messaging.NewHandler(app.parserService, app.browserService) | |
| 144 | 144 | ||
| 145 | 145 | return nil | |
| @@ -139,7 +139,7 @@ func (app *BrowserApp) Initialize() error { | |||||
| 139 | app.browserService = services.NewBrowserService(app.config, app.queries) | 139 | app.browserService = services.NewBrowserService(app.config, app.queries) | ||
| 140 | 140 | ||||
| 141 | // Initialize handlers | 141 | // Initialize handlers | ||
| 142 | - | app.schemeHandler = api.NewSchemeHandler(app.assets, app.parserService, app.browserService, app.config) | 142 | + | app.schemeHandler = api.NewSchemeHandler(app.assets, app.parserService, app.browserService) |
| 143 | app.messageHandler = messaging.NewHandler(app.parserService, app.browserService) | 143 | app.messageHandler = messaging.NewHandler(app.parserService, app.browserService) | ||
| 144 | 144 | ||||
| 145 | return nil | 145 | return nil | ||
M internal/app/messaging/handler.go +65 -0
| @@ -131,6 +131,10 @@ func (h *Handler) Handle(payload string) { | |||
| 131 | 131 | h.handleConsoleMessage(msg) | |
| 132 | 132 | case "request-webview-id": | |
| 133 | 133 | h.handleWebViewIDRequest(msg) | |
| 134 | + | case "get_search_shortcuts": | |
| 135 | + | h.handleGetSearchShortcuts(msg) | |
| 136 | + | case "get_color_palettes": | |
| 137 | + | h.handleGetColorPalettes(msg) | |
| 134 | 138 | } | |
| 135 | 139 | } | |
| 136 | 140 | ||
| @@ -601,3 +605,64 @@ func (h *Handler) handleWebViewIDRequest(msg Message) { | |||
| 601 | 605 | log.Printf("[messaging] Failed to send webview ID: %v", err) | |
| 602 | 606 | } | |
| 603 | 607 | } | |
| 608 | + | ||
| 609 | + | // handleGetSearchShortcuts sends search shortcuts configuration to JavaScript | |
| 610 | + | func (h *Handler) handleGetSearchShortcuts(msg Message) { | |
| 611 | + | if h.webView == nil { | |
| 612 | + | log.Printf("[messaging] Cannot provide search shortcuts - no webview available") | |
| 613 | + | return | |
| 614 | + | } | |
| 615 | + | ||
| 616 | + | if h.webView.IsDestroyed() { | |
| 617 | + | log.Printf("[messaging] Cannot provide search shortcuts - webview is destroyed") | |
| 618 | + | return | |
| 619 | + | } | |
| 620 | + | ||
| 621 | + | // Get search shortcuts from browser service config | |
| 622 | + | shortcuts, err := h.browserService.GetSearchShortcuts(context.Background()) | |
| 623 | + | if err != nil { | |
| 624 | + | log.Printf("[messaging] Failed to get search shortcuts: %v", err) | |
| 625 | + | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to get search shortcuts')") | |
| 626 | + | return | |
| 627 | + | } | |
| 628 | + | ||
| 629 | + | // Marshal to JSON | |
| 630 | + | b, err := json.Marshal(shortcuts) | |
| 631 | + | if err != nil { | |
| 632 | + | log.Printf("[messaging] Failed to marshal search shortcuts: %v", err) | |
| 633 | + | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to load search shortcuts')") | |
| 634 | + | return | |
| 635 | + | } | |
| 636 | + | ||
| 637 | + | // Inject the search shortcuts into the page | |
| 638 | + | log.Printf("[messaging] Sending search shortcuts to JavaScript") | |
| 639 | + | _ = h.webView.InjectScript("window.__dumber_search_shortcuts && window.__dumber_search_shortcuts(" + string(b) + ")") | |
| 640 | + | } | |
| 641 | + | ||
| 642 | + | // handleGetColorPalettes sends color palettes configuration to JavaScript | |
| 643 | + | func (h *Handler) handleGetColorPalettes(msg Message) { | |
| 644 | + | if h.webView == nil { | |
| 645 | + | log.Printf("[messaging] Cannot provide color palettes - no webview available") | |
| 646 | + | return | |
| 647 | + | } | |
| 648 | + | ||
| 649 | + | if h.webView.IsDestroyed() { | |
| 650 | + | log.Printf("[messaging] Cannot provide color palettes - webview is destroyed") | |
| 651 | + | return | |
| 652 | + | } | |
| 653 | + | ||
| 654 | + | // Get color palettes from browser service config | |
| 655 | + | palettes := h.browserService.GetColorPalettesForMessaging() | |
| 656 | + | ||
| 657 | + | // Marshal to JSON | |
| 658 | + | b, err := json.Marshal(palettes) | |
| 659 | + | if err != nil { | |
| 660 | + | log.Printf("[messaging] Failed to marshal color palettes: %v", err) | |
| 661 | + | _ = h.webView.InjectScript("window.__dumber_color_palettes_error && window.__dumber_color_palettes_error('Failed to load color palettes')") | |
| 662 | + | return | |
| 663 | + | } | |
| 664 | + | ||
| 665 | + | // Inject the color palettes into the page | |
| 666 | + | log.Printf("[messaging] Sending color palettes to JavaScript") | |
| 667 | + | _ = h.webView.InjectScript("window.__dumber_color_palettes && window.__dumber_color_palettes(" + string(b) + ")") | |
| 668 | + | } | |
| @@ -131,6 +131,10 @@ func (h *Handler) Handle(payload string) { | |||||
| 131 | h.handleConsoleMessage(msg) | 131 | h.handleConsoleMessage(msg) | ||
| 132 | case "request-webview-id": | 132 | case "request-webview-id": | ||
| 133 | h.handleWebViewIDRequest(msg) | 133 | h.handleWebViewIDRequest(msg) | ||
| 134 | + | case "get_search_shortcuts": | |||
| 135 | + | h.handleGetSearchShortcuts(msg) | |||
| 136 | + | case "get_color_palettes": | |||
| 137 | + | h.handleGetColorPalettes(msg) | |||
| 134 | } | 138 | } | ||
| 135 | } | 139 | } | ||
| 136 | 140 | ||||
| @@ -601,3 +605,64 @@ func (h *Handler) handleWebViewIDRequest(msg Message) { | |||||
| 601 | log.Printf("[messaging] Failed to send webview ID: %v", err) | 605 | log.Printf("[messaging] Failed to send webview ID: %v", err) | ||
| 602 | } | 606 | } | ||
| 603 | } | 607 | } | ||
| 608 | + | ||||
| 609 | + | // handleGetSearchShortcuts sends search shortcuts configuration to JavaScript | |||
| 610 | + | func (h *Handler) handleGetSearchShortcuts(msg Message) { | |||
| 611 | + | if h.webView == nil { | |||
| 612 | + | log.Printf("[messaging] Cannot provide search shortcuts - no webview available") | |||
| 613 | + | return | |||
| 614 | + | } | |||
| 615 | + | ||||
| 616 | + | if h.webView.IsDestroyed() { | |||
| 617 | + | log.Printf("[messaging] Cannot provide search shortcuts - webview is destroyed") | |||
| 618 | + | return | |||
| 619 | + | } | |||
| 620 | + | ||||
| 621 | + | // Get search shortcuts from browser service config | |||
| 622 | + | shortcuts, err := h.browserService.GetSearchShortcuts(context.Background()) | |||
| 623 | + | if err != nil { | |||
| 624 | + | log.Printf("[messaging] Failed to get search shortcuts: %v", err) | |||
| 625 | + | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to get search shortcuts')") | |||
| 626 | + | return | |||
| 627 | + | } | |||
| 628 | + | ||||
| 629 | + | // Marshal to JSON | |||
| 630 | + | b, err := json.Marshal(shortcuts) | |||
| 631 | + | if err != nil { | |||
| 632 | + | log.Printf("[messaging] Failed to marshal search shortcuts: %v", err) | |||
| 633 | + | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to load search shortcuts')") | |||
| 634 | + | return | |||
| 635 | + | } | |||
| 636 | + | ||||
| 637 | + | // Inject the search shortcuts into the page | |||
| 638 | + | log.Printf("[messaging] Sending search shortcuts to JavaScript") | |||
| 639 | + | _ = h.webView.InjectScript("window.__dumber_search_shortcuts && window.__dumber_search_shortcuts(" + string(b) + ")") | |||
| 640 | + | } | |||
| 641 | + | ||||
| 642 | + | // handleGetColorPalettes sends color palettes configuration to JavaScript | |||
| 643 | + | func (h *Handler) handleGetColorPalettes(msg Message) { | |||
| 644 | + | if h.webView == nil { | |||
| 645 | + | log.Printf("[messaging] Cannot provide color palettes - no webview available") | |||
| 646 | + | return | |||
| 647 | + | } | |||
| 648 | + | ||||
| 649 | + | if h.webView.IsDestroyed() { | |||
| 650 | + | log.Printf("[messaging] Cannot provide color palettes - webview is destroyed") | |||
| 651 | + | return | |||
| 652 | + | } | |||
| 653 | + | ||||
| 654 | + | // Get color palettes from browser service config | |||
| 655 | + | palettes := h.browserService.GetColorPalettesForMessaging() | |||
| 656 | + | ||||
| 657 | + | // Marshal to JSON | |||
| 658 | + | b, err := json.Marshal(palettes) | |||
| 659 | + | if err != nil { | |||
| 660 | + | log.Printf("[messaging] Failed to marshal color palettes: %v", err) | |||
| 661 | + | _ = h.webView.InjectScript("window.__dumber_color_palettes_error && window.__dumber_color_palettes_error('Failed to load color palettes')") | |||
| 662 | + | return | |||
| 663 | + | } | |||
| 664 | + | ||||
| 665 | + | // Inject the color palettes into the page | |||
| 666 | + | log.Printf("[messaging] Sending color palettes to JavaScript") | |||
| 667 | + | _ = h.webView.InjectScript("window.__dumber_color_palettes && window.__dumber_color_palettes(" + string(b) + ")") | |||
| 668 | + | } | |||
M internal/services/browser.go +17 -0
| @@ -677,3 +677,20 @@ func (s *BrowserService) handleFaviconURIChanged(pageURL string, faviconURI stri | |||
| 677 | 677 | log.Printf("[favicon] Failed to create favicon cache: %v", err) | |
| 678 | 678 | } | |
| 679 | 679 | } | |
| 680 | + | ||
| 681 | + | // ColorPalettesResponse holds light and dark palettes for JSON marshaling | |
| 682 | + | type ColorPalettesResponse struct { | |
| 683 | + | Light config.ColorPalette `json:"light"` | |
| 684 | + | Dark config.ColorPalette `json:"dark"` | |
| 685 | + | } | |
| 686 | + | ||
| 687 | + | // GetColorPalettesForMessaging returns the color palettes from config | |
| 688 | + | func (s *BrowserService) GetColorPalettesForMessaging() ColorPalettesResponse { | |
| 689 | + | if s.config == nil { | |
| 690 | + | return ColorPalettesResponse{} | |
| 691 | + | } | |
| 692 | + | return ColorPalettesResponse{ | |
| 693 | + | Light: s.config.Appearance.LightPalette, | |
| 694 | + | Dark: s.config.Appearance.DarkPalette, | |
| 695 | + | } | |
| 696 | + | } | |
| @@ -677,3 +677,20 @@ func (s *BrowserService) handleFaviconURIChanged(pageURL string, faviconURI stri | |||||
| 677 | log.Printf("[favicon] Failed to create favicon cache: %v", err) | 677 | log.Printf("[favicon] Failed to create favicon cache: %v", err) | ||
| 678 | } | 678 | } | ||
| 679 | } | 679 | } | ||
| 680 | + | ||||
| 681 | + | // ColorPalettesResponse holds light and dark palettes for JSON marshaling | |||
| 682 | + | type ColorPalettesResponse struct { | |||
| 683 | + | Light config.ColorPalette `json:"light"` | |||
| 684 | + | Dark config.ColorPalette `json:"dark"` | |||
| 685 | + | } | |||
| 686 | + | ||||
| 687 | + | // GetColorPalettesForMessaging returns the color palettes from config | |||
| 688 | + | func (s *BrowserService) GetColorPalettesForMessaging() ColorPalettesResponse { | |||
| 689 | + | if s.config == nil { | |||
| 690 | + | return ColorPalettesResponse{} | |||
| 691 | + | } | |||
| 692 | + | return ColorPalettesResponse{ | |||
| 693 | + | Light: s.config.Appearance.LightPalette, | |||
| 694 | + | Dark: s.config.Appearance.DarkPalette, | |||
| 695 | + | } | |||
| 696 | + | } | |||
M pkg/webkit/webview_cgo.go +1 -13
| @@ -2212,19 +2212,7 @@ func buildDomBridgeScript(cfg *Config, initialZoom float64, webViewId uintptr, w | |||
| 2212 | 2212 | log.Printf("[webkit] Building script with WebView %s, active state: %s", webViewIdStr, activeState) | |
| 2213 | 2213 | script = strings.ReplaceAll(script, "\"__WEBVIEW_ACTIVE__\"", activeState) | |
| 2214 | 2214 | ||
| 2215 | - | // Inject color palette JSON | |
| 2216 | - | if cfg != nil { | |
| 2217 | - | if paletteJSON, err := json.Marshal(cfg.Colors); err == nil && len(paletteJSON) > 0 { | |
| 2218 | - | script = strings.ReplaceAll(script, "\"__PALETTE_JSON__\"", string(paletteJSON)) | |
| 2219 | - | } else { | |
| 2220 | - | if err != nil { | |
| 2221 | - | log.Printf("[webkit] Failed to marshal color palettes: %v", err) | |
| 2222 | - | } | |
| 2223 | - | script = strings.ReplaceAll(script, "\"__PALETTE_JSON__\"", "null") | |
| 2224 | - | } | |
| 2225 | - | } else { | |
| 2226 | - | script = strings.ReplaceAll(script, "\"__PALETTE_JSON__\"", "null") | |
| 2227 | - | } | |
| 2215 | + | // Color palettes and search shortcuts are now loaded via messaging bridge | |
| 2228 | 2216 | ||
| 2229 | 2217 | log.Printf("[webkit] Loaded main-world script successfully, size: %d bytes", len(scriptBytes)) | |
| 2230 | 2218 | return script | |
| @@ -2212,19 +2212,7 @@ func buildDomBridgeScript(cfg *Config, initialZoom float64, webViewId uintptr, w | |||||
| 2212 | log.Printf("[webkit] Building script with WebView %s, active state: %s", webViewIdStr, activeState) | 2212 | log.Printf("[webkit] Building script with WebView %s, active state: %s", webViewIdStr, activeState) | ||
| 2213 | script = strings.ReplaceAll(script, "\"__WEBVIEW_ACTIVE__\"", activeState) | 2213 | script = strings.ReplaceAll(script, "\"__WEBVIEW_ACTIVE__\"", activeState) | ||
| 2214 | 2214 | ||||
| 2215 | - | // Inject color palette JSON | 2215 | + | // Color palettes and search shortcuts are now loaded via messaging bridge |
| 2216 | - | if cfg != nil { | |||
| 2217 | - | if paletteJSON, err := json.Marshal(cfg.Colors); err == nil && len(paletteJSON) > 0 { | |||
| 2218 | - | script = strings.ReplaceAll(script, "\"__PALETTE_JSON__\"", string(paletteJSON)) | |||
| 2219 | - | } else { | |||
| 2220 | - | if err != nil { | |||
| 2221 | - | log.Printf("[webkit] Failed to marshal color palettes: %v", err) | |||
| 2222 | - | } | |||
| 2223 | - | script = strings.ReplaceAll(script, "\"__PALETTE_JSON__\"", "null") | |||
| 2224 | - | } | |||
| 2225 | - | } else { | |||
| 2226 | - | script = strings.ReplaceAll(script, "\"__PALETTE_JSON__\"", "null") | |||
| 2227 | - | } | |||
| 2228 | 2216 | ||||
| 2229 | log.Printf("[webkit] Loaded main-world script successfully, size: %d bytes", len(scriptBytes)) | 2217 | log.Printf("[webkit] Loaded main-world script successfully, size: %d bytes", len(scriptBytes)) | ||
| 2230 | return script | 2218 | return script | ||