From 569557a82a4dbafd1ac0f444750ffb18c3ff23e0 Mon Sep 17 00:00:00 2001 From: Peisong Xiao Date: Mon, 19 Jan 2026 22:19:47 -0500 Subject: [PATCH] minor UI tweaks --- sitecompanion/background.js | 2 + sitecompanion/content.js | 56 ++++-- sitecompanion/popup.css | 60 ++++++- sitecompanion/popup.html | 34 +++- sitecompanion/popup.js | 112 ++++++++++-- sitecompanion/settings.css | 4 + sitecompanion/settings.html | 18 +- sitecompanion/settings.js | 346 +++++++++++++++++++++++++++++++++++- 8 files changed, 586 insertions(+), 46 deletions(-) diff --git a/sitecompanion/background.js b/sitecompanion/background.js index 0732851..b9d32b6 100644 --- a/sitecompanion/background.js +++ b/sitecompanion/background.js @@ -17,6 +17,8 @@ const DEFAULT_SETTINGS = { theme: "system", toolbarAutoHide: true, alwaysShowOutput: false, + alwaysUseDefaultEnvProfile: false, + emptyToolbarBehavior: "open", workspaces: [] }; diff --git a/sitecompanion/content.js b/sitecompanion/content.js index 4fccf0f..e414d5e 100644 --- a/sitecompanion/content.js +++ b/sitecompanion/content.js @@ -360,6 +360,22 @@ function resolveThemeValue(globalTheme, workspace, site) { return globalTheme || "system"; } +function normalizeEmptyToolbarBehavior(value, allowInherit = true) { + if (value === "hide" || value === "open") return value; + if (allowInherit && value === "inherit") return "inherit"; + return allowInherit ? "inherit" : "open"; +} + +function resolveEmptyToolbarBehavior(globalValue, workspace, site) { + const base = normalizeEmptyToolbarBehavior(globalValue, false); + const workspaceValue = normalizeEmptyToolbarBehavior( + workspace?.emptyToolbarBehavior + ); + const workspaceResolved = workspaceValue === "inherit" ? base : workspaceValue; + const siteValue = normalizeEmptyToolbarBehavior(site?.emptyToolbarBehavior); + return siteValue === "inherit" ? workspaceResolved : siteValue; +} + function resolveThemeMode(theme) { if (theme === "dark" || theme === "light") return theme; if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { @@ -393,10 +409,20 @@ function getToolbarThemeTokens(mode) { }; } -function createToolbar(shortcuts, position = "bottom-right", themeMode = "light", options = {}) { +function createToolbar( + shortcuts, + position = "bottom-right", + themeMode = "light", + options = {} +) { let toolbar = document.getElementById("sitecompanion-toolbar"); if (toolbar) toolbar.remove(); + const hasShortcuts = Array.isArray(shortcuts) && shortcuts.length > 0; + const showOpenButton = + options?.unknown || (!hasShortcuts && options?.emptyBehavior === "open"); + if (!hasShortcuts && !showOpenButton) return; + toolbar = document.createElement("div"); toolbar.id = "sitecompanion-toolbar"; @@ -437,7 +463,7 @@ function createToolbar(shortcuts, position = "bottom-right", themeMode = "light" color: ${tokens.ink}; `; - if (options?.unknown) { + if (showOpenButton) { const btn = document.createElement("button"); btn.type = "button"; btn.textContent = "Open SiteCompanion"; @@ -457,12 +483,6 @@ function createToolbar(shortcuts, position = "bottom-right", themeMode = "light" }); }); toolbar.appendChild(btn); - } else if (!shortcuts || !shortcuts.length) { - const label = document.createElement("span"); - label.textContent = "SiteCompanion"; - label.style.fontSize = "12px"; - label.style.color = tokens.muted; - toolbar.appendChild(label); } else { for (const shortcut of shortcuts) { const btn = document.createElement("button"); @@ -527,7 +547,8 @@ async function refreshToolbar() { presets = [], toolbarPosition = "bottom-right", theme = "system", - toolbarAutoHide = true + toolbarAutoHide = true, + emptyToolbarBehavior = "open" } = await chrome.storage.local.get([ "sites", "workspaces", @@ -535,7 +556,8 @@ async function refreshToolbar() { "presets", "toolbarPosition", "theme", - "toolbarAutoHide" + "toolbarAutoHide", + "emptyToolbarBehavior" ]); const currentUrl = window.location.href; const site = sites.find(s => matchUrl(currentUrl, s.urlPattern)); @@ -579,7 +601,19 @@ async function refreshToolbar() { : toolbarPosition; const resolvedTheme = resolveThemeValue(theme, workspace, site); const themeMode = resolveThemeMode(resolvedTheme); - createToolbar(siteShortcuts, resolvedPosition, themeMode); + const resolvedEmptyToolbarBehavior = resolveEmptyToolbarBehavior( + emptyToolbarBehavior, + workspace, + site + ); + if (!siteShortcuts.length && resolvedEmptyToolbarBehavior === "hide") { + const toolbar = document.getElementById("sitecompanion-toolbar"); + if (toolbar) toolbar.remove(); + return; + } + createToolbar(siteShortcuts, resolvedPosition, themeMode, { + emptyBehavior: resolvedEmptyToolbarBehavior + }); } catch (error) { const message = String(error?.message || ""); if (message.includes("Extension context invalidated")) { diff --git a/sitecompanion/popup.css b/sitecompanion/popup.css index e2f4f8b..9765551 100644 --- a/sitecompanion/popup.css +++ b/sitecompanion/popup.css @@ -47,7 +47,8 @@ body { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; color: var(--ink); background: var(--page-bg); - --output-max-height-base: 280px; + --control-height: 30px; + --output-max-height-base: 276px; --output-height-delta: 0px; } @@ -121,7 +122,8 @@ label { select { width: 100%; - padding: 6px 8px; + height: var(--control-height); + padding: 0 8px; border-radius: 10px; border: 1px solid var(--border); background: var(--input-bg); @@ -140,6 +142,49 @@ select { margin: 0; } +.env-profile-summary { + display: none; + align-items: center; + gap: 8px; + font-size: 11px; + color: var(--muted); + margin-bottom: 8px; + font-style: italic; +} + +.env-profile-item { + flex: 1; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +body.always-default-env-profile .selector-row { + display: none; +} + +body.always-default-env-profile .env-profile-summary { + display: flex; + margin-bottom: 8px; +} + +body.always-default-env-profile:not(.custom-task-mode) .config-block { + row-gap: 0; +} + +body.always-default-env-profile:not(.custom-task-mode) { + --output-max-height-base: 309px; +} + +body.custom-task-mode .env-profile-summary { + display: none; +} + +body.custom-task-mode.always-default-env-profile .selector-row { + display: flex; +} + .task-row { display: flex; align-items: flex-end; @@ -147,7 +192,7 @@ select { } .task-row button { - padding: 6px 15px; + padding: 0 15px; } .task-row .task-field { @@ -254,11 +299,18 @@ button { font-family: inherit; border: none; border-radius: 10px; - padding: 6px 10px; + padding: 0 10px; cursor: pointer; transition: transform 0.15s ease, box-shadow 0.15s ease; } +button.control-btn { + height: var(--control-height); + display: inline-flex; + align-items: center; + justify-content: center; +} + button:disabled { opacity: 0.5; cursor: not-allowed; diff --git a/sitecompanion/popup.html b/sitecompanion/popup.html index f37b4ed..db6518c 100644 --- a/sitecompanion/popup.html +++ b/sitecompanion/popup.html @@ -65,15 +65,29 @@ +
+
+ ENV: +
+
+ PROFILE: +
+
- +
- - + +
- +
@@ -100,9 +114,15 @@ diff --git a/sitecompanion/popup.js b/sitecompanion/popup.js index 8fd18a4..453f6b8 100644 --- a/sitecompanion/popup.js +++ b/sitecompanion/popup.js @@ -3,6 +3,9 @@ const abortBtn = document.getElementById("abortBtn"); const taskSelect = document.getElementById("taskSelect"); const envSelect = document.getElementById("envSelect"); const profileSelect = document.getElementById("profileSelect"); +const envProfileSummary = document.getElementById("envProfileSummary"); +const envSummaryValue = document.getElementById("envSummaryValue"); +const profileSummaryValue = document.getElementById("profileSummaryValue"); const customTaskBtn = document.getElementById("customTaskBtn"); const normalTaskBtn = document.getElementById("normalTaskBtn"); const customTaskInput = document.getElementById("customTaskInput"); @@ -67,6 +70,7 @@ const state = { selectedEnvId: "", selectedProfileId: "", alwaysShowOutput: false, + alwaysUseDefaultEnvProfile: false, activeTabId: null, pendingConfigRefresh: false, customTaskMode: false, @@ -682,6 +686,46 @@ function resolveThemeForPopup(baseTheme) { return baseTheme || "system"; } +function resolveAppearanceToggleValue(value, fallback) { + if (value === "enabled") return true; + if (value === "disabled") return false; + if (value === "inherit" || value === null || value === undefined) { + return Boolean(fallback); + } + if (typeof value === "boolean") return value; + return Boolean(fallback); +} + +function resolveAlwaysUseDefaultEnvProfile(baseSetting, workspace, site) { + const resolvedBase = resolveAppearanceToggleValue(baseSetting, false); + const workspaceResolved = resolveAppearanceToggleValue( + workspace?.alwaysUseDefaultEnvProfile, + resolvedBase + ); + return resolveAppearanceToggleValue( + site?.alwaysUseDefaultEnvProfile, + workspaceResolved + ); +} + +function updateEnvProfileSummary() { + if (!envSummaryValue || !profileSummaryValue) return; + const env = getSelectedEnv(); + const profile = getSelectedProfile(); + envSummaryValue.textContent = env ? env.name || "Default" : "None"; + profileSummaryValue.textContent = profile ? profile.name || "Default" : "None"; +} + +function applyAlwaysUseDefaultEnvProfileState() { + document.body.classList.toggle( + "always-default-env-profile", + state.alwaysUseDefaultEnvProfile + ); + updateEnvSelectState(); + updateProfileSelectState(); + updateEnvProfileSummary(); +} + function setAnalyzing(isAnalyzing) { state.isAnalyzing = isAnalyzing; runBtn.disabled = isAnalyzing; @@ -752,6 +796,8 @@ function setCustomTaskMode(enabled, { persist = true } = {}) { }); } updatePromptCount(); + updateEnvSelectState(); + updateProfileSelectState(); if (persist) { void persistCustomTaskState(); } @@ -901,6 +947,7 @@ function renderEnvironments(envs) { option.value = ""; envSelect.appendChild(option); updateEnvSelectState(); + updateEnvProfileSummary(); return; } @@ -911,6 +958,7 @@ function renderEnvironments(envs) { envSelect.appendChild(option); } updateEnvSelectState(); + updateEnvProfileSummary(); } function updateTaskSelectState() { @@ -920,7 +968,10 @@ function updateTaskSelectState() { function updateEnvSelectState() { const hasEnvs = state.envs.length > 0; - envSelect.disabled = state.isAnalyzing || !hasEnvs; + envSelect.disabled = + state.isAnalyzing || + !hasEnvs || + (state.alwaysUseDefaultEnvProfile && !state.customTaskMode); } function renderProfiles(profiles) { @@ -933,6 +984,7 @@ function renderProfiles(profiles) { option.value = ""; profileSelect.appendChild(option); updateProfileSelectState(); + updateEnvProfileSummary(); return; } @@ -943,11 +995,15 @@ function renderProfiles(profiles) { profileSelect.appendChild(option); } updateProfileSelectState(); + updateEnvProfileSummary(); } function updateProfileSelectState() { const hasProfiles = state.profiles.length > 0; - profileSelect.disabled = state.isAnalyzing || !hasProfiles; + profileSelect.disabled = + state.isAnalyzing || + !hasProfiles || + (state.alwaysUseDefaultEnvProfile && !state.customTaskMode); } function getTaskDefaultEnvId(task) { @@ -968,6 +1024,7 @@ function setEnvironmentSelection(envId) { } state.selectedEnvId = target; updatePromptCount(); + updateEnvProfileSummary(); } function setProfileSelection(profileId) { @@ -980,6 +1037,7 @@ function setProfileSelection(profileId) { } state.selectedProfileId = target; updatePromptCount(); + updateEnvProfileSummary(); } function selectTask(taskId, { resetEnv } = { resetEnv: false }) { @@ -1105,6 +1163,7 @@ async function loadConfig() { "sites", "theme", "alwaysShowOutput", + "alwaysUseDefaultEnvProfile", LAST_TASK_KEY, LAST_ENV_KEY, LAST_PROFILE_KEY, @@ -1156,8 +1215,14 @@ async function loadConfig() { state.globalTheme = stored.theme; } state.alwaysShowOutput = Boolean(stored.alwaysShowOutput); + state.alwaysUseDefaultEnvProfile = resolveAlwaysUseDefaultEnvProfile( + stored.alwaysUseDefaultEnvProfile, + activeWorkspace, + activeSite + ); applyTheme(resolveThemeForPopup(state.globalTheme)); updateOutputVisibility(); + applyAlwaysUseDefaultEnvProfileState(); state.customTaskMode = Boolean(stored[CUSTOM_TASK_MODE_KEY]); state.customTaskText = stored[CUSTOM_TASK_TEXT_KEY] || ""; @@ -1205,22 +1270,24 @@ async function loadConfig() { const initialTaskId = effectiveTasks.some((task) => task.id === storedTaskId) ? storedTaskId : effectiveTasks[0].id; - selectTask(initialTaskId, { resetEnv: false }); + selectTask(initialTaskId, { resetEnv: state.alwaysUseDefaultEnvProfile }); const task = effectiveTasks.find((item) => item.id === initialTaskId); - if (storedEnvId && effectiveEnvs.some((env) => env.id === storedEnvId)) { - setEnvironmentSelection(storedEnvId); - } else { - setEnvironmentSelection(getTaskDefaultEnvId(task)); - } + if (!state.alwaysUseDefaultEnvProfile) { + if (storedEnvId && effectiveEnvs.some((env) => env.id === storedEnvId)) { + setEnvironmentSelection(storedEnvId); + } else { + setEnvironmentSelection(getTaskDefaultEnvId(task)); + } - if ( - storedProfileId && - effectiveProfiles.some((profile) => profile.id === storedProfileId) - ) { - setProfileSelection(storedProfileId); - } else { - setProfileSelection(getTaskDefaultProfileId(task)); + if ( + storedProfileId && + effectiveProfiles.some((profile) => profile.id === storedProfileId) + ) { + setProfileSelection(storedProfileId); + } else { + setProfileSelection(getTaskDefaultProfileId(task)); + } } if ( @@ -1296,6 +1363,10 @@ async function handleAnalyze() { setStatus("Select a task."); return; } + if (state.alwaysUseDefaultEnvProfile && !forcedTask && !state.customTaskMode) { + setEnvironmentSelection(getTaskDefaultEnvId(task)); + setProfileSelection(getTaskDefaultProfileId(task)); + } const { apiKeys = [], @@ -1751,7 +1822,13 @@ async function loadShortcutRunRequest() { await persistSelections(); state.autoRunPending = false; state.shortcutRunPending = false; - void handleExtractAndAnalyze(); + void handleExtractAndAnalyze().finally(() => { + if (!state.alwaysUseDefaultEnvProfile) return; + const selectedTask = getSelectedTask(); + if (!selectedTask) return; + setEnvironmentSelection(getTaskDefaultEnvId(selectedTask)); + setProfileSelection(getTaskDefaultProfileId(selectedTask)); + }); } async function loadAutoRunRequest() { @@ -1819,7 +1896,8 @@ chrome.storage.onChanged.addListener((changes) => { "workspaces", "sites", "theme", - "alwaysShowOutput" + "alwaysShowOutput", + "alwaysUseDefaultEnvProfile" ]; if (configKeys.some((key) => changes[key])) { scheduleConfigRefresh(); diff --git a/sitecompanion/settings.css b/sitecompanion/settings.css index 498d13e..d7c9cb7 100644 --- a/sitecompanion/settings.css +++ b/sitecompanion/settings.css @@ -749,6 +749,10 @@ button:active { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.inline-fields.four { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + .inline-fields .field { margin-bottom: 0; } diff --git a/sitecompanion/settings.html b/sitecompanion/settings.html index b00c1f8..0d30a3c 100644 --- a/sitecompanion/settings.html +++ b/sitecompanion/settings.html @@ -103,7 +103,7 @@
-
+
+
+ + +
+
+ + +
diff --git a/sitecompanion/settings.js b/sitecompanion/settings.js index f8cda9a..8203205 100644 --- a/sitecompanion/settings.js +++ b/sitecompanion/settings.js @@ -19,8 +19,14 @@ const statusSidebarEl = document.getElementById("statusSidebar"); const sidebarErrorsEl = document.getElementById("sidebarErrors"); const themeSelect = document.getElementById("themeSelect"); const toolbarPositionSelect = document.getElementById("toolbarPositionSelect"); +const emptyToolbarBehaviorSelect = document.getElementById( + "emptyToolbarBehaviorSelect" +); const toolbarAutoHide = document.getElementById("toolbarAutoHide"); const alwaysShowOutput = document.getElementById("alwaysShowOutput"); +const alwaysUseDefaultEnvProfileSelect = document.getElementById( + "alwaysUseDefaultEnvProfileSelect" +); const globalSitesContainer = document.getElementById("globalSites"); const toc = document.querySelector(".toc"); const tocResizer = document.getElementById("tocResizer"); @@ -453,8 +459,14 @@ function buildSettingsSnapshot() { toolbarPosition: toolbarPositionSelect ? toolbarPositionSelect.value : "bottom-right", + emptyToolbarBehavior: emptyToolbarBehaviorSelect + ? emptyToolbarBehaviorSelect.value + : "open", toolbarAutoHide: toolbarAutoHide ? toolbarAutoHide.checked : true, - alwaysShowOutput: alwaysShowOutput ? alwaysShowOutput.checked : false + alwaysShowOutput: alwaysShowOutput ? alwaysShowOutput.checked : false, + alwaysUseDefaultEnvProfile: alwaysUseDefaultEnvProfileSelect + ? alwaysUseDefaultEnvProfileSelect.value === "enabled" + : false }); } @@ -2287,6 +2299,10 @@ function collectWorkspaces() { const nameInput = card.querySelector(".workspace-name"); const themeSelect = card.querySelector(".appearance-theme"); const toolbarSelect = card.querySelector(".appearance-toolbar-position"); + const defaultEnvProfileSelect = card.querySelector( + ".appearance-default-env-profile" + ); + const emptyToolbarSelect = card.querySelector(".appearance-empty-toolbar"); // Collect nested resources const envsContainer = card.querySelector(".workspace-envs"); @@ -2310,6 +2326,12 @@ function collectWorkspaces() { name: (nameInput?.value || "Untitled Workspace").trim(), theme: themeSelect?.value || "inherit", toolbarPosition: toolbarSelect?.value || "inherit", + alwaysUseDefaultEnvProfile: normalizeAppearanceToggle( + defaultEnvProfileSelect?.value + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + emptyToolbarSelect?.value + ), envConfigs: envsContainer ? collectEnvConfigs(envsContainer) : [], profiles: profilesContainer ? collectProfiles(profilesContainer) : [], tasks: tasksContainer ? collectTasks(tasksContainer) : [], @@ -2422,12 +2444,196 @@ function renderWorkspaceSection(title, containerClass, items, builder, newItemFa summaryRight.appendChild(addBtn); body.appendChild(listContainer); details.appendChild(body); - + return details; } +const THEME_LABELS = { + system: "System", + light: "Light", + dark: "Dark" +}; + +const TOOLBAR_POSITION_LABELS = { + "bottom-right": "Bottom Right", + "bottom-left": "Bottom Left", + "top-right": "Top Right", + "top-left": "Top Left", + "bottom-center": "Bottom Center" +}; + +const EMPTY_TOOLBAR_BEHAVIOR_LABELS = { + hide: "Hide Toolbar", + open: 'Show "Open SiteCompanion"' +}; + +function normalizeAppearanceToggle(value) { + if (value === "inherit" || value === "enabled" || value === "disabled") { + return value; + } + if (value === true) return "enabled"; + if (value === false) return "disabled"; + return "inherit"; +} + +function normalizeEmptyToolbarBehavior(value, allowInherit = true) { + if (value === "hide" || value === "open") return value; + if (allowInherit && value === "inherit") return "inherit"; + return allowInherit ? "inherit" : "open"; +} + +function resolveAppearanceToggleValue(value, fallback) { + const normalized = normalizeAppearanceToggle(value); + if (normalized === "inherit") return Boolean(fallback); + return normalized === "enabled"; +} + +function getThemeLabel(value) { + return THEME_LABELS[value] || String(value || "System"); +} + +function getToolbarPositionLabel(value) { + return TOOLBAR_POSITION_LABELS[value] || String(value || "Bottom Right"); +} + +function getDefaultEnvProfileLabel(value) { + return value ? "Enabled" : "Disabled"; +} + +function getEmptyToolbarBehaviorLabel(value) { + return EMPTY_TOOLBAR_BEHAVIOR_LABELS[value] || "Hide Toolbar"; +} + +function getGlobalAppearanceConfig() { + return { + theme: themeSelect?.value || "system", + toolbarPosition: toolbarPositionSelect?.value || "bottom-right", + alwaysUseDefaultEnvProfile: resolveAppearanceToggleValue( + alwaysUseDefaultEnvProfileSelect?.value, + false + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + emptyToolbarBehaviorSelect?.value, + false + ) + }; +} + +function updateAppearanceInheritedHint(selectEl, hintEl, label) { + if (!selectEl || !hintEl) return; + if (selectEl.value !== "inherit") { + hintEl.textContent = "Not inheriting"; + hintEl.classList.remove("hidden"); + return; + } + hintEl.textContent = `Inherited: ${label}`; + hintEl.classList.remove("hidden"); +} + +function updateAppearanceInheritanceIndicators() { + const global = getGlobalAppearanceConfig(); + const workspaceCards = document.querySelectorAll(".workspace-card"); + const workspaceAppearance = new Map(); + + workspaceCards.forEach((card) => { + const themeSelect = card.querySelector(".appearance-theme"); + const toolbarSelect = card.querySelector(".appearance-toolbar-position"); + const defaultSelect = card.querySelector(".appearance-default-env-profile"); + const emptyToolbarSelect = card.querySelector(".appearance-empty-toolbar"); + const themeValue = themeSelect?.value || "inherit"; + const toolbarValue = toolbarSelect?.value || "inherit"; + const defaultValue = defaultSelect?.value || "inherit"; + const emptyToolbarValue = normalizeEmptyToolbarBehavior( + emptyToolbarSelect?.value || "inherit" + ); + const resolvedTheme = + themeValue === "inherit" ? global.theme : themeValue; + const resolvedToolbar = + toolbarValue === "inherit" ? global.toolbarPosition : toolbarValue; + const resolvedDefault = resolveAppearanceToggleValue( + defaultValue, + global.alwaysUseDefaultEnvProfile + ); + const resolvedEmptyToolbar = + emptyToolbarValue === "inherit" + ? global.emptyToolbarBehavior + : emptyToolbarValue; + workspaceAppearance.set(card.dataset.id, { + theme: resolvedTheme, + toolbarPosition: resolvedToolbar, + alwaysUseDefaultEnvProfile: resolvedDefault, + emptyToolbarBehavior: resolvedEmptyToolbar + }); + + updateAppearanceInheritedHint( + themeSelect, + card.querySelector('.appearance-inherited[data-appearance-key="theme"]'), + getThemeLabel(global.theme) + ); + updateAppearanceInheritedHint( + toolbarSelect, + card.querySelector( + '.appearance-inherited[data-appearance-key="toolbarPosition"]' + ), + getToolbarPositionLabel(global.toolbarPosition) + ); + updateAppearanceInheritedHint( + defaultSelect, + card.querySelector( + '.appearance-inherited[data-appearance-key="alwaysUseDefaultEnvProfile"]' + ), + getDefaultEnvProfileLabel(global.alwaysUseDefaultEnvProfile) + ); + updateAppearanceInheritedHint( + emptyToolbarSelect, + card.querySelector( + '.appearance-inherited[data-appearance-key="emptyToolbarBehavior"]' + ), + getEmptyToolbarBehaviorLabel(global.emptyToolbarBehavior) + ); + }); + + const siteCards = document.querySelectorAll(".site-card"); + siteCards.forEach((card) => { + const workspaceId = card.querySelector(".site-workspace")?.value || "global"; + const resolved = + workspaceAppearance.get(workspaceId) || global; + updateAppearanceInheritedHint( + card.querySelector(".appearance-theme"), + card.querySelector('.appearance-inherited[data-appearance-key="theme"]'), + getThemeLabel(resolved.theme) + ); + updateAppearanceInheritedHint( + card.querySelector(".appearance-toolbar-position"), + card.querySelector( + '.appearance-inherited[data-appearance-key="toolbarPosition"]' + ), + getToolbarPositionLabel(resolved.toolbarPosition) + ); + updateAppearanceInheritedHint( + card.querySelector(".appearance-default-env-profile"), + card.querySelector( + '.appearance-inherited[data-appearance-key="alwaysUseDefaultEnvProfile"]' + ), + getDefaultEnvProfileLabel(resolved.alwaysUseDefaultEnvProfile) + ); + updateAppearanceInheritedHint( + card.querySelector(".appearance-empty-toolbar"), + card.querySelector( + '.appearance-inherited[data-appearance-key="emptyToolbarBehavior"]' + ), + getEmptyToolbarBehaviorLabel(resolved.emptyToolbarBehavior) + ); + }); +} + function buildAppearanceSection( - { theme = "inherit", toolbarPosition = "inherit" } = {}, + { + theme = "inherit", + toolbarPosition = "inherit", + alwaysUseDefaultEnvProfile = "inherit", + emptyToolbarBehavior = "inherit" + } = {}, { stateKey } = {} ) { const details = document.createElement("details"); @@ -2475,6 +2681,10 @@ function buildAppearanceSection( themeSelect.value = theme || "inherit"; themeField.appendChild(themeLabel); themeField.appendChild(themeSelect); + const themeHint = document.createElement("div"); + themeHint.className = "hint appearance-inherited hidden"; + themeHint.dataset.appearanceKey = "theme"; + themeField.appendChild(themeHint); const toolbarField = document.createElement("div"); toolbarField.className = "field"; @@ -2507,11 +2717,69 @@ function buildAppearanceSection( toolbarSelect.value = toolbarPosition || "inherit"; toolbarField.appendChild(toolbarLabel); toolbarField.appendChild(toolbarSelect); + const toolbarHint = document.createElement("div"); + toolbarHint.className = "hint appearance-inherited hidden"; + toolbarHint.dataset.appearanceKey = "toolbarPosition"; + toolbarField.appendChild(toolbarHint); + + const defaultField = document.createElement("div"); + defaultField.className = "field"; + const defaultLabel = document.createElement("label"); + defaultLabel.textContent = "Always use default ENV/PROFILE"; + const defaultSelect = document.createElement("select"); + defaultSelect.className = "appearance-default-env-profile"; + const defaultOptions = [ + { value: "inherit", label: "Inherit" }, + { value: "enabled", label: "Enabled" }, + { value: "disabled", label: "Disabled" } + ]; + for (const optValue of defaultOptions) { + const opt = document.createElement("option"); + opt.value = optValue.value; + opt.textContent = optValue.label; + defaultSelect.appendChild(opt); + } + defaultSelect.value = normalizeAppearanceToggle(alwaysUseDefaultEnvProfile); + defaultField.appendChild(defaultLabel); + defaultField.appendChild(defaultSelect); + const defaultHint = document.createElement("div"); + defaultHint.className = "hint appearance-inherited hidden"; + defaultHint.dataset.appearanceKey = "alwaysUseDefaultEnvProfile"; + defaultField.appendChild(defaultHint); + + const emptyToolbarField = document.createElement("div"); + emptyToolbarField.className = "field"; + const emptyToolbarLabel = document.createElement("label"); + emptyToolbarLabel.textContent = "Empty toolbar"; + const emptyToolbarSelect = document.createElement("select"); + emptyToolbarSelect.className = "appearance-empty-toolbar"; + const emptyToolbarOptions = [ + { value: "inherit", label: "Inherit" }, + { value: "hide", label: "Hide Toolbar" }, + { value: "open", label: 'Show "Open SiteCompanion"' } + ]; + for (const optionConfig of emptyToolbarOptions) { + const opt = document.createElement("option"); + opt.value = optionConfig.value; + opt.textContent = optionConfig.label; + emptyToolbarSelect.appendChild(opt); + } + emptyToolbarSelect.value = normalizeEmptyToolbarBehavior( + emptyToolbarBehavior + ); + emptyToolbarField.appendChild(emptyToolbarLabel); + emptyToolbarField.appendChild(emptyToolbarSelect); + const emptyToolbarHint = document.createElement("div"); + emptyToolbarHint.className = "hint appearance-inherited hidden"; + emptyToolbarHint.dataset.appearanceKey = "emptyToolbarBehavior"; + emptyToolbarField.appendChild(emptyToolbarHint); const appearanceRow = document.createElement("div"); - appearanceRow.className = "inline-fields two appearance-fields"; + appearanceRow.className = "inline-fields four appearance-fields"; appearanceRow.appendChild(themeField); appearanceRow.appendChild(toolbarField); + appearanceRow.appendChild(defaultField); + appearanceRow.appendChild(emptyToolbarField); body.appendChild(appearanceRow); details.appendChild(body); registerDetail(details, details.open); @@ -4024,6 +4292,7 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) { if (confirm(`Delete workspace "${ws.name}"? All items will move to global.`)) { card.remove(); scheduleSidebarErrors(); + updateAppearanceInheritanceIndicators(); updateToc(collectWorkspaces(), collectSites()); } }); @@ -4034,7 +4303,13 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) { const appearanceSection = buildAppearanceSection( { theme: ws.theme || "inherit", - toolbarPosition: ws.toolbarPosition || "inherit" + toolbarPosition: ws.toolbarPosition || "inherit", + alwaysUseDefaultEnvProfile: normalizeAppearanceToggle( + ws.alwaysUseDefaultEnvProfile + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + ws.emptyToolbarBehavior + ) }, { stateKey: `workspace:${card.dataset.id}:appearance` } ); @@ -4255,6 +4530,10 @@ function collectSites() { const parsedTarget = parseExtractionTargetInput(extractInput?.value || ""); const themeSelect = card.querySelector(".appearance-theme"); const toolbarSelect = card.querySelector(".appearance-toolbar-position"); + const defaultEnvProfileSelect = card.querySelector( + ".appearance-default-env-profile" + ); + const emptyToolbarSelect = card.querySelector(".appearance-empty-toolbar"); const envsContainer = card.querySelector(".site-envs"); const profilesContainer = card.querySelector(".site-profiles"); const tasksContainer = card.querySelector(".site-tasks"); @@ -4272,6 +4551,12 @@ function collectSites() { extractTarget: parsedTarget.target, theme: themeSelect?.value || "inherit", toolbarPosition: toolbarSelect?.value || "inherit", + alwaysUseDefaultEnvProfile: normalizeAppearanceToggle( + defaultEnvProfileSelect?.value + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + emptyToolbarSelect?.value + ), envConfigs: envsContainer ? collectEnvConfigs(envsContainer) : [], profiles: profilesContainer ? collectProfiles(profilesContainer) : [], tasks: tasksContainer ? collectTasks(tasksContainer) : [], @@ -4421,7 +4706,13 @@ function buildSiteCard(site, allWorkspaces = []) { const appearanceSection = buildAppearanceSection( { theme: site.theme || "inherit", - toolbarPosition: site.toolbarPosition || "inherit" + toolbarPosition: site.toolbarPosition || "inherit", + alwaysUseDefaultEnvProfile: normalizeAppearanceToggle( + site.alwaysUseDefaultEnvProfile + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + site.emptyToolbarBehavior + ) }, { stateKey: `site:${card.dataset.id}:appearance` } ); @@ -5388,6 +5679,8 @@ async function loadSettings() { toolbarPosition = "bottom-right", toolbarAutoHide: storedToolbarAutoHide = true, alwaysShowOutput: storedAlwaysShowOutput = false, + alwaysUseDefaultEnvProfile: storedAlwaysUseDefaultEnvProfile = false, + emptyToolbarBehavior: storedEmptyToolbarBehavior = "open", sidebarWidth } = await getStorage([ "apiKey", @@ -5410,6 +5703,8 @@ async function loadSettings() { "sites", "toolbarPosition", "toolbarAutoHide", + "emptyToolbarBehavior", + "alwaysUseDefaultEnvProfile", SIDEBAR_WIDTH_KEY ]); @@ -5422,9 +5717,22 @@ async function loadSettings() { if (toolbarAutoHide) { toolbarAutoHide.checked = Boolean(storedToolbarAutoHide); } + if (emptyToolbarBehaviorSelect) { + emptyToolbarBehaviorSelect.value = normalizeEmptyToolbarBehavior( + storedEmptyToolbarBehavior, + false + ); + } if (alwaysShowOutput) { alwaysShowOutput.checked = Boolean(storedAlwaysShowOutput); } + if (alwaysUseDefaultEnvProfileSelect) { + const normalizedDefault = normalizeAppearanceToggle( + storedAlwaysUseDefaultEnvProfile + ); + alwaysUseDefaultEnvProfileSelect.value = + normalizedDefault === "enabled" ? "enabled" : "disabled"; + } if (Number.isFinite(sidebarWidth)) { applySidebarWidth(sidebarWidth); } @@ -5460,6 +5768,12 @@ async function loadSettings() { ...workspace, theme: workspace.theme || "inherit", toolbarPosition: workspace.toolbarPosition || "inherit", + alwaysUseDefaultEnvProfile: normalizeAppearanceToggle( + workspace.alwaysUseDefaultEnvProfile + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + workspace.emptyToolbarBehavior + ), envConfigs: normalizeConfigList(workspace.envConfigs), profiles: normalizeConfigList(workspace.profiles), tasks: normalizeConfigList(workspace.tasks), @@ -5484,6 +5798,12 @@ async function loadSettings() { extractTarget: normalizedTarget.target, theme: site.theme || "inherit", toolbarPosition: site.toolbarPosition || "inherit", + alwaysUseDefaultEnvProfile: normalizeAppearanceToggle( + site.alwaysUseDefaultEnvProfile + ), + emptyToolbarBehavior: normalizeEmptyToolbarBehavior( + site.emptyToolbarBehavior + ), envConfigs: normalizeConfigList(site.envConfigs), profiles: normalizeConfigList(site.profiles), tasks: normalizeConfigList(site.tasks), @@ -5785,6 +6105,7 @@ async function loadSettings() { updateSidebarErrors(); updateToc(workspaces, sites); renderGlobalSitesList(sites); + updateAppearanceInheritanceIndicators(); } async function saveSettings() { @@ -5913,8 +6234,14 @@ async function saveSettings() { toolbarPosition: toolbarPositionSelect ? toolbarPositionSelect.value : "bottom-right", + emptyToolbarBehavior: emptyToolbarBehaviorSelect + ? emptyToolbarBehaviorSelect.value + : "open", toolbarAutoHide: toolbarAutoHide ? toolbarAutoHide.checked : true, alwaysShowOutput: alwaysShowOutput ? alwaysShowOutput.checked : false, + alwaysUseDefaultEnvProfile: alwaysUseDefaultEnvProfileSelect + ? alwaysUseDefaultEnvProfileSelect.value === "enabled" + : false, workspaces: updatedWorkspaces, sites: mergedSites }); @@ -6059,6 +6386,8 @@ addWorkspaceBtn.addEventListener("click", () => { name: "New Workspace", theme: "inherit", toolbarPosition: "inherit", + alwaysUseDefaultEnvProfile: "inherit", + emptyToolbarBehavior: "inherit", envConfigs: [], profiles: [], tasks: [], @@ -6075,6 +6404,7 @@ addWorkspaceBtn.addEventListener("click", () => { centerCardInView(newCard); refreshWorkspaceInheritedLists(); scheduleSidebarErrors(); + updateAppearanceInheritanceIndicators(); updateToc(collectWorkspaces(), collectSites()); }); @@ -6087,6 +6417,8 @@ addSiteBtn.addEventListener("click", () => { workspaceId: "global", theme: "inherit", toolbarPosition: "inherit", + alwaysUseDefaultEnvProfile: "inherit", + emptyToolbarBehavior: "inherit", envConfigs: [], profiles: [], tasks: [], @@ -6103,6 +6435,7 @@ addSiteBtn.addEventListener("click", () => { centerCardInView(newCard); refreshSiteInheritedLists(); scheduleSidebarErrors(); + updateAppearanceInheritanceIndicators(); updateToc(collectWorkspaces(), collectSites()); }); @@ -6609,6 +6942,7 @@ function handleSettingsInputChange() { scheduleSidebarErrors(); scheduleDirtyCheck(); refreshInheritedSourceLabels(); + updateAppearanceInheritanceIndicators(); } document.addEventListener("input", handleSettingsInputChange);