From a62a882791a69d4c0f976ad1039a1323749d410d Mon Sep 17 00:00:00 2001 From: Jordan Wages Date: Tue, 8 Jul 2025 16:54:04 -0500 Subject: [PATCH] Refactor theme detection --- background.js | 20 ++------------------ details.js | 3 ++- modules/themeUtils.js | 20 ++++++++++++++++++++ options/options.js | 17 +++++++---------- 4 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 modules/themeUtils.js diff --git a/background.js b/background.js index 39bea3e..5d4de8d 100644 --- a/background.js +++ b/background.js @@ -29,6 +29,7 @@ let collapseWhitespace = false; let TurndownService = null; let userTheme = 'auto'; let currentTheme = 'light'; +let detectSystemTheme; function normalizeRules(rules) { return Array.isArray(rules) ? rules.map(r => { @@ -50,24 +51,6 @@ function iconPaths(name) { }; } -async function detectSystemTheme() { - try { - const t = await browser.theme.getCurrent(); - const scheme = t?.properties?.color_scheme; - if (scheme === 'dark' || scheme === 'light') { - return scheme; - } - const color = t?.colors?.frame || t?.colors?.toolbar; - if (color && /^#/.test(color)) { - const r = parseInt(color.slice(1, 3), 16); - const g = parseInt(color.slice(3, 5), 16); - const b = parseInt(color.slice(5, 7), 16); - const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255; - return lum < 0.5 ? 'dark' : 'light'; - } - } catch {} - return 'light'; -} const ICONS = { logo: () => 'resources/img/logo.png', @@ -298,6 +281,7 @@ async function clearCacheForMessages(idsInput) { (async () => { logger = await import(browser.runtime.getURL("logger.js")); + ({ detectSystemTheme } = await import(browser.runtime.getURL('modules/themeUtils.js'))); try { AiClassifier = await import(browser.runtime.getURL("modules/AiClassifier.js")); logger.aiLog("AiClassifier imported", { debug: true }); diff --git a/details.js b/details.js index a068bdb..f306e01 100644 --- a/details.js +++ b/details.js @@ -1,8 +1,9 @@ const aiLog = (await import(browser.runtime.getURL("logger.js"))).aiLog; const storage = (globalThis.messenger ?? browser).storage; +const { detectSystemTheme } = await import(browser.runtime.getURL('modules/themeUtils.js')); const { theme } = await storage.local.get('theme'); const mode = (theme || 'auto') === 'auto' - ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') + ? await detectSystemTheme() : theme; document.documentElement.dataset.theme = mode; diff --git a/modules/themeUtils.js b/modules/themeUtils.js new file mode 100644 index 0000000..58728f1 --- /dev/null +++ b/modules/themeUtils.js @@ -0,0 +1,20 @@ +"use strict"; + +export async function detectSystemTheme() { + try { + const t = await browser.theme.getCurrent(); + const scheme = t?.properties?.color_scheme; + if (scheme === 'dark' || scheme === 'light') { + return scheme; + } + const color = t?.colors?.frame || t?.colors?.toolbar; + if (color && /^#/.test(color)) { + const r = parseInt(color.slice(1, 3), 16); + const g = parseInt(color.slice(3, 5), 16); + const b = parseInt(color.slice(5, 7), 16); + const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + return lum < 0.5 ? 'dark' : 'light'; + } + } catch {} + return 'light'; +} diff --git a/options/options.js b/options/options.js index 1fc0a7f..5132807 100644 --- a/options/options.js +++ b/options/options.js @@ -3,6 +3,7 @@ document.addEventListener('DOMContentLoaded', async () => { const logger = await import(browser.runtime.getURL('logger.js')); const AiClassifier = await import(browser.runtime.getURL('modules/AiClassifier.js')); const dataTransfer = await import(browser.runtime.getURL('options/dataTransfer.js')); + const { detectSystemTheme } = await import(browser.runtime.getURL('modules/themeUtils.js')); const defaults = await storage.local.get([ 'endpoint', 'templateName', @@ -42,10 +43,6 @@ document.addEventListener('DOMContentLoaded', async () => { const themeSelect = document.getElementById('theme-select'); themeSelect.value = defaults.theme || 'auto'; - function systemTheme() { - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - } - function updateIcons(theme) { document.querySelectorAll('img[data-icon]').forEach(img => { const name = img.dataset.icon; @@ -58,16 +55,16 @@ document.addEventListener('DOMContentLoaded', async () => { }); } - function applyTheme(setting) { - const mode = setting === 'auto' ? systemTheme() : setting; + async function applyTheme(setting) { + const mode = setting === 'auto' ? await detectSystemTheme() : setting; document.documentElement.dataset.theme = mode; updateIcons(mode); } - applyTheme(themeSelect.value); - themeSelect.addEventListener('change', () => { + await applyTheme(themeSelect.value); + themeSelect.addEventListener('change', async () => { markDirty(); - applyTheme(themeSelect.value); + await applyTheme(themeSelect.value); }); const DEFAULT_AI_PARAMS = { max_tokens: 4096, @@ -486,7 +483,7 @@ document.addEventListener('DOMContentLoaded', async () => { const collapseWhitespace = collapseWhitespaceToggle.checked; const theme = themeSelect.value; await storage.local.set({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging, htmlToMarkdown, stripUrlParams, altTextImages, collapseWhitespace, aiRules: rules, theme }); - applyTheme(theme); + await applyTheme(theme); try { await AiClassifier.setConfig({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging }); logger.setDebug(debugLogging);