diff --git a/tests/api-profile-usage.test.ts b/tests/api-profile-usage.test.ts index b5885e2..661a0ff 100644 --- a/tests/api-profile-usage.test.ts +++ b/tests/api-profile-usage.test.ts @@ -45,3 +45,13 @@ describe("forgetApiProfileUsage", () => { expect(forgetApiProfileUsage("missing")).toBe(false); }); }); + +describe("groups with no used model", () => { + it("shows nothing, so the settings page hides the whole group", () => { + recordApiProfileUsage("used"); + const members = [{ id: "unused-a" }, { id: "unused-b" }]; + expect(filterUsedApiProfiles(members, loadApiProfileUsage(), null)).toEqual( + [], + ); + }); +}); diff --git a/web/app.js b/web/app.js index faea6d5..192d434 100644 --- a/web/app.js +++ b/web/app.js @@ -23,17 +23,7 @@ import { isModuleOpeningWaiting, renderQuestionsCard, } from "./questions-ui.js"; -import { - applyColorTheme, - bumpUiFontScale, - getStoredColorTheme, - getStoredUiFontScale, - initColorTheme, - initUiFontScale, - themeById, - UI_FONT_SCALE_MAX, - UI_FONT_SCALE_MIN, -} from "./theme.js"; +import { initColorTheme, initUiFontScale } from "./theme.js"; import { applyMarkdownRender, initMarkdownRender, @@ -1366,29 +1356,6 @@ function syncMarkdownMenuUi() { }); } -function syncThemeMenuUi() { - const colorId = document.documentElement.getAttribute("data-color-theme") || getStoredColorTheme(); - const percent = getStoredUiFontScale(); - const colorMeta = themeById(colorId); - const sum = $("theme-menu-sum"); - if (sum) { - sum.title = `${colorMeta.name} · 字号 ${percent}`; - if (!sum.querySelector(".hamburger-icon")) { - sum.innerHTML = ``; - } - } - document.querySelectorAll("[data-color-theme].more-theme-item").forEach((btn) => { - btn.classList.toggle("is-active", btn.getAttribute("data-color-theme") === colorMeta.id); - }); - const valueEl = $("theme-font-scale-value"); - if (valueEl) valueEl.textContent = String(percent); - const minus = document.querySelector("[data-font-scale-bump='-1']"); - const plus = document.querySelector("[data-font-scale-bump='1']"); - if (minus) minus.disabled = percent <= UI_FONT_SCALE_MIN; - if (plus) plus.disabled = percent >= UI_FONT_SCALE_MAX; - syncMarkdownMenuUi(); -} - function stashComposerDraftFromDom() { if (skipComposerStash) { skipComposerStash = false; @@ -4598,26 +4565,6 @@ $("btn-save-instance")?.addEventListener("click", () => { void saveCurrentInstance(); }); -$("theme-menu")?.addEventListener("click", (e) => { - const menu = $("theme-menu"); - if (!menu) return; - const colorBtn = e.target.closest("[data-color-theme]"); - if (colorBtn && menu.contains(colorBtn)) { - e.preventDefault(); - applyColorTheme(colorBtn.getAttribute("data-color-theme")); - syncThemeMenuUi(); - menu.removeAttribute("open"); - return; - } - const bumpBtn = e.target.closest("[data-font-scale-bump]"); - if (bumpBtn && menu.contains(bumpBtn)) { - e.preventDefault(); - e.stopPropagation(); - bumpUiFontScale(bumpBtn.getAttribute("data-font-scale-bump")); - syncThemeMenuUi(); - } -}); - $("composer")?.addEventListener("click", (e) => { const menu = e.target.closest("#composer-more-menu"); if (!menu) return; @@ -4764,7 +4711,7 @@ async function init() { initUiFontScale(); initMarkdownRender(); initPlayAuxLlmBubbles(); - syncThemeMenuUi(); + syncMarkdownMenuUi(); try { await Promise.all([loadBooks(), loadPersonas(), loadApiProfiles()]); if (books.length) { diff --git a/web/index.html b/web/index.html index e335ef2..9b7fba5 100644 --- a/web/index.html +++ b/web/index.html @@ -42,6 +42,7 @@ 技能 库 Token + 预设 设置
@@ -54,37 +55,6 @@ -
- - - - -
diff --git a/web/libraries.html b/web/libraries.html index afb5b00..b8d0692 100644 --- a/web/libraries.html +++ b/web/libraries.html @@ -23,6 +23,7 @@ 技能 库 Token + 预设 设置 @@ -127,4 +128,3 @@ - diff --git a/web/modules.html b/web/modules.html index 306ca39..c6f1473 100644 --- a/web/modules.html +++ b/web/modules.html @@ -32,6 +32,7 @@ 技能 库 Token + 预设 设置 diff --git a/web/settings.css b/web/settings.css index 3e4cf7b..d02e4e6 100644 --- a/web/settings.css +++ b/web/settings.css @@ -165,194 +165,6 @@ code { font-weight: 600; } -.st-top-actions { - display: flex; - align-items: center; - justify-content: flex-end; - min-width: 0; -} - -.st-theme-menu { - position: relative; -} - -.st-theme-summary { - width: 32px; - height: 32px; - display: inline-flex; - align-items: center; - justify-content: center; - border: 1px solid var(--st-border-strong); - border-radius: 8px; - background: var(--st-surface); - color: var(--st-muted); - cursor: pointer; - list-style: none; -} - -.st-theme-summary::-webkit-details-marker { - display: none; -} - -.st-theme-summary:hover, -.st-theme-menu[open] .st-theme-summary { - color: var(--st-text); - border-color: var(--st-accent); - background: var(--st-accent-soft); -} - -.st-theme-summary:focus-visible, -.st-theme-item:focus-visible, -.st-font-scale button:focus-visible { - outline: 2px solid color-mix(in srgb, var(--st-accent) 70%, transparent); - outline-offset: 2px; -} - -.st-hamburger, -.st-hamburger::before, -.st-hamburger::after { - width: 16px; - height: 1.5px; - display: block; - border-radius: 2px; - background: currentColor; -} - -.st-hamburger { - position: relative; -} - -.st-hamburger::before, -.st-hamburger::after { - content: ""; - position: absolute; - left: 0; -} - -.st-hamburger::before { - top: -5px; -} - -.st-hamburger::after { - top: 5px; -} - -.st-theme-panel { - position: absolute; - top: calc(100% + 8px); - right: 0; - z-index: 100; - width: 258px; - padding: 10px; - border: 1px solid var(--st-border-strong); - border-radius: 12px; - background: var(--st-surface); - color: var(--st-text); - box-shadow: 0 12px 32px color-mix(in srgb, var(--st-text) 12%, transparent); -} - -.st-theme-label { - padding: 4px 10px 7px; - color: var(--st-muted); - font-size: 12px; - font-weight: 600; -} - -.st-theme-item { - width: 100%; - min-height: 54px; - display: grid; - grid-template-columns: 26px minmax(0, 1fr); - align-items: center; - gap: 10px; - padding: 7px 10px; - border: 0; - border-radius: 8px; - background: transparent; - color: var(--st-text); - text-align: left; - cursor: pointer; -} - -.st-theme-item:hover { - background: color-mix(in srgb, var(--st-surface) 58%, var(--st-bg)); -} - -.st-theme-item.is-active { - background: var(--st-accent-soft); - color: var(--st-accent); -} - -.st-theme-icon { - font-size: 18px; - line-height: 1; - text-align: center; -} - -.st-theme-copy { - display: flex; - flex-direction: column; - min-width: 0; - line-height: 1.35; -} - -.st-theme-name { - font-size: 14px; - font-weight: 650; -} - -.st-theme-desc { - color: var(--st-muted); - font-size: 12px; -} - -.st-theme-item.is-active .st-theme-desc { - color: color-mix(in srgb, var(--st-accent) 72%, var(--st-muted)); -} - -.st-theme-separator { - height: 1px; - margin: 8px 4px; - background: var(--st-border); -} - -.st-font-scale { - display: grid; - grid-template-columns: 38px minmax(0, 1fr) 38px; - align-items: center; - gap: 8px; - padding: 0 10px 2px; -} - -.st-font-scale button { - height: 38px; - border: 1px solid var(--st-border-strong); - border-radius: 8px; - background: var(--st-surface); - color: var(--st-text); - cursor: pointer; - font-size: 18px; - line-height: 1; -} - -.st-font-scale button:hover:not(:disabled) { - border-color: var(--st-accent); - background: var(--st-accent-soft); -} - -.st-font-scale button:disabled { - cursor: not-allowed; - opacity: 0.4; -} - -.st-font-value { - color: var(--st-text); - font-size: 16px; - font-variant-numeric: tabular-nums; - font-weight: 600; - text-align: center; -} - /* —— Shell —— */ .st-shell { display: grid; @@ -804,14 +616,6 @@ code { margin-top: 4px; } -.profile-group-empty { - margin: 0; - padding: 12px 16px 14px; - font-size: 12px; - color: var(--st-muted); - border-top: 1px solid var(--st-border); -} - @media (max-width: 640px) { .profile-model-row { flex-direction: column; @@ -1583,6 +1387,95 @@ code { margin-top: 16px; } +/* —— Appearance (设置 · 外观) —— */ +.appearance-themes { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 10px; +} + +.appearance-theme { + display: grid; + grid-template-columns: 26px minmax(0, 1fr); + align-items: center; + gap: 10px; + padding: 9px 12px; + border: 1px solid var(--st-border-strong); + border-radius: var(--st-radius); + background: var(--st-surface); + color: var(--st-text); + font: inherit; + text-align: left; + cursor: pointer; +} + +.appearance-theme:hover { + border-color: var(--st-accent); + background: color-mix(in srgb, var(--st-surface) 62%, var(--st-bg)); +} + +.appearance-theme.is-active { + border-color: var(--st-accent); + background: var(--st-accent-soft); + color: var(--st-accent); +} + +.appearance-theme:focus-visible, +.appearance-scale button:focus-visible { + outline: 2px solid color-mix(in srgb, var(--st-accent) 70%, transparent); + outline-offset: 2px; +} + +.appearance-theme-icon { + font-size: 18px; + line-height: 1; + text-align: center; +} + +.appearance-theme-copy { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + line-height: 1.35; +} + +.appearance-theme-name { + font-size: 14px; + font-weight: 650; +} + +.appearance-theme-desc { + font-size: 12px; + color: var(--st-muted); +} + +.appearance-theme.is-active .appearance-theme-desc { + color: color-mix(in srgb, var(--st-accent) 70%, var(--st-muted)); +} + +.appearance-scale { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.appearance-scale button { + min-width: 44px; + font-size: 15px; + line-height: 1; +} + +.appearance-scale-value { + min-width: 4.5em; + padding: 0 2px; + font-size: 15px; + font-variant-numeric: tabular-nums; + font-weight: 600; + text-align: center; +} + /* —— Dialog —— */ .profile-dialog { border: none; @@ -1895,11 +1788,6 @@ code { scrollbar-width: thin; } - .st-top-actions { - grid-column: 2; - grid-row: 1; - } - .st-nav a { flex: 0 0 auto; } diff --git a/web/settings.html b/web/settings.html index e6d361c..1267900 100644 --- a/web/settings.html +++ b/web/settings.html @@ -22,6 +22,7 @@ 技能 库 Token + 预设 设置 @@ -35,6 +36,7 @@ +

保存在本机 ~/.writing-agent/,明文、不同步。

@@ -230,6 +232,31 @@ + + diff --git a/web/settings.js b/web/settings.js index 1457114..8965523 100644 --- a/web/settings.js +++ b/web/settings.js @@ -9,6 +9,17 @@ import { sortByApiProfileUsage, } from "./api-profile-usage.js"; import { readSniffModels, writeSniffModels } from "./model-sniff-cache.js"; +import { + applyColorTheme, + applyUiFontScale, + bumpUiFontScale, + COLOR_THEMES, + getStoredColorTheme, + getStoredUiFontScale, + UI_FONT_SCALE_DEFAULT, + UI_FONT_SCALE_MAX, + UI_FONT_SCALE_MIN, +} from "./theme.js"; let state = { settings: { activeProfileId: null, activePresetId: null }, @@ -29,6 +40,8 @@ let sniffBootId = ""; let sniffRestoreSeq = 0; /** 组 id → 是否展开显示该组全部配置(默认只显示曾选用);有搜索词时按匹配显示 */ const showAllProfilesByGroup = new Set(); +/** 上一轮渲染里因为一个曾用型号都没有而整组收起的组 id */ +let lastHiddenGroupIds = []; let profileSearchQuery = ""; let editingPersonaId = null; let activeSection = "api"; @@ -59,6 +72,12 @@ const sectionPreferenceEl = document.getElementById("section-preference"); const sectionPresetEl = document.getElementById("section-preset"); const sectionStorageEl = document.getElementById("section-storage"); const sectionChatEl = document.getElementById("section-chat"); +const sectionAppearanceEl = document.getElementById("section-appearance"); +const appearanceThemesEl = document.getElementById("appearance-themes"); +const appearanceScaleValueEl = document.getElementById("appearance-scale-value"); +const appearanceScaleMinusEl = document.getElementById("appearance-scale-minus"); +const appearanceScalePlusEl = document.getElementById("appearance-scale-plus"); +const appearanceScaleResetEl = document.getElementById("appearance-scale-reset"); const preferenceCollectEveryEl = document.getElementById("preference-collect-every"); const activeSettingsBarEl = document.getElementById("active-settings-bar"); const settingsToastEl = document.getElementById("settings-toast"); @@ -126,6 +145,35 @@ function showToast(message, isError = false) { }, 4000); } +/** 外观(主题/字号)只存在本机浏览器里,不走服务端设置。 */ +const APPEARANCE_STORAGE_KEYS = ["wa-color-theme", "wa-ui-font-scale"]; + +function renderAppearanceThemes() { + if (!appearanceThemesEl || appearanceThemesEl.childElementCount) return; + appearanceThemesEl.innerHTML = COLOR_THEMES.map( + (theme) => ``, + ).join(""); +} + +function syncAppearanceUi() { + const activeTheme = getStoredColorTheme(); + appearanceThemesEl?.querySelectorAll("[data-appearance-theme]").forEach((btn) => { + const on = btn.getAttribute("data-appearance-theme") === activeTheme; + btn.classList.toggle("is-active", on); + btn.setAttribute("aria-checked", String(on)); + }); + const percent = getStoredUiFontScale(); + if (appearanceScaleValueEl) appearanceScaleValueEl.textContent = `${percent}%`; + if (appearanceScaleMinusEl) appearanceScaleMinusEl.disabled = percent <= UI_FONT_SCALE_MIN; + if (appearanceScalePlusEl) appearanceScalePlusEl.disabled = percent >= UI_FONT_SCALE_MAX; +} + function renderActiveBar() { const profile = state.profiles.find( (p) => p.id === state.settings.activeProfileId, @@ -159,6 +207,8 @@ function switchSection(section) { sectionPresetEl.classList.toggle("hidden", section !== "preset"); sectionStorageEl?.classList.toggle("hidden", section !== "storage"); sectionChatEl?.classList.toggle("hidden", section !== "chat"); + sectionAppearanceEl?.classList.toggle("hidden", section !== "appearance"); + if (section === "appearance") syncAppearanceUi(); renderPanelHeader(); } @@ -197,6 +247,9 @@ function renderPanelHeader() { btn.textContent = "导入 JSON"; btn.addEventListener("click", () => presetFileEl.click()); panelActionsEl.appendChild(btn); + } else if (activeSection === "appearance") { + panelTitleEl.textContent = "外观"; + panelSubtitleEl.textContent = "主题与字号,只影响这台浏览器"; } else if (activeSection === "chat") { panelTitleEl.textContent = "直聊"; panelSubtitleEl.textContent = "卡片拖动的跟手倍率"; @@ -338,6 +391,7 @@ function renderProfiles() { ? state.groups : [{ id: "", name: "未分组", baseUrl: "" }]; const seen = new Set(); + const hiddenGroupIds = []; let shownGroups = 0; for (const group of groups) { @@ -352,6 +406,11 @@ function renderProfiles() { activeId, ); if (query && !visible.length) continue; + // 默认「只看曾用」时,整组都没有曾用型号就整组收起,不留空卡片。 + if (!showAll && !visible.length) { + hiddenGroupIds.push(group.id); + continue; + } const hasActive = members.some((p) => p.id === activeId); const sample = members[0]; @@ -409,19 +468,10 @@ function renderProfiles() { `; - if (!visible.length) { - const empty = document.createElement("p"); - empty.className = "profile-group-empty"; - empty.textContent = query - ? "没有匹配的模型" - : "本组还没有曾选用过的型号。点「显示全部」,或「添加型号」。"; - card.appendChild(empty); - } else { - const rows = document.createElement("div"); - rows.className = "profile-model-rows"; - for (const p of visible) rows.appendChild(renderModelRow(p)); - card.appendChild(rows); - } + const rows = document.createElement("div"); + rows.className = "profile-model-rows"; + for (const p of visible) rows.appendChild(renderModelRow(p)); + card.appendChild(rows); profilesListEl.appendChild(card); shownGroups += 1; @@ -452,7 +502,17 @@ function renderProfiles() { shownGroups += 1; } - if (!shownGroups) { + lastHiddenGroupIds = hiddenGroupIds; + // 一个曾用型号都没有的组整组收起后,留一行提示,需要时还能展开。 + if (hiddenGroupIds.length) { + const hint = document.createElement("p"); + hint.className = "empty-hint profile-hidden-groups"; + hint.innerHTML = `已自动隐藏 ${hiddenGroupIds.length} 个还没有曾用型号的组 + `; + profilesListEl.appendChild(hint); + } + + if (!shownGroups && !hiddenGroupIds.length) { profilesListEl.innerHTML = query ? `

没有匹配「${escapeHtml(profileSearchQuery.trim())}」的配置。

` : '

暂无配置,点击右上角「新增配置」。

'; @@ -1525,6 +1585,13 @@ profilesListEl.addEventListener("click", async (e) => { const action = btn.dataset.action; try { + if (action === "reveal-hidden-groups") { + for (const hiddenId of lastHiddenGroupIds) { + showAllProfilesByGroup.add(hiddenId); + } + renderProfiles(); + return; + } if (action === "toggle-group-all") { if (showAllProfilesByGroup.has(id)) showAllProfilesByGroup.delete(id); else showAllProfilesByGroup.add(id); @@ -1924,6 +1991,37 @@ document.getElementById("btn-clear-context")?.addEventListener("click", async () } }); +appearanceThemesEl?.addEventListener("click", (e) => { + const btn = e.target.closest("[data-appearance-theme]"); + if (!btn) return; + applyColorTheme(btn.getAttribute("data-appearance-theme")); + syncAppearanceUi(); +}); + +appearanceScaleMinusEl?.addEventListener("click", () => { + bumpUiFontScale(-1); + syncAppearanceUi(); +}); + +appearanceScalePlusEl?.addEventListener("click", () => { + bumpUiFontScale(1); + syncAppearanceUi(); +}); + +appearanceScaleResetEl?.addEventListener("click", () => { + applyColorTheme("modern"); + applyUiFontScale(UI_FONT_SCALE_DEFAULT); + syncAppearanceUi(); + showToast("外观已恢复默认"); +}); + +window.addEventListener("storage", (e) => { + if (APPEARANCE_STORAGE_KEYS.includes(e.key)) syncAppearanceUi(); +}); + +renderAppearanceThemes(); +syncAppearanceUi(); + renderPanelHeader(); loadAll().catch((err) => { showToast(`加载设置失败: ${err.message}`, true); diff --git a/web/stats.html b/web/stats.html index 4baeafe..21a6309 100644 --- a/web/stats.html +++ b/web/stats.html @@ -23,6 +23,7 @@ 技能 库 Token + 预设 设置 diff --git a/web/styles.css b/web/styles.css index 6705c4e..6dcf085 100644 --- a/web/styles.css +++ b/web/styles.css @@ -3241,50 +3241,6 @@ body[data-lifecycle="play"] .msg-play-aux .msg-body { letter-spacing: 0.02em; color: var(--muted); } -.more-font-scale { - display: flex; - align-items: center; - gap: 8px; - padding: 4px 10px 8px; -} -.more-font-btn { - box-sizing: border-box; - width: 28px; - height: 28px; - padding: 0; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--surface); - color: var(--text); - font: inherit; - font-size: 16px; - line-height: 1; - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; -} -.more-font-btn:hover:not(:disabled) { - background: var(--bg); - border-color: var(--accent); - color: var(--accent); -} -.more-font-btn:disabled { - opacity: 0.4; - cursor: not-allowed; -} -.more-font-btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 1px; -} -.more-font-value { - min-width: 2.5em; - text-align: center; - font-size: 13px; - font-variant-numeric: tabular-nums; - font-weight: 600; - color: var(--text); -} .more-menu-item { display: block; width: 100%; diff --git a/web/theme-init.js b/web/theme-init.js index dff5912..8cc51b9 100644 --- a/web/theme-init.js +++ b/web/theme-init.js @@ -1,13 +1,6 @@ -/* Apply the conversation workspace theme before first paint, then mount the - * same theme controls in every secondary-page top bar. */ +/* Apply the workspace theme + UI font scale before first paint. + * The controls live in 设置 → 外观 (see settings.js); pages only read storage. */ (function () { - var themes = [ - { id: "paper", icon: "📖", name: "仿纸暖黄", description: "经典护眼" }, - { id: "modern", icon: "✨", name: "现代清爽", description: "干净明快" }, - { id: "dark", icon: "🌙", name: "夜间暗色", description: "黑底灰字" }, - { id: "green", icon: "🍃", name: "豆沙绿", description: "自然疗愈" }, - ]; - function normalizeTheme(value) { return /^(paper|modern|dark|green)$/.test(value || "") ? value : "modern"; } @@ -24,9 +17,6 @@ var next = normalizeTheme(theme); document.documentElement.setAttribute("data-color-theme", next); document.documentElement.setAttribute("data-theme", next); - try { - localStorage.setItem("wa-color-theme", next); - } catch (_) {} return next; } @@ -34,9 +24,6 @@ var next = normalizeScale(scale); document.documentElement.setAttribute("data-ui-font-scale", String(next)); document.documentElement.style.setProperty("--ui-font-scale", String(next / 100)); - try { - localStorage.setItem("wa-ui-font-scale", String(next)); - } catch (_) {} return next; } @@ -47,105 +34,9 @@ /* Storage may be unavailable; CSS defaults remain usable. */ } - function mountThemeMenu() { - var top = document.querySelector(".st-top"); - if (!top || top.querySelector(".st-theme-menu")) return; - - var actions = document.createElement("div"); - actions.className = "st-top-actions"; - actions.innerHTML = - '
' + - '' + - '' + - "" + - '
' + - '
主题
' + - themes - .map(function (item) { - return ( - '" - ); - }) - .join("") + - '
' + - '
字号
' + - '
' + - '' + - '' + - '' + - "
"; - top.appendChild(actions); - - var menu = actions.querySelector(".st-theme-menu"); - var value = actions.querySelector(".st-font-value"); - - function sync() { - var active = normalizeTheme( - document.documentElement.getAttribute("data-color-theme"), - ); - var scale = normalizeScale( - document.documentElement.getAttribute("data-ui-font-scale"), - ); - actions.querySelectorAll("[data-st-theme]").forEach(function (button) { - var selected = button.getAttribute("data-st-theme") === active; - button.classList.toggle("is-active", selected); - button.setAttribute("aria-pressed", String(selected)); - }); - value.textContent = String(scale); - var minus = actions.querySelector('[data-st-scale="-1"]'); - var plus = actions.querySelector('[data-st-scale="1"]'); - minus.disabled = scale <= 80; - plus.disabled = scale >= 150; - } - - actions.addEventListener("click", function (event) { - var themeButton = event.target.closest("[data-st-theme]"); - if (themeButton) { - applyTheme(themeButton.getAttribute("data-st-theme")); - sync(); - return; - } - var scaleButton = event.target.closest("[data-st-scale]"); - if (scaleButton) { - var current = normalizeScale( - document.documentElement.getAttribute("data-ui-font-scale"), - ); - applyScale(current + Number(scaleButton.getAttribute("data-st-scale")) * 5); - sync(); - } - }); - - document.addEventListener("click", function (event) { - if (menu.open && !menu.contains(event.target)) menu.removeAttribute("open"); - }); - document.addEventListener("keydown", function (event) { - if (event.key === "Escape" && menu.open) { - menu.removeAttribute("open"); - menu.querySelector("summary").focus(); - } - }); - window.addEventListener("storage", function (event) { - if (event.key === "wa-color-theme") applyTheme(event.newValue); - if (event.key === "wa-ui-font-scale") applyScale(event.newValue); - sync(); - }); - sync(); - } - - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", mountThemeMenu, { once: true }); - } else { - mountThemeMenu(); - } + /* Keep tabs in sync when 设置 · 外观 changes the stored values. */ + window.addEventListener("storage", function (event) { + if (event.key === "wa-color-theme") applyTheme(event.newValue); + if (event.key === "wa-ui-font-scale") applyScale(event.newValue); + }); })();