diff --git a/sitecompanion/popup.js b/sitecompanion/popup.js
index f6b89e8..ed8420d 100644
--- a/sitecompanion/popup.js
+++ b/sitecompanion/popup.js
@@ -18,6 +18,7 @@ const SHORTCUT_RUN_KEY = "runShortcutId";
const LAST_TASK_KEY = "lastSelectedTaskId";
const LAST_ENV_KEY = "lastSelectedEnvId";
const LAST_PROFILE_KEY = "lastSelectedProfileId";
+const POPUP_DRAFT_KEY = "popupDraft";
const unknownSiteState = document.getElementById("unknownSiteState");
const extractionReviewState = document.getElementById("extractionReviewState");
@@ -25,6 +26,7 @@ const normalExecutionState = document.getElementById("normalExecutionState");
const partialTextPaste = document.getElementById("partialTextPaste");
const extractFullBtn = document.getElementById("extractFullBtn");
const extractedPreview = document.getElementById("extractedPreview");
+const siteNameInput = document.getElementById("siteNameInput");
const urlPatternInput = document.getElementById("urlPatternInput");
const retryExtractBtn = document.getElementById("retryExtractBtn");
const confirmSiteBtn = document.getElementById("confirmSiteBtn");
@@ -44,6 +46,9 @@ const state = {
outputRaw: "",
autoRunPending: false,
shortcutRunPending: false,
+ currentPopupState: "unknown",
+ globalTheme: "system",
+ forcedTask: null,
selectedTaskId: "",
selectedEnvId: "",
selectedProfileId: ""
@@ -53,6 +58,7 @@ async function switchState(stateName) {
unknownSiteState.classList.add("hidden");
extractionReviewState.classList.add("hidden");
normalExecutionState.classList.add("hidden");
+ state.currentPopupState = stateName;
if (stateName === "unknown") {
unknownSiteState.classList.remove("hidden");
@@ -64,6 +70,37 @@ async function switchState(stateName) {
await chrome.storage.local.set({ lastPopupState: stateName });
}
+function buildPopupDraft() {
+ return {
+ state: state.currentPopupState,
+ siteText: state.siteText || "",
+ urlPattern: urlPatternInput?.value?.trim() || "",
+ siteName: siteNameInput?.value?.trim() || ""
+ };
+}
+
+async function persistPopupDraft() {
+ await chrome.storage.local.set({ [POPUP_DRAFT_KEY]: buildPopupDraft() });
+}
+
+async function clearPopupDraft() {
+ await chrome.storage.local.remove(POPUP_DRAFT_KEY);
+}
+
+function applyPopupDraft(draft) {
+ if (!draft || typeof draft !== "object") return;
+ if (draft.siteText) {
+ state.siteText = draft.siteText;
+ extractedPreview.textContent = state.siteText;
+ }
+ if (typeof draft.urlPattern === "string") {
+ urlPatternInput.value = draft.urlPattern;
+ }
+ if (typeof draft.siteName === "string") {
+ siteNameInput.value = draft.siteName;
+ }
+}
+
function matchUrl(url, pattern) {
if (!pattern) return false;
let regex = null;
@@ -112,6 +149,20 @@ function resolveScopedItems(parentItems, localItems, disabledNames) {
return [...inherited, ...localEnabled];
}
+function findTaskById(taskId, globalTasks, workspace, site) {
+ if (!taskId) return null;
+ const lists = [
+ Array.isArray(site?.tasks) ? site.tasks : [],
+ Array.isArray(workspace?.tasks) ? workspace.tasks : [],
+ Array.isArray(globalTasks) ? globalTasks : []
+ ];
+ for (const list of lists) {
+ const found = list.find((item) => item?.id === taskId);
+ if (found) return found;
+ }
+ return null;
+}
+
function resolveEffectiveList(globalItems, workspace, site, listKey, disabledKey) {
const workspaceItems = workspace?.[listKey] || [];
const workspaceDisabled = workspace?.disabledInherited?.[disabledKey] || [];
@@ -378,6 +429,14 @@ function applyTheme(theme) {
document.documentElement.dataset.theme = value;
}
+function resolveThemeForPopup(baseTheme) {
+ const siteTheme = state.currentSite?.theme;
+ if (siteTheme && siteTheme !== "inherit") return siteTheme;
+ const workspaceTheme = state.currentWorkspace?.theme;
+ if (workspaceTheme && workspaceTheme !== "inherit") return workspaceTheme;
+ return baseTheme || "system";
+}
+
function setAnalyzing(isAnalyzing) {
state.isAnalyzing = isAnalyzing;
runBtn.disabled = isAnalyzing;
@@ -604,17 +663,20 @@ async function loadConfig() {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
const currentUrl = tabs[0]?.url || "";
- const { lastPopupState } = await getStorage(["lastPopupState"]);
+ const { lastPopupState, [POPUP_DRAFT_KEY]: popupDraft } = await getStorage([
+ "lastPopupState",
+ POPUP_DRAFT_KEY
+ ]);
await detectSite(currentUrl);
- if (lastPopupState && lastPopupState !== "unknown") {
- // If we had a state like 'review', we might want to stay there,
- // but detectSite might have switched to 'normal' if it matched.
- // AGENTS.md says popup state must be persisted.
- if (state.currentSite && lastPopupState === "normal") {
- await switchState("normal");
- } else if (!state.currentSite && (lastPopupState === "unknown" || lastPopupState === "review")) {
- await switchState(lastPopupState);
+ if (state.currentSite) {
+ if (lastPopupState === "normal") {
+ await switchState("normal");
}
+ } else if (popupDraft?.state === "review") {
+ applyPopupDraft(popupDraft);
+ await switchState("review");
+ } else if (lastPopupState === "unknown") {
+ await switchState("unknown");
}
const stored = await getStorage([
@@ -624,6 +686,7 @@ async function loadConfig() {
"shortcuts",
"workspaces",
"sites",
+ "theme",
LAST_TASK_KEY,
LAST_ENV_KEY,
LAST_PROFILE_KEY
@@ -650,6 +713,10 @@ async function loadConfig() {
state.currentWorkspace = activeWorkspace;
currentWorkspaceName.textContent = activeWorkspace.name || "Global";
}
+ if (stored.theme) {
+ state.globalTheme = stored.theme;
+ }
+ applyTheme(resolveThemeForPopup(state.globalTheme));
const effectiveEnvs = resolveEffectiveList(
envs,
@@ -721,7 +788,8 @@ async function loadConfig() {
async function loadTheme() {
const { theme = "system" } = await getStorage(["theme"]);
- applyTheme(theme);
+ state.globalTheme = theme;
+ applyTheme(resolveThemeForPopup(theme));
}
async function handleExtract() {
@@ -758,7 +826,11 @@ async function handleAnalyze() {
}
const taskId = taskSelect.value;
- const task = state.tasks.find((item) => item.id === taskId);
+ const forcedTask = state.forcedTask;
+ const task = forcedTask || state.tasks.find((item) => item.id === taskId);
+ if (forcedTask) {
+ state.forcedTask = null;
+ }
if (!task) {
setStatus("Select a task.");
return;
@@ -947,6 +1019,16 @@ function handleCopyRaw() {
void copyTextToClipboard(text, "Markdown");
}
+async function fillSiteDefaultsFromTab() {
+ const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
+ if (!tabs[0]?.url) return;
+ const url = new URL(tabs[0].url);
+ urlPatternInput.value = url.hostname + url.pathname + "*";
+ if (!siteNameInput.value.trim()) {
+ siteNameInput.value = url.hostname;
+ }
+}
+
partialTextPaste.addEventListener("input", async () => {
const text = partialTextPaste.value.trim();
if (text.length < 5) return;
@@ -957,10 +1039,9 @@ partialTextPaste.addEventListener("input", async () => {
if (response?.ok) {
state.siteText = response.extracted;
extractedPreview.textContent = state.siteText;
- const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
- const url = new URL(tabs[0].url);
- urlPatternInput.value = url.hostname + url.pathname + "*";
+ await fillSiteDefaultsFromTab();
switchState("review");
+ await persistPopupDraft();
setStatus("Review extraction.");
}
} catch (error) {
@@ -975,10 +1056,9 @@ extractFullBtn.addEventListener("click", async () => {
if (response?.ok) {
state.siteText = response.extracted;
extractedPreview.textContent = state.siteText;
- const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
- const url = new URL(tabs[0].url);
- urlPatternInput.value = url.hostname + url.pathname + "*";
+ await fillSiteDefaultsFromTab();
switchState("review");
+ await persistPopupDraft();
setStatus("Review extraction.");
}
} catch (error) {
@@ -986,14 +1066,34 @@ extractFullBtn.addEventListener("click", async () => {
}
});
+siteNameInput.addEventListener("input", () => {
+ if (state.currentPopupState !== "review") return;
+ void persistPopupDraft();
+});
+
+urlPatternInput.addEventListener("input", () => {
+ if (state.currentPopupState !== "review") return;
+ void persistPopupDraft();
+});
+
retryExtractBtn.addEventListener("click", () => {
switchState("unknown");
partialTextPaste.value = "";
+ extractedPreview.textContent = "";
+ urlPatternInput.value = "";
+ siteNameInput.value = "";
+ state.siteText = "";
+ void clearPopupDraft();
setStatus("Ready.");
});
confirmSiteBtn.addEventListener("click", async () => {
+ const name = siteNameInput.value.trim();
const pattern = urlPatternInput.value.trim();
+ if (!name) {
+ setStatus("Enter a site name.");
+ return;
+ }
if (!pattern) {
setStatus("Enter a URL pattern.");
return;
@@ -1008,12 +1108,14 @@ confirmSiteBtn.addEventListener("click", async () => {
const newSite = {
id: `site-${Date.now()}`,
+ name,
urlPattern: pattern,
workspaceId: "global" // Default to global for now
};
state.sites.push(newSite);
await chrome.storage.local.set({ sites: state.sites });
+ await clearPopupDraft();
state.currentSite = newSite;
state.currentWorkspace = { name: "Global", id: "global" };
currentWorkspaceName.textContent = "Global";
@@ -1058,7 +1160,8 @@ async function loadShortcutRunRequest() {
SHORTCUT_RUN_KEY,
"shortcuts",
"workspaces",
- "sites"
+ "sites",
+ "tasks"
]);
const shortcutId = stored[SHORTCUT_RUN_KEY];
if (!shortcutId) return;
@@ -1066,7 +1169,12 @@ async function loadShortcutRunRequest() {
state.shortcutRunPending = true;
await chrome.storage.local.remove(SHORTCUT_RUN_KEY);
+ if (!state.tasks.length) {
+ await loadConfig();
+ }
+
const globalShortcuts = normalizeConfigList(stored.shortcuts);
+ const globalTasks = normalizeConfigList(stored.tasks);
const sites = Array.isArray(stored.sites) ? stored.sites : state.sites;
const workspaces = Array.isArray(stored.workspaces)
? stored.workspaces
@@ -1092,9 +1200,28 @@ async function loadShortcutRunRequest() {
return;
}
- if (shortcut.taskId) {
- selectTask(shortcut.taskId, { resetEnv: true });
+ const shortcutTaskId = shortcut.taskId || "";
+ const scopedTask = findTaskById(
+ shortcutTaskId,
+ globalTasks,
+ activeWorkspace,
+ activeSite
+ );
+ if (shortcutTaskId && !scopedTask) {
+ setStatus("Shortcut task is unavailable.");
+ state.shortcutRunPending = false;
+ return;
}
+
+ if (scopedTask && state.tasks.some((item) => item.id === scopedTask.id)) {
+ selectTask(scopedTask.id, { resetEnv: true });
+ } else if (!state.tasks.length && !scopedTask) {
+ setStatus("No tasks configured.");
+ state.shortcutRunPending = false;
+ return;
+ }
+
+ state.forcedTask = scopedTask || null;
if (shortcut.envId) {
setEnvironmentSelection(shortcut.envId);
}
@@ -1156,6 +1283,7 @@ chrome.storage.onChanged.addListener((changes) => {
}
if (changes.theme) {
- applyTheme(changes.theme.newValue || "system");
+ state.globalTheme = changes.theme.newValue || "system";
+ applyTheme(resolveThemeForPopup(state.globalTheme));
}
});
diff --git a/sitecompanion/settings.css b/sitecompanion/settings.css
index 8cd78ea..927ebfd 100644
--- a/sitecompanion/settings.css
+++ b/sitecompanion/settings.css
@@ -49,6 +49,7 @@ body {
.toc {
flex: 0 0 160px;
+ width: 160px;
display: flex;
flex-direction: column;
gap: 8px;
@@ -56,6 +57,9 @@ body {
position: sticky;
top: 16px;
padding: 12px;
+ max-height: calc(100vh - 32px);
+ min-height: 0;
+ overflow: hidden;
border-radius: 12px;
border: 1px solid var(--border);
background: var(--panel);
@@ -77,6 +81,24 @@ body {
.toc-links {
display: block;
+ overflow-y: auto;
+ flex: 1 1 auto;
+ min-height: 0;
+}
+
+.toc-resizer {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 6px;
+ height: 100%;
+ cursor: col-resize;
+ background: transparent;
+}
+
+body.is-resizing {
+ cursor: col-resize;
+ user-select: none;
}
.toc ul {
@@ -148,7 +170,7 @@ body {
}
.sidebar-errors {
- margin-top: auto;
+ margin-top: 8px;
border-radius: 10px;
border: 1px solid #c0392b;
background: rgba(192, 57, 43, 0.08);
@@ -172,14 +194,6 @@ body {
margin-bottom: 16px;
}
-.page-bar {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- gap: 12px;
- margin-bottom: 16px;
-}
-
.title {
font-size: 26px;
font-weight: 700;
@@ -376,6 +390,30 @@ button:active {
gap: 12px;
}
+.workspaces,
+.sites {
+ display: grid;
+ gap: 12px;
+}
+
+.workspace-card,
+.site-card {
+ padding: 12px;
+ display: grid;
+ gap: 12px;
+ overflow: visible;
+}
+
+.workspace-header,
+.site-header {
+ align-items: flex-end;
+}
+
+.workspace-header .field,
+.site-header .field {
+ margin-bottom: 0;
+}
+
.task-card {
padding: 12px;
border-radius: 12px;
@@ -511,12 +549,22 @@ button:active {
.api-key-actions .delete,
.api-config-actions .delete,
.env-config-actions .delete,
-.task-actions .delete {
+.task-actions .delete,
+.shortcut-actions .delete {
background: #c0392b;
border-color: #c0392b;
color: #fff6f2;
}
+.workspace-header .delete,
+.site-header .delete {
+ background: #c0392b;
+ border-color: #c0392b;
+ color: #fff6f2;
+ padding: 10px 12px;
+ font-size: 13px;
+}
+
.api-configs {
display: grid;
gap: 12px;
@@ -622,6 +670,12 @@ button:active {
justify-content: flex-end;
}
+.shortcut-actions {
+ display: flex;
+ gap: 6px;
+ justify-content: flex-end;
+}
+
@media (max-width: 720px) {
.settings-layout {
flex-direction: column;
diff --git a/sitecompanion/settings.html b/sitecompanion/settings.html
index 0408bc4..4965fa9 100644
--- a/sitecompanion/settings.html
+++ b/sitecompanion/settings.html
@@ -11,16 +11,12 @@
SiteCompanion Settings
Configure workspaces, tasks, and API access
-
-
diff --git a/sitecompanion/settings.js b/sitecompanion/settings.js
index f6ce166..3f501a4 100644
--- a/sitecompanion/settings.js
+++ b/sitecompanion/settings.js
@@ -1,4 +1,3 @@
-const saveBtn = document.getElementById("saveBtn");
const saveBtnSidebar = document.getElementById("saveBtnSidebar");
const addApiConfigBtn = document.getElementById("addApiConfigBtn");
const apiConfigsContainer = document.getElementById("apiConfigs");
@@ -16,13 +15,14 @@ const addSiteBtn = document.getElementById("addSiteBtn");
const sitesContainer = document.getElementById("sites");
const addShortcutBtn = document.getElementById("addShortcutBtn");
const shortcutsContainer = document.getElementById("shortcuts");
-const statusEl = document.getElementById("status");
const statusSidebarEl = document.getElementById("statusSidebar");
const sidebarErrorsEl = document.getElementById("sidebarErrors");
const themeSelect = document.getElementById("themeSelect");
const toolbarPositionSelect = document.getElementById("toolbarPositionSelect");
const toolbarAutoHide = document.getElementById("toolbarAutoHide");
const globalSitesContainer = document.getElementById("globalSites");
+const toc = document.querySelector(".toc");
+const tocResizer = document.getElementById("tocResizer");
const OPENAI_DEFAULTS = {
apiBaseUrl: "https://api.openai.com/v1",
@@ -32,17 +32,63 @@ const OPENAI_DEFAULTS = {
const DEFAULT_MODEL = "gpt-4o-mini";
const DEFAULT_SYSTEM_PROMPT =
"You are a precise, honest assistant. Be concise and avoid inventing details, be critical about evaluations. You should put in a small summary of all the sections at the end. You should answer in no longer than 3 sections including the summary. And remember to bold or italicize key points.";
+const SIDEBAR_WIDTH_KEY = "sidebarWidth";
+
+function getSidebarWidthLimits() {
+ const min = 160;
+ const max = Math.max(min, Math.min(360, window.innerWidth - 240));
+ return { min, max };
+}
+
+function applySidebarWidth(width) {
+ if (!toc) return;
+ const { min, max } = getSidebarWidthLimits();
+ if (!Number.isFinite(width)) {
+ toc.style.width = "";
+ toc.style.flex = "";
+ return;
+ }
+ const clamped = Math.min(Math.max(width, min), max);
+ toc.style.width = `${clamped}px`;
+ toc.style.flex = `0 0 ${clamped}px`;
+}
+
+function initSidebarResize() {
+ if (!toc || !tocResizer) return;
+ let startX = 0;
+ let startWidth = 0;
+
+ const onMouseMove = (event) => {
+ const delta = event.clientX - startX;
+ applySidebarWidth(startWidth + delta);
+ };
+
+ const onMouseUp = () => {
+ document.removeEventListener("mousemove", onMouseMove);
+ document.removeEventListener("mouseup", onMouseUp);
+ document.body.classList.remove("is-resizing");
+ const width = toc.getBoundingClientRect().width;
+ void chrome.storage.local.set({ [SIDEBAR_WIDTH_KEY]: Math.round(width) });
+ };
+
+ tocResizer.addEventListener("mousedown", (event) => {
+ event.preventDefault();
+ startX = event.clientX;
+ startWidth = toc.getBoundingClientRect().width;
+ document.body.classList.add("is-resizing");
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("mouseup", onMouseUp);
+ });
+}
function getStorage(keys) {
return new Promise((resolve) => chrome.storage.local.get(keys, resolve));
}
function setStatus(message) {
- statusEl.textContent = message;
if (statusSidebarEl) statusSidebarEl.textContent = message;
if (!message) return;
setTimeout(() => {
- if (statusEl.textContent === message) statusEl.textContent = "";
if (statusSidebarEl?.textContent === message) statusSidebarEl.textContent = "";
}, 2000);
}
@@ -73,7 +119,7 @@ function renderGlobalSitesList(sites) {
for (const site of globalSites) {
const link = document.createElement("a");
link.href = "#";
- link.textContent = site.urlPattern || "Untitled Site";
+ link.textContent = site.name || site.urlPattern || "Untitled Site";
link.addEventListener("click", (e) => {
e.preventDefault();
const card = document.querySelector(`.site-card[data-id="${site.id}"]`);
@@ -1863,7 +1909,13 @@ function buildScopedModuleSection({
const newItem = newItemFactory(localContainer);
const options =
typeof cardOptions === "function" ? cardOptions() : cardOptions;
- localContainer.appendChild(buildCard(newItem, localContainer, options));
+ const newCard = buildCard(newItem, localContainer, options);
+ const first = localContainer.firstElementChild;
+ if (first) {
+ localContainer.insertBefore(newCard, first);
+ } else {
+ localContainer.appendChild(newCard);
+ }
if (module === "envs") {
updateEnvApiOptions();
} else if (module === "profiles") {
@@ -1933,7 +1985,10 @@ function listWorkspaceTargets() {
function listSiteTargets() {
return [...sitesContainer.querySelectorAll(".site-card")].map((card) => ({
id: card.dataset.id || "",
- name: card.querySelector(".site-pattern")?.value || "Untitled Site"
+ name:
+ card.querySelector(".site-name")?.value ||
+ card.querySelector(".site-pattern")?.value ||
+ "Untitled Site"
}));
}
@@ -2214,6 +2269,7 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) {
const header = document.createElement("div");
header.className = "row workspace-header";
+ header.style.alignItems = "flex-end";
const nameField = document.createElement("div");
nameField.className = "field";
@@ -2387,7 +2443,7 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) {
for (const site of ownedSites) {
const link = document.createElement("a");
link.href = "#";
- link.textContent = site.urlPattern || "Untitled Site";
+ link.textContent = site.name || site.urlPattern || "Untitled Site";
link.addEventListener("click", (e) => {
e.preventDefault();
const siteCard = document.querySelector(
@@ -2411,6 +2467,7 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) {
function collectSites() {
const cards = [...sitesContainer.querySelectorAll(".site-card")];
return cards.map((card) => {
+ const nameInput = card.querySelector(".site-name");
const patternInput = card.querySelector(".site-pattern");
const workspaceSelect = card.querySelector(".site-workspace");
const themeSelect = card.querySelector(".appearance-theme");
@@ -2426,6 +2483,7 @@ function collectSites() {
const apiConfigsInherited = card.querySelector('.inherited-list[data-module="apiConfigs"]');
return {
id: card.dataset.id || newSiteId(),
+ name: (nameInput?.value || "").trim(),
urlPattern: (patternInput?.value || "").trim(),
workspaceId: workspaceSelect?.value || "global",
theme: themeSelect?.value || "inherit",
@@ -2451,9 +2509,26 @@ function buildSiteCard(site, allWorkspaces = []) {
card.dataset.id = site.id || newSiteId();
const row = document.createElement("div");
- row.className = "row";
+ row.className = "row site-header";
row.style.alignItems = "flex-end";
+ const nameField = document.createElement("div");
+ nameField.className = "field";
+ nameField.style.flex = "0.6";
+ const nameLabel = document.createElement("label");
+ nameLabel.textContent = "Site name";
+ const nameInput = document.createElement("input");
+ nameInput.type = "text";
+ nameInput.value = site.name || "";
+ nameInput.className = "site-name";
+ nameInput.placeholder = "Site Name";
+ nameInput.addEventListener("input", () => {
+ updateToc(collectWorkspaces(), collectSites());
+ scheduleSidebarErrors();
+ });
+ nameField.appendChild(nameLabel);
+ nameField.appendChild(nameInput);
+
const patternField = document.createElement("div");
patternField.className = "field";
patternField.style.flex = "1";
@@ -2519,6 +2594,7 @@ function buildSiteCard(site, allWorkspaces = []) {
scheduleSidebarErrors();
});
+ row.appendChild(nameField);
row.appendChild(patternField);
row.appendChild(wsField);
row.appendChild(deleteBtn);
@@ -2924,11 +3000,13 @@ function buildShortcutCard(shortcut, _container, options = {}) {
taskField.appendChild(taskLabel);
taskField.appendChild(taskSelect);
+ const actions = document.createElement("div");
+ actions.className = "shortcut-actions";
+
const deleteBtn = document.createElement("button");
deleteBtn.type = "button";
deleteBtn.className = "ghost delete";
deleteBtn.textContent = "Delete";
- deleteBtn.style.marginTop = "8px";
deleteBtn.addEventListener("click", () => {
card.remove();
scheduleSidebarErrors();
@@ -2939,7 +3017,7 @@ function buildShortcutCard(shortcut, _container, options = {}) {
card.appendChild(envField);
card.appendChild(profileField);
card.appendChild(taskField);
- card.appendChild(
+ actions.appendChild(
buildDuplicateControls("shortcuts", () => ({
id: card.dataset.id,
name: nameInput.value || "Untitled Shortcut",
@@ -2949,7 +3027,8 @@ function buildShortcutCard(shortcut, _container, options = {}) {
enabled: enabledInput.checked
}))
);
- card.appendChild(deleteBtn);
+ actions.appendChild(deleteBtn);
+ card.appendChild(actions);
return card;
}
@@ -3071,7 +3150,10 @@ function updateSidebarErrors() {
const siteCards = [...sitesContainer.querySelectorAll(".site-card")];
siteCards.forEach((card) => {
- const label = card.querySelector(".site-pattern")?.value || "Site";
+ const label =
+ card.querySelector(".site-name")?.value ||
+ card.querySelector(".site-pattern")?.value ||
+ "Site";
checkNameInputs(
card.querySelector(".site-envs"),
".env-config-name",
@@ -3094,6 +3176,8 @@ function updateSidebarErrors() {
);
});
+ checkNameInputs(sitesContainer, ".site-name", "Sites");
+
if (!enabledTasks.length) errors.push("No tasks enabled.");
if (!enabledEnvs.length) errors.push("No environments enabled.");
if (!enabledProfiles.length) errors.push("No profiles enabled.");
@@ -3206,7 +3290,8 @@ async function loadSettings() {
workspaces = [],
sites = [],
toolbarPosition = "bottom-right",
- toolbarAutoHide: storedToolbarAutoHide = true
+ toolbarAutoHide: storedToolbarAutoHide = true,
+ sidebarWidth
} = await getStorage([
"apiKey",
"apiKeys",
@@ -3229,7 +3314,8 @@ async function loadSettings() {
"workspaces",
"sites",
"toolbarPosition",
- "toolbarAutoHide"
+ "toolbarAutoHide",
+ SIDEBAR_WIDTH_KEY
]);
themeSelect.value = theme;
@@ -3241,6 +3327,9 @@ async function loadSettings() {
if (toolbarAutoHide) {
toolbarAutoHide.checked = Boolean(storedToolbarAutoHide);
}
+ if (Number.isFinite(sidebarWidth)) {
+ applySidebarWidth(sidebarWidth);
+ }
if (!shortcuts.length && Array.isArray(legacyPresets) && legacyPresets.length) {
shortcuts = legacyPresets;
@@ -3287,6 +3376,7 @@ async function loadSettings() {
if (!site || typeof site !== "object") return site;
return {
...site,
+ name: site.name || site.urlPattern || "",
workspaceId: site.workspaceId || "global",
theme: site.theme || "inherit",
toolbarPosition: site.toolbarPosition || "inherit",
@@ -3638,7 +3728,6 @@ async function saveSettings() {
}
}
-saveBtn.addEventListener("click", () => void saveSettings());
if (saveBtnSidebar) {
saveBtnSidebar.addEventListener("click", () => void saveSettings());
}
@@ -3759,7 +3848,12 @@ addWorkspaceBtn.addEventListener("click", () => {
shortcuts: [],
disabledInherited: normalizeDisabledInherited()
}, collectWorkspaces(), collectSites());
- workspacesContainer.appendChild(newCard);
+ const first = workspacesContainer.firstElementChild;
+ if (first) {
+ workspacesContainer.insertBefore(newCard, first);
+ } else {
+ workspacesContainer.appendChild(newCard);
+ }
refreshWorkspaceInheritedLists();
scheduleSidebarErrors();
updateToc(collectWorkspaces(), collectSites());
@@ -3768,6 +3862,7 @@ addWorkspaceBtn.addEventListener("click", () => {
addSiteBtn.addEventListener("click", () => {
const newCard = buildSiteCard({
id: newSiteId(),
+ name: "",
urlPattern: "",
workspaceId: "global",
theme: "inherit",
@@ -3778,7 +3873,12 @@ addSiteBtn.addEventListener("click", () => {
shortcuts: [],
disabledInherited: normalizeDisabledInherited()
}, collectWorkspaces());
- sitesContainer.appendChild(newCard);
+ const first = sitesContainer.firstElementChild;
+ if (first) {
+ sitesContainer.insertBefore(newCard, first);
+ } else {
+ sitesContainer.appendChild(newCard);
+ }
refreshSiteInheritedLists();
scheduleSidebarErrors();
updateToc(collectWorkspaces(), collectSites());
@@ -3792,11 +3892,17 @@ addShortcutBtn.addEventListener("click", () => {
profileId: "",
taskId: ""
});
- shortcutsContainer.appendChild(newCard);
+ const first = shortcutsContainer.firstElementChild;
+ if (first) {
+ shortcutsContainer.insertBefore(newCard, first);
+ } else {
+ shortcutsContainer.appendChild(newCard);
+ }
scheduleSidebarErrors();
});
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
+initSidebarResize();
loadSettings();
@@ -3908,7 +4014,7 @@ function updateToc(workspaces, sites) {
for (const site of sites) {
const li = document.createElement("li");
const a = document.createElement("a");
- a.textContent = site.urlPattern || "Untitled Site";
+ a.textContent = site.name || site.urlPattern || "Untitled Site";
a.href = "#";
a.addEventListener("click", (e) => {
e.preventDefault();