v0.4.8-dev New Release (#3)
# New Features - Added custom prompt mode - Always use the default environment and profile for a more compact UI - Added option to hide the toolbar when it's empty - Added documentation and icon # Fixed bugs - Fixed issue with config returning to defaults - Fixed TOC lag when cards update - Fixed some UI consistency issues - Dynamically show site text char count in popup UI Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
@@ -17,6 +17,8 @@ const DEFAULT_SETTINGS = {
|
||||
theme: "system",
|
||||
toolbarAutoHide: true,
|
||||
alwaysShowOutput: false,
|
||||
alwaysUseDefaultEnvProfile: false,
|
||||
emptyToolbarBehavior: "open",
|
||||
workspaces: []
|
||||
};
|
||||
|
||||
|
||||
@@ -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")) {
|
||||
@@ -596,6 +630,7 @@ async function refreshToolbar() {
|
||||
|
||||
|
||||
let refreshTimer = null;
|
||||
let contentChangeTimer = null;
|
||||
function scheduleToolbarRefresh() {
|
||||
if (refreshTimer) return;
|
||||
refreshTimer = window.setTimeout(() => {
|
||||
@@ -610,9 +645,22 @@ function scheduleToolbarRefresh() {
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function scheduleContentChangeNotice() {
|
||||
if (contentChangeTimer) return;
|
||||
contentChangeTimer = window.setTimeout(() => {
|
||||
contentChangeTimer = null;
|
||||
chrome.runtime.sendMessage({ type: "SITE_CONTENT_CHANGED" }, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
if (suppressObserver) return;
|
||||
scheduleToolbarRefresh();
|
||||
scheduleContentChangeNotice();
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, { childList: true, subtree: true });
|
||||
|
||||
BIN
sitecompanion/icon128.png
Normal file
BIN
sitecompanion/icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "SiteCompanion",
|
||||
"version": "0.4.7",
|
||||
"version": "0.4.8",
|
||||
"description": "AI companion for site-bound text extraction and tasks.",
|
||||
"permissions": ["storage", "activeTab"],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
@@ -22,5 +22,8 @@
|
||||
"options_ui": {
|
||||
"page": "settings.html",
|
||||
"open_in_tab": true
|
||||
},
|
||||
"icons": {
|
||||
"128": "icon128.png"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ body {
|
||||
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
color: var(--ink);
|
||||
background: var(--page-bg);
|
||||
--control-height: 30px;
|
||||
--output-max-height-base: 276px;
|
||||
--output-height-delta: 0px;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
@@ -119,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);
|
||||
@@ -138,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;
|
||||
@@ -145,7 +192,7 @@ select {
|
||||
}
|
||||
|
||||
.task-row button {
|
||||
padding: 6px 15px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.task-row .task-field {
|
||||
@@ -157,6 +204,37 @@ select {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.custom-task-row {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.custom-task-field {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.custom-task-field textarea {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--input-bg);
|
||||
color: var(--input-fg);
|
||||
font-size: 12px;
|
||||
resize: vertical;
|
||||
min-height: 52px;
|
||||
}
|
||||
|
||||
.custom-task-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.custom-task-actions button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -221,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;
|
||||
@@ -287,14 +372,14 @@ button:active {
|
||||
padding: 8px;
|
||||
background: var(--output-bg);
|
||||
min-height: 210px;
|
||||
max-height: 280px;
|
||||
max-height: calc(var(--output-max-height-base) - var(--output-height-delta));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.output-body {
|
||||
margin: 0;
|
||||
word-break: break-word;
|
||||
max-height: 260px;
|
||||
max-height: calc(var(--output-max-height-base) - var(--output-height-delta) - 20px);
|
||||
overflow-y: auto;
|
||||
font-size: 11px;
|
||||
line-height: 1.45;
|
||||
@@ -384,6 +469,10 @@ button:active {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footer.compact {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.footer-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
<textarea id="partialTextPaste" rows="4" placeholder="Paste some text here..."></textarea>
|
||||
<div id="minimalExtractStatus" class="helper-text hidden"></div>
|
||||
<div class="row">
|
||||
<button id="extractMinimalBtn" class="accent">Try Extracting Minimal</button>
|
||||
<button id="extractFullBtn" class="ghost">Extract Full Text</button>
|
||||
<button id="extractMinimalBtn" class="accent control-btn">Try Extracting Minimal</button>
|
||||
<button id="extractFullBtn" class="ghost control-btn">Extract Full Text</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -43,8 +43,8 @@
|
||||
<select id="workspaceSelect"></select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button id="retryExtractBtn" class="ghost">Retry</button>
|
||||
<button id="confirmSiteBtn" class="accent">Confirm</button>
|
||||
<button id="retryExtractBtn" class="ghost control-btn">Retry</button>
|
||||
<button id="confirmSiteBtn" class="accent control-btn">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -65,19 +65,45 @@
|
||||
<select id="profileSelect"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-row">
|
||||
<div class="env-profile-summary" id="envProfileSummary">
|
||||
<div class="env-profile-item">
|
||||
ENV: <span id="envSummaryValue"></span>
|
||||
</div>
|
||||
<div class="env-profile-item">
|
||||
PROFILE: <span id="profileSummaryValue"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-row" id="normalTaskRow">
|
||||
<div class="field inline-field task-field">
|
||||
<label for="taskSelect">Task</label>
|
||||
<select id="taskSelect"></select>
|
||||
</div>
|
||||
<button id="runBtn" class="accent">Run</button>
|
||||
<button id="abortBtn" class="ghost stop-btn hidden" disabled>Stop</button>
|
||||
<button id="customTaskBtn" class="ghost control-btn">Custom</button>
|
||||
<div id="taskActions">
|
||||
<button id="runBtn" class="accent control-btn">Run</button>
|
||||
<button
|
||||
id="abortBtn"
|
||||
class="ghost stop-btn hidden control-btn"
|
||||
disabled
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-row custom-task-row hidden" id="customTaskRow">
|
||||
<div class="field custom-task-field">
|
||||
<textarea id="customTaskInput" rows="2" placeholder="Enter temporary custom task..."></textarea>
|
||||
</div>
|
||||
<div class="custom-task-actions">
|
||||
<button id="normalTaskBtn" class="ghost control-btn">Normal</button>
|
||||
<div id="taskActionsSlot"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<span id="postingCount">Site Text: 0 chars</span>
|
||||
<span id="promptCount">Task: 0 chars</span>
|
||||
<span id="promptCount">Total: 0 chars</span>
|
||||
<span id="status" class="status">Idle</span>
|
||||
</div>
|
||||
</section>
|
||||
@@ -88,9 +114,15 @@
|
||||
|
||||
<footer class="footer">
|
||||
<div class="footer-left">
|
||||
<button id="copyRenderedBtn" class="ghost" type="button">Copy</button>
|
||||
<button id="copyRawBtn" class="ghost" type="button">Copy Markdown</button>
|
||||
<button id="clearOutputBtn" class="ghost" type="button">Clear</button>
|
||||
<button id="copyRenderedBtn" class="ghost control-btn" type="button">
|
||||
Copy
|
||||
</button>
|
||||
<button id="copyRawBtn" class="ghost control-btn" type="button">
|
||||
Copy Markdown
|
||||
</button>
|
||||
<button id="clearOutputBtn" class="ghost control-btn" type="button">
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
<button id="settingsBtn" class="link">Settings</button>
|
||||
</footer>
|
||||
|
||||
@@ -3,6 +3,16 @@ 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");
|
||||
const normalTaskRow = document.getElementById("normalTaskRow");
|
||||
const customTaskRow = document.getElementById("customTaskRow");
|
||||
const taskActions = document.getElementById("taskActions");
|
||||
const taskActionsSlot = document.getElementById("taskActionsSlot");
|
||||
const outputEl = document.getElementById("output");
|
||||
const statusEl = document.getElementById("status");
|
||||
const postingCountEl = document.getElementById("postingCount");
|
||||
@@ -12,6 +22,8 @@ const copyRenderedBtn = document.getElementById("copyRenderedBtn");
|
||||
const copyRawBtn = document.getElementById("copyRawBtn");
|
||||
const clearOutputBtn = document.getElementById("clearOutputBtn");
|
||||
const outputSection = document.querySelector(".output");
|
||||
const footerLeft = document.querySelector(".footer-left");
|
||||
const footer = document.querySelector(".footer");
|
||||
|
||||
const OUTPUT_STORAGE_KEY = "lastOutput";
|
||||
const AUTO_RUN_KEY = "autoRunDefaultTask";
|
||||
@@ -20,6 +32,8 @@ const LAST_TASK_KEY = "lastSelectedTaskId";
|
||||
const LAST_ENV_KEY = "lastSelectedEnvId";
|
||||
const LAST_PROFILE_KEY = "lastSelectedProfileId";
|
||||
const POPUP_DRAFT_KEY = "popupDraft";
|
||||
const CUSTOM_TASK_MODE_KEY = "customTaskMode";
|
||||
const CUSTOM_TASK_TEXT_KEY = "customTaskText";
|
||||
|
||||
const unknownSiteState = document.getElementById("unknownSiteState");
|
||||
const extractionReviewState = document.getElementById("extractionReviewState");
|
||||
@@ -57,7 +71,12 @@ const state = {
|
||||
selectedTaskId: "",
|
||||
selectedEnvId: "",
|
||||
selectedProfileId: "",
|
||||
alwaysShowOutput: false
|
||||
alwaysShowOutput: false,
|
||||
alwaysUseDefaultEnvProfile: false,
|
||||
activeTabId: null,
|
||||
pendingConfigRefresh: false,
|
||||
customTaskMode: false,
|
||||
customTaskText: ""
|
||||
};
|
||||
|
||||
async function switchState(stateName) {
|
||||
@@ -118,6 +137,7 @@ function applyPopupDraft(draft) {
|
||||
} else if (typeof draft.siteTextSelector === "string") {
|
||||
state.siteTextTarget = { kind: "css", selector: draft.siteTextSelector };
|
||||
}
|
||||
updateCounts();
|
||||
}
|
||||
|
||||
function matchUrl(url, pattern) {
|
||||
@@ -668,6 +688,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;
|
||||
@@ -677,6 +737,10 @@ function setAnalyzing(isAnalyzing) {
|
||||
updateTaskSelectState();
|
||||
updateEnvSelectState();
|
||||
updateProfileSelectState();
|
||||
if (!isAnalyzing && state.pendingConfigRefresh) {
|
||||
state.pendingConfigRefresh = false;
|
||||
scheduleConfigRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
function updateOutputVisibility() {
|
||||
@@ -684,14 +748,175 @@ function updateOutputVisibility() {
|
||||
const shouldHide =
|
||||
state.currentPopupState !== "normal" && !state.alwaysShowOutput;
|
||||
outputSection.classList.toggle("hidden", shouldHide);
|
||||
footerLeft?.classList.toggle("hidden", shouldHide);
|
||||
footer?.classList.toggle("compact", shouldHide);
|
||||
}
|
||||
|
||||
async function persistCustomTaskState() {
|
||||
await chrome.storage.local.set({
|
||||
[CUSTOM_TASK_MODE_KEY]: state.customTaskMode,
|
||||
[CUSTOM_TASK_TEXT_KEY]: state.customTaskText
|
||||
});
|
||||
}
|
||||
|
||||
function setCustomTaskMode(enabled, { persist = true } = {}) {
|
||||
state.customTaskMode = Boolean(enabled);
|
||||
document.body.classList.toggle("custom-task-mode", state.customTaskMode);
|
||||
if (state.customTaskMode) {
|
||||
if (normalTaskRow) {
|
||||
const measured = measureRowHeight(normalTaskRow);
|
||||
if (measured) normalTaskRowHeight = measured;
|
||||
normalTaskRow.classList.add("hidden");
|
||||
}
|
||||
customTaskRow?.classList.remove("hidden");
|
||||
if (taskActionsSlot && taskActions) {
|
||||
taskActionsSlot.appendChild(taskActions);
|
||||
}
|
||||
if (customTaskInput) {
|
||||
customTaskInput.value = state.customTaskText || "";
|
||||
customTaskInput.focus();
|
||||
}
|
||||
window.requestAnimationFrame(() => {
|
||||
if (customTaskRow) {
|
||||
const measured = measureRowHeight(customTaskRow);
|
||||
if (measured) customTaskRowHeight = measured;
|
||||
}
|
||||
updateOutputHeightDelta();
|
||||
});
|
||||
} else {
|
||||
customTaskRow?.classList.add("hidden");
|
||||
if (normalTaskRow) {
|
||||
normalTaskRow.classList.remove("hidden");
|
||||
}
|
||||
if (normalTaskRow && taskActions) {
|
||||
normalTaskRow.appendChild(taskActions);
|
||||
}
|
||||
window.requestAnimationFrame(() => {
|
||||
if (normalTaskRow) {
|
||||
const measured = measureRowHeight(normalTaskRow);
|
||||
if (measured) normalTaskRowHeight = measured;
|
||||
}
|
||||
updateOutputHeightDelta();
|
||||
});
|
||||
}
|
||||
updatePromptCount();
|
||||
updateEnvSelectState();
|
||||
updateProfileSelectState();
|
||||
if (persist) {
|
||||
void persistCustomTaskState();
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedTask() {
|
||||
if (state.forcedTask) return state.forcedTask;
|
||||
const selectedId = taskSelect?.value || state.selectedTaskId;
|
||||
return state.tasks.find((item) => item.id === selectedId) || state.tasks[0] || null;
|
||||
}
|
||||
|
||||
function getSelectedProfile() {
|
||||
const selectedId = profileSelect?.value || state.selectedProfileId;
|
||||
return (
|
||||
state.profiles.find((item) => item.id === selectedId) ||
|
||||
state.profiles[0] ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function getSelectedEnv() {
|
||||
const selectedId = envSelect?.value || state.selectedEnvId;
|
||||
return state.envs.find((item) => item.id === selectedId) || state.envs[0] || null;
|
||||
}
|
||||
|
||||
function buildTotalPromptText() {
|
||||
const task = getSelectedTask();
|
||||
const profile = getSelectedProfile();
|
||||
const env = getSelectedEnv();
|
||||
const systemPrompt = env?.systemPrompt || "";
|
||||
const customText = (state.customTaskText || "").trim();
|
||||
const taskText =
|
||||
state.customTaskMode && !state.forcedTask ? customText : task?.text || "";
|
||||
const userPrompt = buildUserMessage(
|
||||
profile?.text || "",
|
||||
taskText,
|
||||
state.siteText || ""
|
||||
);
|
||||
return systemPrompt ? `${systemPrompt}\n\n${userPrompt}` : userPrompt;
|
||||
}
|
||||
|
||||
function updateSiteTextCount() {
|
||||
postingCountEl.textContent = `Site Text: ${state.siteText.length} chars`;
|
||||
const length = (state.siteText || "").length;
|
||||
postingCountEl.textContent = `Site Text: ${length} chars`;
|
||||
}
|
||||
|
||||
function updatePromptCount(count) {
|
||||
promptCountEl.textContent = `Task: ${count} chars`;
|
||||
const total =
|
||||
typeof count === "number" ? count : buildTotalPromptText().length;
|
||||
promptCountEl.textContent = `Total: ${total} chars`;
|
||||
}
|
||||
|
||||
function updateCounts() {
|
||||
updateSiteTextCount();
|
||||
updatePromptCount();
|
||||
}
|
||||
|
||||
let siteContentRefreshTimer = null;
|
||||
function scheduleSiteContentRefresh() {
|
||||
if (siteContentRefreshTimer) return;
|
||||
siteContentRefreshTimer = window.setTimeout(() => {
|
||||
siteContentRefreshTimer = null;
|
||||
void refreshSiteContentCounts();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
let configRefreshTimer = null;
|
||||
function scheduleConfigRefresh() {
|
||||
if (state.isAnalyzing) {
|
||||
state.pendingConfigRefresh = true;
|
||||
return;
|
||||
}
|
||||
if (configRefreshTimer) return;
|
||||
configRefreshTimer = window.setTimeout(() => {
|
||||
configRefreshTimer = null;
|
||||
void loadConfig();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
let normalTaskRowHeight = null;
|
||||
let customTaskRowHeight = null;
|
||||
|
||||
function measureRowHeight(row) {
|
||||
if (!row) return 0;
|
||||
return row.getBoundingClientRect().height || 0;
|
||||
}
|
||||
|
||||
function updateOutputHeightDelta() {
|
||||
const baseHeight = normalTaskRowHeight || measureRowHeight(normalTaskRow);
|
||||
if (!baseHeight) return;
|
||||
if (!state.customTaskMode) {
|
||||
document.body.style.setProperty("--output-height-delta", "0px");
|
||||
return;
|
||||
}
|
||||
const customHeight = customTaskRowHeight || measureRowHeight(customTaskRow);
|
||||
const delta = Math.max(0, customHeight - baseHeight);
|
||||
document.body.style.setProperty("--output-height-delta", `${Math.round(delta)}px`);
|
||||
}
|
||||
|
||||
async function refreshSiteContentCounts() {
|
||||
if (state.isAnalyzing) return;
|
||||
if (state.currentPopupState !== "normal") return;
|
||||
if (!state.siteTextTarget) return;
|
||||
try {
|
||||
const response = await sendToActiveTab({
|
||||
type: "EXTRACT_BY_SELECTOR",
|
||||
target: state.siteTextTarget
|
||||
});
|
||||
if (!response?.ok) return;
|
||||
state.siteText = response.extracted || "";
|
||||
state.siteTextTarget = response.target || state.siteTextTarget;
|
||||
updateCounts();
|
||||
} catch {
|
||||
// Ignore refresh failures; counts will update on next explicit extract.
|
||||
}
|
||||
}
|
||||
|
||||
function renderTasks(tasks) {
|
||||
@@ -726,6 +951,7 @@ function renderEnvironments(envs) {
|
||||
option.value = "";
|
||||
envSelect.appendChild(option);
|
||||
updateEnvSelectState();
|
||||
updateEnvProfileSummary();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -736,6 +962,7 @@ function renderEnvironments(envs) {
|
||||
envSelect.appendChild(option);
|
||||
}
|
||||
updateEnvSelectState();
|
||||
updateEnvProfileSummary();
|
||||
}
|
||||
|
||||
function updateTaskSelectState() {
|
||||
@@ -745,7 +972,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) {
|
||||
@@ -758,6 +988,7 @@ function renderProfiles(profiles) {
|
||||
option.value = "";
|
||||
profileSelect.appendChild(option);
|
||||
updateProfileSelectState();
|
||||
updateEnvProfileSummary();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -768,11 +999,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) {
|
||||
@@ -792,6 +1027,8 @@ function setEnvironmentSelection(envId) {
|
||||
envSelect.value = target;
|
||||
}
|
||||
state.selectedEnvId = target;
|
||||
updatePromptCount();
|
||||
updateEnvProfileSummary();
|
||||
}
|
||||
|
||||
function setProfileSelection(profileId) {
|
||||
@@ -803,6 +1040,8 @@ function setProfileSelection(profileId) {
|
||||
profileSelect.value = target;
|
||||
}
|
||||
state.selectedProfileId = target;
|
||||
updatePromptCount();
|
||||
updateEnvProfileSummary();
|
||||
}
|
||||
|
||||
function selectTask(taskId, { resetEnv } = { resetEnv: false }) {
|
||||
@@ -814,6 +1053,7 @@ function selectTask(taskId, { resetEnv } = { resetEnv: false }) {
|
||||
setEnvironmentSelection(getTaskDefaultEnvId(task));
|
||||
setProfileSelection(getTaskDefaultProfileId(task));
|
||||
}
|
||||
updatePromptCount();
|
||||
}
|
||||
|
||||
async function persistSelections() {
|
||||
@@ -900,6 +1140,7 @@ function ensurePort() {
|
||||
async function loadConfig() {
|
||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
const currentUrl = tabs[0]?.url || "";
|
||||
state.activeTabId = tabs[0]?.id || null;
|
||||
|
||||
const { lastPopupState, [POPUP_DRAFT_KEY]: popupDraft } = await getStorage([
|
||||
"lastPopupState",
|
||||
@@ -926,9 +1167,12 @@ async function loadConfig() {
|
||||
"sites",
|
||||
"theme",
|
||||
"alwaysShowOutput",
|
||||
"alwaysUseDefaultEnvProfile",
|
||||
LAST_TASK_KEY,
|
||||
LAST_ENV_KEY,
|
||||
LAST_PROFILE_KEY
|
||||
LAST_PROFILE_KEY,
|
||||
CUSTOM_TASK_MODE_KEY,
|
||||
CUSTOM_TASK_TEXT_KEY
|
||||
]);
|
||||
const tasks = normalizeConfigList(stored.tasks);
|
||||
const envs = normalizeConfigList(stored.envConfigs);
|
||||
@@ -968,12 +1212,23 @@ async function loadConfig() {
|
||||
state.currentWorkspace = activeWorkspace;
|
||||
currentWorkspaceName.textContent = activeWorkspace.name || "Global";
|
||||
}
|
||||
if (state.currentSite && !state.siteTextTarget) {
|
||||
state.siteTextTarget = normalizeStoredExtractTarget(state.currentSite);
|
||||
}
|
||||
if (stored.theme) {
|
||||
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] || "";
|
||||
|
||||
const effectiveEnvs = resolveEffectiveList(
|
||||
envs,
|
||||
@@ -1000,11 +1255,16 @@ async function loadConfig() {
|
||||
renderTasks(effectiveTasks);
|
||||
renderEnvironments(effectiveEnvs);
|
||||
renderProfiles(effectiveProfiles);
|
||||
if (customTaskInput) {
|
||||
customTaskInput.value = state.customTaskText;
|
||||
}
|
||||
setCustomTaskMode(state.customTaskMode, { persist: false });
|
||||
|
||||
if (!effectiveTasks.length) {
|
||||
state.selectedTaskId = "";
|
||||
setEnvironmentSelection(effectiveEnvs[0]?.id || "");
|
||||
setProfileSelection(effectiveProfiles[0]?.id || "");
|
||||
updateCounts();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1014,22 +1274,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 (
|
||||
@@ -1040,6 +1302,10 @@ async function loadConfig() {
|
||||
await persistSelections();
|
||||
}
|
||||
|
||||
updateCounts();
|
||||
if (state.currentSite) {
|
||||
await refreshSiteContentCounts();
|
||||
}
|
||||
maybeRunDefaultTask();
|
||||
}
|
||||
|
||||
@@ -1068,8 +1334,7 @@ async function handleExtract() {
|
||||
|
||||
state.siteText = response.extracted || "";
|
||||
state.siteTextTarget = response.target || target;
|
||||
updateSiteTextCount();
|
||||
updatePromptCount(0);
|
||||
updateCounts();
|
||||
setStatus("Text extracted.");
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -1094,13 +1359,18 @@ async function handleAnalyze() {
|
||||
const taskId = taskSelect.value;
|
||||
const forcedTask = state.forcedTask;
|
||||
const task = forcedTask || state.tasks.find((item) => item.id === taskId);
|
||||
const useCustomTask = state.customTaskMode && !forcedTask;
|
||||
if (forcedTask) {
|
||||
state.forcedTask = null;
|
||||
}
|
||||
if (!task) {
|
||||
if (!useCustomTask && !task) {
|
||||
setStatus("Select a task.");
|
||||
return;
|
||||
}
|
||||
if (state.alwaysUseDefaultEnvProfile && !forcedTask && !state.customTaskMode) {
|
||||
setEnvironmentSelection(getTaskDefaultEnvId(task));
|
||||
setProfileSelection(getTaskDefaultProfileId(task));
|
||||
}
|
||||
|
||||
const {
|
||||
apiKeys = [],
|
||||
@@ -1139,6 +1409,12 @@ async function handleAnalyze() {
|
||||
}
|
||||
const resolvedSystemPrompt =
|
||||
activeEnv.systemPrompt ?? systemPrompt ?? "";
|
||||
const customTaskText = (state.customTaskText || "").trim();
|
||||
const resolvedTaskText = useCustomTask ? customTaskText : task?.text || "";
|
||||
if (useCustomTask && !resolvedTaskText) {
|
||||
setStatus("Enter a custom task.");
|
||||
return;
|
||||
}
|
||||
const resolvedApiConfigId =
|
||||
activeEnv.apiConfigId || activeApiConfigId || resolvedConfigs[0]?.id || "";
|
||||
const activeConfig =
|
||||
@@ -1194,12 +1470,7 @@ async function handleAnalyze() {
|
||||
}
|
||||
}
|
||||
|
||||
const promptText = buildUserMessage(
|
||||
profileText,
|
||||
task.text || "",
|
||||
state.siteText
|
||||
);
|
||||
updatePromptCount(promptText.length);
|
||||
updatePromptCount();
|
||||
|
||||
state.outputRaw = "";
|
||||
renderOutput();
|
||||
@@ -1220,7 +1491,7 @@ async function handleAnalyze() {
|
||||
model: resolvedModel,
|
||||
systemPrompt: resolvedSystemPrompt,
|
||||
profileText,
|
||||
taskText: task.text || "",
|
||||
taskText: resolvedTaskText,
|
||||
siteText: state.siteText,
|
||||
tabId: tab.id
|
||||
}
|
||||
@@ -1298,6 +1569,7 @@ async function runMinimalExtraction(text, minLength = 5) {
|
||||
state.siteText = response.extracted;
|
||||
state.siteTextTarget = response.target || { kind: "textScope", text: trimmed };
|
||||
extractedPreview.textContent = state.siteText;
|
||||
updateCounts();
|
||||
await fillSiteDefaultsFromTab();
|
||||
switchState("review");
|
||||
await persistPopupDraft();
|
||||
@@ -1336,6 +1608,7 @@ extractFullBtn.addEventListener("click", async () => {
|
||||
state.siteText = response.extracted;
|
||||
state.siteTextTarget = target;
|
||||
extractedPreview.textContent = state.siteText;
|
||||
updateCounts();
|
||||
await fillSiteDefaultsFromTab();
|
||||
switchState("review");
|
||||
await persistPopupDraft();
|
||||
@@ -1372,6 +1645,7 @@ retryExtractBtn.addEventListener("click", () => {
|
||||
if (workspaceSelect) workspaceSelect.value = "global";
|
||||
state.siteText = "";
|
||||
state.siteTextTarget = null;
|
||||
updateCounts();
|
||||
setMinimalStatus("");
|
||||
void clearPopupDraft();
|
||||
setStatus("Ready.");
|
||||
@@ -1427,10 +1701,24 @@ confirmSiteBtn.addEventListener("click", async () => {
|
||||
currentWorkspaceName.textContent = state.currentWorkspace.name || "Global";
|
||||
await loadConfig();
|
||||
await switchState("normal");
|
||||
updateSiteTextCount();
|
||||
updateCounts();
|
||||
setStatus("Site saved.");
|
||||
});
|
||||
|
||||
customTaskBtn?.addEventListener("click", () => {
|
||||
setCustomTaskMode(true);
|
||||
});
|
||||
|
||||
normalTaskBtn?.addEventListener("click", () => {
|
||||
setCustomTaskMode(false);
|
||||
});
|
||||
|
||||
customTaskInput?.addEventListener("input", () => {
|
||||
state.customTaskText = customTaskInput.value || "";
|
||||
updatePromptCount();
|
||||
void persistCustomTaskState();
|
||||
});
|
||||
|
||||
runBtn.addEventListener("click", handleExtractAndAnalyze);
|
||||
abortBtn.addEventListener("click", handleAbort);
|
||||
settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage());
|
||||
@@ -1450,8 +1738,7 @@ profileSelect.addEventListener("change", () => {
|
||||
void persistSelections();
|
||||
});
|
||||
|
||||
updateSiteTextCount();
|
||||
updatePromptCount(0);
|
||||
updateCounts();
|
||||
renderOutput();
|
||||
setAnalyzing(false);
|
||||
void loadTheme();
|
||||
@@ -1475,6 +1762,7 @@ async function loadShortcutRunRequest() {
|
||||
|
||||
state.shortcutRunPending = true;
|
||||
await chrome.storage.local.remove(SHORTCUT_RUN_KEY);
|
||||
setCustomTaskMode(false);
|
||||
|
||||
if (!state.tasks.length) {
|
||||
await loadConfig();
|
||||
@@ -1538,7 +1826,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() {
|
||||
@@ -1597,4 +1891,28 @@ chrome.storage.onChanged.addListener((changes) => {
|
||||
state.alwaysShowOutput = Boolean(changes.alwaysShowOutput.newValue);
|
||||
updateOutputVisibility();
|
||||
}
|
||||
|
||||
const configKeys = [
|
||||
"tasks",
|
||||
"envConfigs",
|
||||
"profiles",
|
||||
"shortcuts",
|
||||
"workspaces",
|
||||
"sites",
|
||||
"theme",
|
||||
"alwaysShowOutput",
|
||||
"alwaysUseDefaultEnvProfile"
|
||||
];
|
||||
if (configKeys.some((key) => changes[key])) {
|
||||
scheduleConfigRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender) => {
|
||||
if (message?.type !== "SITE_CONTENT_CHANGED") return;
|
||||
const senderTabId = sender?.tab?.id || null;
|
||||
if (state.activeTabId && senderTabId && senderTabId !== state.activeTabId) {
|
||||
return;
|
||||
}
|
||||
scheduleSiteContentRefresh();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
</div>
|
||||
</summary>
|
||||
<div class="panel-body">
|
||||
<div class="inline-fields two appearance-fields">
|
||||
<div class="inline-fields four appearance-fields">
|
||||
<div class="field">
|
||||
<label for="themeSelect">Theme</label>
|
||||
<select id="themeSelect">
|
||||
@@ -122,6 +122,22 @@
|
||||
<option value="bottom-center">Bottom Center</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="alwaysUseDefaultEnvProfileSelect">
|
||||
Always use default ENV/PROFILE
|
||||
</label>
|
||||
<select id="alwaysUseDefaultEnvProfileSelect">
|
||||
<option value="enabled">Enabled</option>
|
||||
<option value="disabled">Disabled</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="emptyToolbarBehaviorSelect">Empty toolbar</label>
|
||||
<select id="emptyToolbarBehaviorSelect">
|
||||
<option value="hide">Hide Toolbar</option>
|
||||
<option value="open">Show "Open SiteCompanion"</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-fields two appearance-toggles">
|
||||
<div class="field">
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -519,6 +531,35 @@ function scheduleSidebarErrors() {
|
||||
});
|
||||
}
|
||||
|
||||
let tocUpdateFrame = null;
|
||||
function scheduleTocUpdate() {
|
||||
if (!toc) return;
|
||||
if (tocUpdateFrame) return;
|
||||
tocUpdateFrame = requestAnimationFrame(() => {
|
||||
tocUpdateFrame = null;
|
||||
updateToc(collectWorkspaces(), collectSites());
|
||||
});
|
||||
}
|
||||
|
||||
const TOC_NAME_INPUT_SELECTOR = [
|
||||
".api-key-name",
|
||||
".api-config-name",
|
||||
".env-config-name",
|
||||
".profile-name",
|
||||
".task-name",
|
||||
".shortcut-name",
|
||||
".workspace-name",
|
||||
".site-name",
|
||||
".site-pattern"
|
||||
].join(", ");
|
||||
|
||||
function handleTocNameInput(event) {
|
||||
const target = event.target;
|
||||
if (!(target instanceof Element)) return;
|
||||
if (!target.matches(TOC_NAME_INPUT_SELECTOR)) return;
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function renderGlobalSitesList(sites) {
|
||||
if (!globalSitesContainer) return;
|
||||
globalSitesContainer.innerHTML = "";
|
||||
@@ -1230,6 +1271,7 @@ function updateApiConfigControls() {
|
||||
if (moveDownBtn) moveDownBtn.disabled = index === cards.length - 1;
|
||||
});
|
||||
scheduleSidebarErrors();
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function buildApiKeyCard(entry) {
|
||||
@@ -1404,6 +1446,7 @@ function updateApiKeyControls() {
|
||||
if (moveDownBtn) moveDownBtn.disabled = index === cards.length - 1;
|
||||
});
|
||||
scheduleSidebarErrors();
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function updateApiConfigKeyOptions() {
|
||||
@@ -1630,6 +1673,7 @@ function updateEnvControls(container = envConfigsContainer) {
|
||||
if (moveDownBtn) moveDownBtn.disabled = index === cards.length - 1;
|
||||
});
|
||||
scheduleSidebarErrors();
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function updateTaskEnvOptionsForContainer(container, envs, allEnvsById) {
|
||||
@@ -1879,6 +1923,7 @@ function updateProfileControls(container = profilesContainer) {
|
||||
if (moveDownBtn) moveDownBtn.disabled = index === cards.length - 1;
|
||||
});
|
||||
scheduleSidebarErrors();
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function updateTaskProfileOptionsForContainer(container, profiles, allProfilesById) {
|
||||
@@ -1952,7 +1997,9 @@ function updateEnvApiOptionsForContainer(container, apiConfigs) {
|
||||
if (!container) return;
|
||||
const selects = container.querySelectorAll(".env-config-api-select");
|
||||
selects.forEach((select) => {
|
||||
select.dataset.preferred = select.value;
|
||||
if (select.value) {
|
||||
select.dataset.preferred = select.value;
|
||||
}
|
||||
populateSelect(select, apiConfigs, "No API configs configured");
|
||||
});
|
||||
}
|
||||
@@ -2195,15 +2242,21 @@ function updateShortcutOptionsForContainer(container, options = {}) {
|
||||
const profileSelect = card.querySelector(".shortcut-profile");
|
||||
const taskSelect = card.querySelector(".shortcut-task");
|
||||
if (envSelect) {
|
||||
envSelect.dataset.preferred = envSelect.value;
|
||||
if (envSelect.value) {
|
||||
envSelect.dataset.preferred = envSelect.value;
|
||||
}
|
||||
populateSelect(envSelect, envs, "No environments configured");
|
||||
}
|
||||
if (profileSelect) {
|
||||
profileSelect.dataset.preferred = profileSelect.value;
|
||||
if (profileSelect.value) {
|
||||
profileSelect.dataset.preferred = profileSelect.value;
|
||||
}
|
||||
populateSelect(profileSelect, profiles, "No profiles configured");
|
||||
}
|
||||
if (taskSelect) {
|
||||
taskSelect.dataset.preferred = taskSelect.value;
|
||||
if (taskSelect.value) {
|
||||
taskSelect.dataset.preferred = taskSelect.value;
|
||||
}
|
||||
populateSelect(taskSelect, tasks, "No tasks configured");
|
||||
}
|
||||
});
|
||||
@@ -2246,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");
|
||||
@@ -2269,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) : [],
|
||||
@@ -2381,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");
|
||||
@@ -2434,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";
|
||||
@@ -2466,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);
|
||||
@@ -3983,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());
|
||||
}
|
||||
});
|
||||
@@ -3993,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` }
|
||||
);
|
||||
@@ -4214,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");
|
||||
@@ -4231,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) : [],
|
||||
@@ -4380,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` }
|
||||
);
|
||||
@@ -5026,6 +5358,7 @@ function updateShortcutControls(container = shortcutsContainer) {
|
||||
if (moveDownBtn) moveDownBtn.disabled = index === cards.length - 1;
|
||||
});
|
||||
scheduleSidebarErrors();
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function updateTaskControls(container = tasksContainer) {
|
||||
@@ -5039,6 +5372,7 @@ function updateTaskControls(container = tasksContainer) {
|
||||
if (moveDownBtn) moveDownBtn.disabled = index === cards.length - 1;
|
||||
});
|
||||
scheduleSidebarErrors();
|
||||
scheduleTocUpdate();
|
||||
}
|
||||
|
||||
function collectTasks(container = tasksContainer) {
|
||||
@@ -5345,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",
|
||||
@@ -5367,6 +5703,8 @@ async function loadSettings() {
|
||||
"sites",
|
||||
"toolbarPosition",
|
||||
"toolbarAutoHide",
|
||||
"emptyToolbarBehavior",
|
||||
"alwaysUseDefaultEnvProfile",
|
||||
SIDEBAR_WIDTH_KEY
|
||||
]);
|
||||
|
||||
@@ -5379,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);
|
||||
}
|
||||
@@ -5417,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),
|
||||
@@ -5441,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),
|
||||
@@ -5742,6 +6105,7 @@ async function loadSettings() {
|
||||
updateSidebarErrors();
|
||||
updateToc(workspaces, sites);
|
||||
renderGlobalSitesList(sites);
|
||||
updateAppearanceInheritanceIndicators();
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
@@ -5870,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
|
||||
});
|
||||
@@ -6016,6 +6386,8 @@ addWorkspaceBtn.addEventListener("click", () => {
|
||||
name: "New Workspace",
|
||||
theme: "inherit",
|
||||
toolbarPosition: "inherit",
|
||||
alwaysUseDefaultEnvProfile: "inherit",
|
||||
emptyToolbarBehavior: "inherit",
|
||||
envConfigs: [],
|
||||
profiles: [],
|
||||
tasks: [],
|
||||
@@ -6032,6 +6404,7 @@ addWorkspaceBtn.addEventListener("click", () => {
|
||||
centerCardInView(newCard);
|
||||
refreshWorkspaceInheritedLists();
|
||||
scheduleSidebarErrors();
|
||||
updateAppearanceInheritanceIndicators();
|
||||
updateToc(collectWorkspaces(), collectSites());
|
||||
});
|
||||
|
||||
@@ -6044,6 +6417,8 @@ addSiteBtn.addEventListener("click", () => {
|
||||
workspaceId: "global",
|
||||
theme: "inherit",
|
||||
toolbarPosition: "inherit",
|
||||
alwaysUseDefaultEnvProfile: "inherit",
|
||||
emptyToolbarBehavior: "inherit",
|
||||
envConfigs: [],
|
||||
profiles: [],
|
||||
tasks: [],
|
||||
@@ -6060,6 +6435,7 @@ addSiteBtn.addEventListener("click", () => {
|
||||
centerCardInView(newCard);
|
||||
refreshSiteInheritedLists();
|
||||
scheduleSidebarErrors();
|
||||
updateAppearanceInheritanceIndicators();
|
||||
updateToc(collectWorkspaces(), collectSites());
|
||||
});
|
||||
|
||||
@@ -6109,6 +6485,9 @@ async function initSettings() {
|
||||
registerAllDetails();
|
||||
restoreScrollPosition();
|
||||
initDirtyObserver();
|
||||
if (settingsLayout) {
|
||||
settingsLayout.addEventListener("input", handleTocNameInput);
|
||||
}
|
||||
captureSavedSnapshot();
|
||||
suppressDirtyTracking = false;
|
||||
window.addEventListener("scroll", handleSettingsScroll, { passive: true });
|
||||
@@ -6563,6 +6942,7 @@ function handleSettingsInputChange() {
|
||||
scheduleSidebarErrors();
|
||||
scheduleDirtyCheck();
|
||||
refreshInheritedSourceLabels();
|
||||
updateAppearanceInheritanceIndicators();
|
||||
}
|
||||
|
||||
document.addEventListener("input", handleSettingsInputChange);
|
||||
|
||||
Reference in New Issue
Block a user