fixed Shortcuts bar bug, added UI improvements, [unfixed] sections folding
This commit is contained in:
@@ -47,13 +47,56 @@ function resolveScopedItems(parentItems, localItems, disabledNames) {
|
|||||||
return [...inherited, ...localEnabled];
|
return [...inherited, ...localEnabled];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createToolbar(shortcuts, position = "bottom-right") {
|
function resolveThemeValue(globalTheme, workspace, site) {
|
||||||
|
const siteTheme = site?.theme;
|
||||||
|
if (siteTheme && siteTheme !== "inherit") return siteTheme;
|
||||||
|
const workspaceTheme = workspace?.theme;
|
||||||
|
if (workspaceTheme && workspaceTheme !== "inherit") return workspaceTheme;
|
||||||
|
return globalTheme || "system";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveThemeMode(theme) {
|
||||||
|
if (theme === "dark" || theme === "light") return theme;
|
||||||
|
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||||
|
return "dark";
|
||||||
|
}
|
||||||
|
return "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getToolbarThemeTokens(mode) {
|
||||||
|
if (mode === "dark") {
|
||||||
|
return {
|
||||||
|
ink: "#abb2bf",
|
||||||
|
muted: "#8b93a5",
|
||||||
|
accent: "#61afef",
|
||||||
|
accentDeep: "#56b6c2",
|
||||||
|
panel: "#2f343f",
|
||||||
|
border: "#3e4451",
|
||||||
|
glow: "rgba(97, 175, 239, 0.2)",
|
||||||
|
shadow: "rgba(0, 0, 0, 0.35)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ink: "#1f1a17",
|
||||||
|
muted: "#6b5f55",
|
||||||
|
accent: "#b14d2b",
|
||||||
|
accentDeep: "#7d321b",
|
||||||
|
panel: "#fff7ec",
|
||||||
|
border: "#e4d6c5",
|
||||||
|
glow: "rgba(177, 77, 43, 0.18)",
|
||||||
|
shadow: "rgba(122, 80, 47, 0.12)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createToolbar(shortcuts, position = "bottom-right", themeMode = "light") {
|
||||||
let toolbar = document.getElementById("sitecompanion-toolbar");
|
let toolbar = document.getElementById("sitecompanion-toolbar");
|
||||||
if (toolbar) toolbar.remove();
|
if (toolbar) toolbar.remove();
|
||||||
|
|
||||||
toolbar = document.createElement("div");
|
toolbar = document.createElement("div");
|
||||||
toolbar.id = "sitecompanion-toolbar";
|
toolbar.id = "sitecompanion-toolbar";
|
||||||
|
|
||||||
|
const tokens = getToolbarThemeTokens(themeMode);
|
||||||
|
|
||||||
let posStyle = "";
|
let posStyle = "";
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case "top-left":
|
case "top-left":
|
||||||
@@ -77,35 +120,38 @@ function createToolbar(shortcuts, position = "bottom-right") {
|
|||||||
toolbar.style.cssText = `
|
toolbar.style.cssText = `
|
||||||
position: fixed;
|
position: fixed;
|
||||||
${posStyle}
|
${posStyle}
|
||||||
background: #fff7ec;
|
background: ${tokens.panel};
|
||||||
border: 1px solid #e4d6c5;
|
border: 1px solid ${tokens.border};
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
box-shadow: 0 12px 30px ${tokens.shadow};
|
||||||
z-index: 999999;
|
z-index: 2147483647;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||||
|
color: ${tokens.ink};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (!shortcuts || !shortcuts.length) {
|
if (!shortcuts || !shortcuts.length) {
|
||||||
const label = document.createElement("span");
|
const label = document.createElement("span");
|
||||||
label.textContent = "SiteCompanion";
|
label.textContent = "SiteCompanion";
|
||||||
label.style.fontSize = "12px";
|
label.style.fontSize = "12px";
|
||||||
label.style.color = "#6b5f55";
|
label.style.color = tokens.muted;
|
||||||
toolbar.appendChild(label);
|
toolbar.appendChild(label);
|
||||||
} else {
|
} else {
|
||||||
for (const shortcut of shortcuts) {
|
for (const shortcut of shortcuts) {
|
||||||
const btn = document.createElement("button");
|
const btn = document.createElement("button");
|
||||||
|
btn.type = "button";
|
||||||
btn.textContent = shortcut.name;
|
btn.textContent = shortcut.name;
|
||||||
btn.style.cssText = `
|
btn.style.cssText = `
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
background: #b14d2b;
|
background: ${tokens.accent};
|
||||||
color: white;
|
color: #fff9f3;
|
||||||
border: none;
|
border: 1px solid ${tokens.accent};
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
box-shadow: 0 8px 20px ${tokens.glow};
|
||||||
`;
|
`;
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
chrome.runtime.sendMessage({ type: "RUN_SHORTCUT", shortcutId: shortcut.id });
|
chrome.runtime.sendMessage({ type: "RUN_SHORTCUT", shortcutId: shortcut.id });
|
||||||
@@ -138,24 +184,35 @@ function matchUrl(url, pattern) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let suppressObserver = false;
|
||||||
|
|
||||||
async function refreshToolbar() {
|
async function refreshToolbar() {
|
||||||
|
suppressObserver = true;
|
||||||
let {
|
let {
|
||||||
sites = [],
|
sites = [],
|
||||||
workspaces = [],
|
workspaces = [],
|
||||||
shortcuts = [],
|
shortcuts = [],
|
||||||
presets = [],
|
presets = [],
|
||||||
toolbarPosition = "bottom-right"
|
toolbarPosition = "bottom-right",
|
||||||
|
theme = "system"
|
||||||
} = await chrome.storage.local.get([
|
} = await chrome.storage.local.get([
|
||||||
"sites",
|
"sites",
|
||||||
"workspaces",
|
"workspaces",
|
||||||
"shortcuts",
|
"shortcuts",
|
||||||
"presets",
|
"presets",
|
||||||
"toolbarPosition"
|
"toolbarPosition",
|
||||||
|
"theme"
|
||||||
]);
|
]);
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
const site = sites.find(s => matchUrl(currentUrl, s.urlPattern));
|
const site = sites.find(s => matchUrl(currentUrl, s.urlPattern));
|
||||||
|
|
||||||
if (site) {
|
try {
|
||||||
|
if (!site) {
|
||||||
|
const toolbar = document.getElementById("sitecompanion-toolbar");
|
||||||
|
if (toolbar) toolbar.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!shortcuts.length && Array.isArray(presets) && presets.length) {
|
if (!shortcuts.length && Array.isArray(presets) && presets.length) {
|
||||||
shortcuts = presets;
|
shortcuts = presets;
|
||||||
await chrome.storage.local.set({ shortcuts });
|
await chrome.storage.local.set({ shortcuts });
|
||||||
@@ -181,21 +238,33 @@ async function refreshToolbar() {
|
|||||||
: workspace?.toolbarPosition && workspace.toolbarPosition !== "inherit"
|
: workspace?.toolbarPosition && workspace.toolbarPosition !== "inherit"
|
||||||
? workspace.toolbarPosition
|
? workspace.toolbarPosition
|
||||||
: toolbarPosition;
|
: toolbarPosition;
|
||||||
createToolbar(siteShortcuts, resolvedPosition);
|
const resolvedTheme = resolveThemeValue(theme, workspace, site);
|
||||||
|
const themeMode = resolveThemeMode(resolvedTheme);
|
||||||
|
createToolbar(siteShortcuts, resolvedPosition, themeMode);
|
||||||
|
} finally {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
suppressObserver = false;
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let refreshTimer = null;
|
||||||
|
function scheduleToolbarRefresh() {
|
||||||
|
if (refreshTimer) return;
|
||||||
|
refreshTimer = window.setTimeout(() => {
|
||||||
|
refreshTimer = null;
|
||||||
|
void refreshToolbar();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
|
if (suppressObserver) return;
|
||||||
// refreshToolbar(); // Debounce this?
|
scheduleToolbarRefresh();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, { childList: true, subtree: true });
|
||||||
|
|
||||||
// observer.observe(document.documentElement, { childList: true, subtree: true });
|
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||||
if (!message || typeof message !== "object") return;
|
if (!message || typeof message !== "object") return;
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
<div class="state-body">
|
<div class="state-body">
|
||||||
<p>Review extracted text:</p>
|
<p>Review extracted text:</p>
|
||||||
<div id="extractedPreview" class="preview-box"></div>
|
<div id="extractedPreview" class="preview-box"></div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="siteNameInput">Site Name</label>
|
||||||
|
<input type="text" id="siteNameInput" placeholder="Example" />
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="urlPatternInput">URL Match Pattern</label>
|
<label for="urlPatternInput">URL Match Pattern</label>
|
||||||
<input type="text" id="urlPatternInput" placeholder="example.com/*" />
|
<input type="text" id="urlPatternInput" placeholder="example.com/*" />
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const SHORTCUT_RUN_KEY = "runShortcutId";
|
|||||||
const LAST_TASK_KEY = "lastSelectedTaskId";
|
const LAST_TASK_KEY = "lastSelectedTaskId";
|
||||||
const LAST_ENV_KEY = "lastSelectedEnvId";
|
const LAST_ENV_KEY = "lastSelectedEnvId";
|
||||||
const LAST_PROFILE_KEY = "lastSelectedProfileId";
|
const LAST_PROFILE_KEY = "lastSelectedProfileId";
|
||||||
|
const POPUP_DRAFT_KEY = "popupDraft";
|
||||||
|
|
||||||
const unknownSiteState = document.getElementById("unknownSiteState");
|
const unknownSiteState = document.getElementById("unknownSiteState");
|
||||||
const extractionReviewState = document.getElementById("extractionReviewState");
|
const extractionReviewState = document.getElementById("extractionReviewState");
|
||||||
@@ -25,6 +26,7 @@ const normalExecutionState = document.getElementById("normalExecutionState");
|
|||||||
const partialTextPaste = document.getElementById("partialTextPaste");
|
const partialTextPaste = document.getElementById("partialTextPaste");
|
||||||
const extractFullBtn = document.getElementById("extractFullBtn");
|
const extractFullBtn = document.getElementById("extractFullBtn");
|
||||||
const extractedPreview = document.getElementById("extractedPreview");
|
const extractedPreview = document.getElementById("extractedPreview");
|
||||||
|
const siteNameInput = document.getElementById("siteNameInput");
|
||||||
const urlPatternInput = document.getElementById("urlPatternInput");
|
const urlPatternInput = document.getElementById("urlPatternInput");
|
||||||
const retryExtractBtn = document.getElementById("retryExtractBtn");
|
const retryExtractBtn = document.getElementById("retryExtractBtn");
|
||||||
const confirmSiteBtn = document.getElementById("confirmSiteBtn");
|
const confirmSiteBtn = document.getElementById("confirmSiteBtn");
|
||||||
@@ -44,6 +46,9 @@ const state = {
|
|||||||
outputRaw: "",
|
outputRaw: "",
|
||||||
autoRunPending: false,
|
autoRunPending: false,
|
||||||
shortcutRunPending: false,
|
shortcutRunPending: false,
|
||||||
|
currentPopupState: "unknown",
|
||||||
|
globalTheme: "system",
|
||||||
|
forcedTask: null,
|
||||||
selectedTaskId: "",
|
selectedTaskId: "",
|
||||||
selectedEnvId: "",
|
selectedEnvId: "",
|
||||||
selectedProfileId: ""
|
selectedProfileId: ""
|
||||||
@@ -53,6 +58,7 @@ async function switchState(stateName) {
|
|||||||
unknownSiteState.classList.add("hidden");
|
unknownSiteState.classList.add("hidden");
|
||||||
extractionReviewState.classList.add("hidden");
|
extractionReviewState.classList.add("hidden");
|
||||||
normalExecutionState.classList.add("hidden");
|
normalExecutionState.classList.add("hidden");
|
||||||
|
state.currentPopupState = stateName;
|
||||||
|
|
||||||
if (stateName === "unknown") {
|
if (stateName === "unknown") {
|
||||||
unknownSiteState.classList.remove("hidden");
|
unknownSiteState.classList.remove("hidden");
|
||||||
@@ -64,6 +70,37 @@ async function switchState(stateName) {
|
|||||||
await chrome.storage.local.set({ lastPopupState: 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) {
|
function matchUrl(url, pattern) {
|
||||||
if (!pattern) return false;
|
if (!pattern) return false;
|
||||||
let regex = null;
|
let regex = null;
|
||||||
@@ -112,6 +149,20 @@ function resolveScopedItems(parentItems, localItems, disabledNames) {
|
|||||||
return [...inherited, ...localEnabled];
|
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) {
|
function resolveEffectiveList(globalItems, workspace, site, listKey, disabledKey) {
|
||||||
const workspaceItems = workspace?.[listKey] || [];
|
const workspaceItems = workspace?.[listKey] || [];
|
||||||
const workspaceDisabled = workspace?.disabledInherited?.[disabledKey] || [];
|
const workspaceDisabled = workspace?.disabledInherited?.[disabledKey] || [];
|
||||||
@@ -378,6 +429,14 @@ function applyTheme(theme) {
|
|||||||
document.documentElement.dataset.theme = value;
|
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) {
|
function setAnalyzing(isAnalyzing) {
|
||||||
state.isAnalyzing = isAnalyzing;
|
state.isAnalyzing = isAnalyzing;
|
||||||
runBtn.disabled = isAnalyzing;
|
runBtn.disabled = isAnalyzing;
|
||||||
@@ -604,17 +663,20 @@ async function loadConfig() {
|
|||||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
const currentUrl = tabs[0]?.url || "";
|
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);
|
await detectSite(currentUrl);
|
||||||
if (lastPopupState && lastPopupState !== "unknown") {
|
if (state.currentSite) {
|
||||||
// If we had a state like 'review', we might want to stay there,
|
if (lastPopupState === "normal") {
|
||||||
// but detectSite might have switched to 'normal' if it matched.
|
await switchState("normal");
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
} else if (popupDraft?.state === "review") {
|
||||||
|
applyPopupDraft(popupDraft);
|
||||||
|
await switchState("review");
|
||||||
|
} else if (lastPopupState === "unknown") {
|
||||||
|
await switchState("unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stored = await getStorage([
|
const stored = await getStorage([
|
||||||
@@ -624,6 +686,7 @@ async function loadConfig() {
|
|||||||
"shortcuts",
|
"shortcuts",
|
||||||
"workspaces",
|
"workspaces",
|
||||||
"sites",
|
"sites",
|
||||||
|
"theme",
|
||||||
LAST_TASK_KEY,
|
LAST_TASK_KEY,
|
||||||
LAST_ENV_KEY,
|
LAST_ENV_KEY,
|
||||||
LAST_PROFILE_KEY
|
LAST_PROFILE_KEY
|
||||||
@@ -650,6 +713,10 @@ async function loadConfig() {
|
|||||||
state.currentWorkspace = activeWorkspace;
|
state.currentWorkspace = activeWorkspace;
|
||||||
currentWorkspaceName.textContent = activeWorkspace.name || "Global";
|
currentWorkspaceName.textContent = activeWorkspace.name || "Global";
|
||||||
}
|
}
|
||||||
|
if (stored.theme) {
|
||||||
|
state.globalTheme = stored.theme;
|
||||||
|
}
|
||||||
|
applyTheme(resolveThemeForPopup(state.globalTheme));
|
||||||
|
|
||||||
const effectiveEnvs = resolveEffectiveList(
|
const effectiveEnvs = resolveEffectiveList(
|
||||||
envs,
|
envs,
|
||||||
@@ -721,7 +788,8 @@ async function loadConfig() {
|
|||||||
|
|
||||||
async function loadTheme() {
|
async function loadTheme() {
|
||||||
const { theme = "system" } = await getStorage(["theme"]);
|
const { theme = "system" } = await getStorage(["theme"]);
|
||||||
applyTheme(theme);
|
state.globalTheme = theme;
|
||||||
|
applyTheme(resolveThemeForPopup(theme));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleExtract() {
|
async function handleExtract() {
|
||||||
@@ -758,7 +826,11 @@ async function handleAnalyze() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const taskId = taskSelect.value;
|
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) {
|
if (!task) {
|
||||||
setStatus("Select a task.");
|
setStatus("Select a task.");
|
||||||
return;
|
return;
|
||||||
@@ -947,6 +1019,16 @@ function handleCopyRaw() {
|
|||||||
void copyTextToClipboard(text, "Markdown");
|
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 () => {
|
partialTextPaste.addEventListener("input", async () => {
|
||||||
const text = partialTextPaste.value.trim();
|
const text = partialTextPaste.value.trim();
|
||||||
if (text.length < 5) return;
|
if (text.length < 5) return;
|
||||||
@@ -957,10 +1039,9 @@ partialTextPaste.addEventListener("input", async () => {
|
|||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
state.siteText = response.extracted;
|
state.siteText = response.extracted;
|
||||||
extractedPreview.textContent = state.siteText;
|
extractedPreview.textContent = state.siteText;
|
||||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
await fillSiteDefaultsFromTab();
|
||||||
const url = new URL(tabs[0].url);
|
|
||||||
urlPatternInput.value = url.hostname + url.pathname + "*";
|
|
||||||
switchState("review");
|
switchState("review");
|
||||||
|
await persistPopupDraft();
|
||||||
setStatus("Review extraction.");
|
setStatus("Review extraction.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -975,10 +1056,9 @@ extractFullBtn.addEventListener("click", async () => {
|
|||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
state.siteText = response.extracted;
|
state.siteText = response.extracted;
|
||||||
extractedPreview.textContent = state.siteText;
|
extractedPreview.textContent = state.siteText;
|
||||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
await fillSiteDefaultsFromTab();
|
||||||
const url = new URL(tabs[0].url);
|
|
||||||
urlPatternInput.value = url.hostname + url.pathname + "*";
|
|
||||||
switchState("review");
|
switchState("review");
|
||||||
|
await persistPopupDraft();
|
||||||
setStatus("Review extraction.");
|
setStatus("Review extraction.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} 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", () => {
|
retryExtractBtn.addEventListener("click", () => {
|
||||||
switchState("unknown");
|
switchState("unknown");
|
||||||
partialTextPaste.value = "";
|
partialTextPaste.value = "";
|
||||||
|
extractedPreview.textContent = "";
|
||||||
|
urlPatternInput.value = "";
|
||||||
|
siteNameInput.value = "";
|
||||||
|
state.siteText = "";
|
||||||
|
void clearPopupDraft();
|
||||||
setStatus("Ready.");
|
setStatus("Ready.");
|
||||||
});
|
});
|
||||||
|
|
||||||
confirmSiteBtn.addEventListener("click", async () => {
|
confirmSiteBtn.addEventListener("click", async () => {
|
||||||
|
const name = siteNameInput.value.trim();
|
||||||
const pattern = urlPatternInput.value.trim();
|
const pattern = urlPatternInput.value.trim();
|
||||||
|
if (!name) {
|
||||||
|
setStatus("Enter a site name.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!pattern) {
|
if (!pattern) {
|
||||||
setStatus("Enter a URL pattern.");
|
setStatus("Enter a URL pattern.");
|
||||||
return;
|
return;
|
||||||
@@ -1008,12 +1108,14 @@ confirmSiteBtn.addEventListener("click", async () => {
|
|||||||
|
|
||||||
const newSite = {
|
const newSite = {
|
||||||
id: `site-${Date.now()}`,
|
id: `site-${Date.now()}`,
|
||||||
|
name,
|
||||||
urlPattern: pattern,
|
urlPattern: pattern,
|
||||||
workspaceId: "global" // Default to global for now
|
workspaceId: "global" // Default to global for now
|
||||||
};
|
};
|
||||||
|
|
||||||
state.sites.push(newSite);
|
state.sites.push(newSite);
|
||||||
await chrome.storage.local.set({ sites: state.sites });
|
await chrome.storage.local.set({ sites: state.sites });
|
||||||
|
await clearPopupDraft();
|
||||||
state.currentSite = newSite;
|
state.currentSite = newSite;
|
||||||
state.currentWorkspace = { name: "Global", id: "global" };
|
state.currentWorkspace = { name: "Global", id: "global" };
|
||||||
currentWorkspaceName.textContent = "Global";
|
currentWorkspaceName.textContent = "Global";
|
||||||
@@ -1058,7 +1160,8 @@ async function loadShortcutRunRequest() {
|
|||||||
SHORTCUT_RUN_KEY,
|
SHORTCUT_RUN_KEY,
|
||||||
"shortcuts",
|
"shortcuts",
|
||||||
"workspaces",
|
"workspaces",
|
||||||
"sites"
|
"sites",
|
||||||
|
"tasks"
|
||||||
]);
|
]);
|
||||||
const shortcutId = stored[SHORTCUT_RUN_KEY];
|
const shortcutId = stored[SHORTCUT_RUN_KEY];
|
||||||
if (!shortcutId) return;
|
if (!shortcutId) return;
|
||||||
@@ -1066,7 +1169,12 @@ async function loadShortcutRunRequest() {
|
|||||||
state.shortcutRunPending = true;
|
state.shortcutRunPending = true;
|
||||||
await chrome.storage.local.remove(SHORTCUT_RUN_KEY);
|
await chrome.storage.local.remove(SHORTCUT_RUN_KEY);
|
||||||
|
|
||||||
|
if (!state.tasks.length) {
|
||||||
|
await loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
const globalShortcuts = normalizeConfigList(stored.shortcuts);
|
const globalShortcuts = normalizeConfigList(stored.shortcuts);
|
||||||
|
const globalTasks = normalizeConfigList(stored.tasks);
|
||||||
const sites = Array.isArray(stored.sites) ? stored.sites : state.sites;
|
const sites = Array.isArray(stored.sites) ? stored.sites : state.sites;
|
||||||
const workspaces = Array.isArray(stored.workspaces)
|
const workspaces = Array.isArray(stored.workspaces)
|
||||||
? stored.workspaces
|
? stored.workspaces
|
||||||
@@ -1092,9 +1200,28 @@ async function loadShortcutRunRequest() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shortcut.taskId) {
|
const shortcutTaskId = shortcut.taskId || "";
|
||||||
selectTask(shortcut.taskId, { resetEnv: true });
|
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) {
|
if (shortcut.envId) {
|
||||||
setEnvironmentSelection(shortcut.envId);
|
setEnvironmentSelection(shortcut.envId);
|
||||||
}
|
}
|
||||||
@@ -1156,6 +1283,7 @@ chrome.storage.onChanged.addListener((changes) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (changes.theme) {
|
if (changes.theme) {
|
||||||
applyTheme(changes.theme.newValue || "system");
|
state.globalTheme = changes.theme.newValue || "system";
|
||||||
|
applyTheme(resolveThemeForPopup(state.globalTheme));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ body {
|
|||||||
|
|
||||||
.toc {
|
.toc {
|
||||||
flex: 0 0 160px;
|
flex: 0 0 160px;
|
||||||
|
width: 160px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -56,6 +57,9 @@ body {
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 16px;
|
top: 16px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
max-height: calc(100vh - 32px);
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
@@ -77,6 +81,24 @@ body {
|
|||||||
|
|
||||||
.toc-links {
|
.toc-links {
|
||||||
display: block;
|
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 {
|
.toc ul {
|
||||||
@@ -148,7 +170,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-errors {
|
.sidebar-errors {
|
||||||
margin-top: auto;
|
margin-top: 8px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid #c0392b;
|
border: 1px solid #c0392b;
|
||||||
background: rgba(192, 57, 43, 0.08);
|
background: rgba(192, 57, 43, 0.08);
|
||||||
@@ -172,14 +194,6 @@ body {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-bar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -376,6 +390,30 @@ button:active {
|
|||||||
gap: 12px;
|
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 {
|
.task-card {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -511,12 +549,22 @@ button:active {
|
|||||||
.api-key-actions .delete,
|
.api-key-actions .delete,
|
||||||
.api-config-actions .delete,
|
.api-config-actions .delete,
|
||||||
.env-config-actions .delete,
|
.env-config-actions .delete,
|
||||||
.task-actions .delete {
|
.task-actions .delete,
|
||||||
|
.shortcut-actions .delete {
|
||||||
background: #c0392b;
|
background: #c0392b;
|
||||||
border-color: #c0392b;
|
border-color: #c0392b;
|
||||||
color: #fff6f2;
|
color: #fff6f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-header .delete,
|
||||||
|
.site-header .delete {
|
||||||
|
background: #c0392b;
|
||||||
|
border-color: #c0392b;
|
||||||
|
color: #fff6f2;
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.api-configs {
|
.api-configs {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -622,6 +670,12 @@ button:active {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shortcut-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.settings-layout {
|
.settings-layout {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -11,16 +11,12 @@
|
|||||||
<div class="title">SiteCompanion Settings</div>
|
<div class="title">SiteCompanion Settings</div>
|
||||||
<div class="subtitle">Configure workspaces, tasks, and API access</div>
|
<div class="subtitle">Configure workspaces, tasks, and API access</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="page-bar">
|
|
||||||
<div id="status" class="status"></div>
|
|
||||||
<button id="saveBtn" class="accent" type="button">Save Settings</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-layout">
|
<div class="settings-layout">
|
||||||
<nav class="toc" aria-label="Settings table of contents">
|
<nav class="toc" aria-label="Settings table of contents">
|
||||||
<div class="toc-heading">SiteCompanion Settings</div>
|
<div class="toc-heading">SiteCompanion Settings</div>
|
||||||
<button id="saveBtnSidebar" class="accent" type="button">Save Settings</button>
|
<button id="saveBtnSidebar" class="accent" type="button">Save Settings</button>
|
||||||
<div id="statusSidebar" class="status"> </div>
|
<div id="statusSidebar" class="status"> </div>
|
||||||
|
<div id="sidebarErrors" class="sidebar-errors hidden"></div>
|
||||||
<div class="toc-title">Sections</div>
|
<div class="toc-title">Sections</div>
|
||||||
<div class="toc-links">
|
<div class="toc-links">
|
||||||
<ul>
|
<ul>
|
||||||
@@ -53,7 +49,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebarErrors" class="sidebar-errors hidden"></div>
|
<div id="tocResizer" class="toc-resizer" aria-hidden="true"></div>
|
||||||
</nav>
|
</nav>
|
||||||
<main class="settings-main">
|
<main class="settings-main">
|
||||||
<details class="panel" id="global-config-panel" open>
|
<details class="panel" id="global-config-panel" open>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
const saveBtn = document.getElementById("saveBtn");
|
|
||||||
const saveBtnSidebar = document.getElementById("saveBtnSidebar");
|
const saveBtnSidebar = document.getElementById("saveBtnSidebar");
|
||||||
const addApiConfigBtn = document.getElementById("addApiConfigBtn");
|
const addApiConfigBtn = document.getElementById("addApiConfigBtn");
|
||||||
const apiConfigsContainer = document.getElementById("apiConfigs");
|
const apiConfigsContainer = document.getElementById("apiConfigs");
|
||||||
@@ -16,13 +15,14 @@ const addSiteBtn = document.getElementById("addSiteBtn");
|
|||||||
const sitesContainer = document.getElementById("sites");
|
const sitesContainer = document.getElementById("sites");
|
||||||
const addShortcutBtn = document.getElementById("addShortcutBtn");
|
const addShortcutBtn = document.getElementById("addShortcutBtn");
|
||||||
const shortcutsContainer = document.getElementById("shortcuts");
|
const shortcutsContainer = document.getElementById("shortcuts");
|
||||||
const statusEl = document.getElementById("status");
|
|
||||||
const statusSidebarEl = document.getElementById("statusSidebar");
|
const statusSidebarEl = document.getElementById("statusSidebar");
|
||||||
const sidebarErrorsEl = document.getElementById("sidebarErrors");
|
const sidebarErrorsEl = document.getElementById("sidebarErrors");
|
||||||
const themeSelect = document.getElementById("themeSelect");
|
const themeSelect = document.getElementById("themeSelect");
|
||||||
const toolbarPositionSelect = document.getElementById("toolbarPositionSelect");
|
const toolbarPositionSelect = document.getElementById("toolbarPositionSelect");
|
||||||
const toolbarAutoHide = document.getElementById("toolbarAutoHide");
|
const toolbarAutoHide = document.getElementById("toolbarAutoHide");
|
||||||
const globalSitesContainer = document.getElementById("globalSites");
|
const globalSitesContainer = document.getElementById("globalSites");
|
||||||
|
const toc = document.querySelector(".toc");
|
||||||
|
const tocResizer = document.getElementById("tocResizer");
|
||||||
|
|
||||||
const OPENAI_DEFAULTS = {
|
const OPENAI_DEFAULTS = {
|
||||||
apiBaseUrl: "https://api.openai.com/v1",
|
apiBaseUrl: "https://api.openai.com/v1",
|
||||||
@@ -32,17 +32,63 @@ const OPENAI_DEFAULTS = {
|
|||||||
const DEFAULT_MODEL = "gpt-4o-mini";
|
const DEFAULT_MODEL = "gpt-4o-mini";
|
||||||
const DEFAULT_SYSTEM_PROMPT =
|
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.";
|
"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) {
|
function getStorage(keys) {
|
||||||
return new Promise((resolve) => chrome.storage.local.get(keys, resolve));
|
return new Promise((resolve) => chrome.storage.local.get(keys, resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStatus(message) {
|
function setStatus(message) {
|
||||||
statusEl.textContent = message;
|
|
||||||
if (statusSidebarEl) statusSidebarEl.textContent = message;
|
if (statusSidebarEl) statusSidebarEl.textContent = message;
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (statusEl.textContent === message) statusEl.textContent = "";
|
|
||||||
if (statusSidebarEl?.textContent === message) statusSidebarEl.textContent = "";
|
if (statusSidebarEl?.textContent === message) statusSidebarEl.textContent = "";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
@@ -73,7 +119,7 @@ function renderGlobalSitesList(sites) {
|
|||||||
for (const site of globalSites) {
|
for (const site of globalSites) {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = "#";
|
link.href = "#";
|
||||||
link.textContent = site.urlPattern || "Untitled Site";
|
link.textContent = site.name || site.urlPattern || "Untitled Site";
|
||||||
link.addEventListener("click", (e) => {
|
link.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const card = document.querySelector(`.site-card[data-id="${site.id}"]`);
|
const card = document.querySelector(`.site-card[data-id="${site.id}"]`);
|
||||||
@@ -1863,7 +1909,13 @@ function buildScopedModuleSection({
|
|||||||
const newItem = newItemFactory(localContainer);
|
const newItem = newItemFactory(localContainer);
|
||||||
const options =
|
const options =
|
||||||
typeof cardOptions === "function" ? cardOptions() : cardOptions;
|
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") {
|
if (module === "envs") {
|
||||||
updateEnvApiOptions();
|
updateEnvApiOptions();
|
||||||
} else if (module === "profiles") {
|
} else if (module === "profiles") {
|
||||||
@@ -1933,7 +1985,10 @@ function listWorkspaceTargets() {
|
|||||||
function listSiteTargets() {
|
function listSiteTargets() {
|
||||||
return [...sitesContainer.querySelectorAll(".site-card")].map((card) => ({
|
return [...sitesContainer.querySelectorAll(".site-card")].map((card) => ({
|
||||||
id: card.dataset.id || "",
|
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");
|
const header = document.createElement("div");
|
||||||
header.className = "row workspace-header";
|
header.className = "row workspace-header";
|
||||||
|
header.style.alignItems = "flex-end";
|
||||||
|
|
||||||
const nameField = document.createElement("div");
|
const nameField = document.createElement("div");
|
||||||
nameField.className = "field";
|
nameField.className = "field";
|
||||||
@@ -2387,7 +2443,7 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) {
|
|||||||
for (const site of ownedSites) {
|
for (const site of ownedSites) {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = "#";
|
link.href = "#";
|
||||||
link.textContent = site.urlPattern || "Untitled Site";
|
link.textContent = site.name || site.urlPattern || "Untitled Site";
|
||||||
link.addEventListener("click", (e) => {
|
link.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const siteCard = document.querySelector(
|
const siteCard = document.querySelector(
|
||||||
@@ -2411,6 +2467,7 @@ function buildWorkspaceCard(ws, allWorkspaces = [], allSites = []) {
|
|||||||
function collectSites() {
|
function collectSites() {
|
||||||
const cards = [...sitesContainer.querySelectorAll(".site-card")];
|
const cards = [...sitesContainer.querySelectorAll(".site-card")];
|
||||||
return cards.map((card) => {
|
return cards.map((card) => {
|
||||||
|
const nameInput = card.querySelector(".site-name");
|
||||||
const patternInput = card.querySelector(".site-pattern");
|
const patternInput = card.querySelector(".site-pattern");
|
||||||
const workspaceSelect = card.querySelector(".site-workspace");
|
const workspaceSelect = card.querySelector(".site-workspace");
|
||||||
const themeSelect = card.querySelector(".appearance-theme");
|
const themeSelect = card.querySelector(".appearance-theme");
|
||||||
@@ -2426,6 +2483,7 @@ function collectSites() {
|
|||||||
const apiConfigsInherited = card.querySelector('.inherited-list[data-module="apiConfigs"]');
|
const apiConfigsInherited = card.querySelector('.inherited-list[data-module="apiConfigs"]');
|
||||||
return {
|
return {
|
||||||
id: card.dataset.id || newSiteId(),
|
id: card.dataset.id || newSiteId(),
|
||||||
|
name: (nameInput?.value || "").trim(),
|
||||||
urlPattern: (patternInput?.value || "").trim(),
|
urlPattern: (patternInput?.value || "").trim(),
|
||||||
workspaceId: workspaceSelect?.value || "global",
|
workspaceId: workspaceSelect?.value || "global",
|
||||||
theme: themeSelect?.value || "inherit",
|
theme: themeSelect?.value || "inherit",
|
||||||
@@ -2451,9 +2509,26 @@ function buildSiteCard(site, allWorkspaces = []) {
|
|||||||
card.dataset.id = site.id || newSiteId();
|
card.dataset.id = site.id || newSiteId();
|
||||||
|
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.className = "row";
|
row.className = "row site-header";
|
||||||
row.style.alignItems = "flex-end";
|
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");
|
const patternField = document.createElement("div");
|
||||||
patternField.className = "field";
|
patternField.className = "field";
|
||||||
patternField.style.flex = "1";
|
patternField.style.flex = "1";
|
||||||
@@ -2519,6 +2594,7 @@ function buildSiteCard(site, allWorkspaces = []) {
|
|||||||
scheduleSidebarErrors();
|
scheduleSidebarErrors();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
row.appendChild(nameField);
|
||||||
row.appendChild(patternField);
|
row.appendChild(patternField);
|
||||||
row.appendChild(wsField);
|
row.appendChild(wsField);
|
||||||
row.appendChild(deleteBtn);
|
row.appendChild(deleteBtn);
|
||||||
@@ -2924,11 +3000,13 @@ function buildShortcutCard(shortcut, _container, options = {}) {
|
|||||||
taskField.appendChild(taskLabel);
|
taskField.appendChild(taskLabel);
|
||||||
taskField.appendChild(taskSelect);
|
taskField.appendChild(taskSelect);
|
||||||
|
|
||||||
|
const actions = document.createElement("div");
|
||||||
|
actions.className = "shortcut-actions";
|
||||||
|
|
||||||
const deleteBtn = document.createElement("button");
|
const deleteBtn = document.createElement("button");
|
||||||
deleteBtn.type = "button";
|
deleteBtn.type = "button";
|
||||||
deleteBtn.className = "ghost delete";
|
deleteBtn.className = "ghost delete";
|
||||||
deleteBtn.textContent = "Delete";
|
deleteBtn.textContent = "Delete";
|
||||||
deleteBtn.style.marginTop = "8px";
|
|
||||||
deleteBtn.addEventListener("click", () => {
|
deleteBtn.addEventListener("click", () => {
|
||||||
card.remove();
|
card.remove();
|
||||||
scheduleSidebarErrors();
|
scheduleSidebarErrors();
|
||||||
@@ -2939,7 +3017,7 @@ function buildShortcutCard(shortcut, _container, options = {}) {
|
|||||||
card.appendChild(envField);
|
card.appendChild(envField);
|
||||||
card.appendChild(profileField);
|
card.appendChild(profileField);
|
||||||
card.appendChild(taskField);
|
card.appendChild(taskField);
|
||||||
card.appendChild(
|
actions.appendChild(
|
||||||
buildDuplicateControls("shortcuts", () => ({
|
buildDuplicateControls("shortcuts", () => ({
|
||||||
id: card.dataset.id,
|
id: card.dataset.id,
|
||||||
name: nameInput.value || "Untitled Shortcut",
|
name: nameInput.value || "Untitled Shortcut",
|
||||||
@@ -2949,7 +3027,8 @@ function buildShortcutCard(shortcut, _container, options = {}) {
|
|||||||
enabled: enabledInput.checked
|
enabled: enabledInput.checked
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
card.appendChild(deleteBtn);
|
actions.appendChild(deleteBtn);
|
||||||
|
card.appendChild(actions);
|
||||||
|
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
@@ -3071,7 +3150,10 @@ function updateSidebarErrors() {
|
|||||||
|
|
||||||
const siteCards = [...sitesContainer.querySelectorAll(".site-card")];
|
const siteCards = [...sitesContainer.querySelectorAll(".site-card")];
|
||||||
siteCards.forEach((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(
|
checkNameInputs(
|
||||||
card.querySelector(".site-envs"),
|
card.querySelector(".site-envs"),
|
||||||
".env-config-name",
|
".env-config-name",
|
||||||
@@ -3094,6 +3176,8 @@ function updateSidebarErrors() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
checkNameInputs(sitesContainer, ".site-name", "Sites");
|
||||||
|
|
||||||
if (!enabledTasks.length) errors.push("No tasks enabled.");
|
if (!enabledTasks.length) errors.push("No tasks enabled.");
|
||||||
if (!enabledEnvs.length) errors.push("No environments enabled.");
|
if (!enabledEnvs.length) errors.push("No environments enabled.");
|
||||||
if (!enabledProfiles.length) errors.push("No profiles enabled.");
|
if (!enabledProfiles.length) errors.push("No profiles enabled.");
|
||||||
@@ -3206,7 +3290,8 @@ async function loadSettings() {
|
|||||||
workspaces = [],
|
workspaces = [],
|
||||||
sites = [],
|
sites = [],
|
||||||
toolbarPosition = "bottom-right",
|
toolbarPosition = "bottom-right",
|
||||||
toolbarAutoHide: storedToolbarAutoHide = true
|
toolbarAutoHide: storedToolbarAutoHide = true,
|
||||||
|
sidebarWidth
|
||||||
} = await getStorage([
|
} = await getStorage([
|
||||||
"apiKey",
|
"apiKey",
|
||||||
"apiKeys",
|
"apiKeys",
|
||||||
@@ -3229,7 +3314,8 @@ async function loadSettings() {
|
|||||||
"workspaces",
|
"workspaces",
|
||||||
"sites",
|
"sites",
|
||||||
"toolbarPosition",
|
"toolbarPosition",
|
||||||
"toolbarAutoHide"
|
"toolbarAutoHide",
|
||||||
|
SIDEBAR_WIDTH_KEY
|
||||||
]);
|
]);
|
||||||
|
|
||||||
themeSelect.value = theme;
|
themeSelect.value = theme;
|
||||||
@@ -3241,6 +3327,9 @@ async function loadSettings() {
|
|||||||
if (toolbarAutoHide) {
|
if (toolbarAutoHide) {
|
||||||
toolbarAutoHide.checked = Boolean(storedToolbarAutoHide);
|
toolbarAutoHide.checked = Boolean(storedToolbarAutoHide);
|
||||||
}
|
}
|
||||||
|
if (Number.isFinite(sidebarWidth)) {
|
||||||
|
applySidebarWidth(sidebarWidth);
|
||||||
|
}
|
||||||
|
|
||||||
if (!shortcuts.length && Array.isArray(legacyPresets) && legacyPresets.length) {
|
if (!shortcuts.length && Array.isArray(legacyPresets) && legacyPresets.length) {
|
||||||
shortcuts = legacyPresets;
|
shortcuts = legacyPresets;
|
||||||
@@ -3287,6 +3376,7 @@ async function loadSettings() {
|
|||||||
if (!site || typeof site !== "object") return site;
|
if (!site || typeof site !== "object") return site;
|
||||||
return {
|
return {
|
||||||
...site,
|
...site,
|
||||||
|
name: site.name || site.urlPattern || "",
|
||||||
workspaceId: site.workspaceId || "global",
|
workspaceId: site.workspaceId || "global",
|
||||||
theme: site.theme || "inherit",
|
theme: site.theme || "inherit",
|
||||||
toolbarPosition: site.toolbarPosition || "inherit",
|
toolbarPosition: site.toolbarPosition || "inherit",
|
||||||
@@ -3638,7 +3728,6 @@ async function saveSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveBtn.addEventListener("click", () => void saveSettings());
|
|
||||||
if (saveBtnSidebar) {
|
if (saveBtnSidebar) {
|
||||||
saveBtnSidebar.addEventListener("click", () => void saveSettings());
|
saveBtnSidebar.addEventListener("click", () => void saveSettings());
|
||||||
}
|
}
|
||||||
@@ -3759,7 +3848,12 @@ addWorkspaceBtn.addEventListener("click", () => {
|
|||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
disabledInherited: normalizeDisabledInherited()
|
disabledInherited: normalizeDisabledInherited()
|
||||||
}, collectWorkspaces(), collectSites());
|
}, collectWorkspaces(), collectSites());
|
||||||
workspacesContainer.appendChild(newCard);
|
const first = workspacesContainer.firstElementChild;
|
||||||
|
if (first) {
|
||||||
|
workspacesContainer.insertBefore(newCard, first);
|
||||||
|
} else {
|
||||||
|
workspacesContainer.appendChild(newCard);
|
||||||
|
}
|
||||||
refreshWorkspaceInheritedLists();
|
refreshWorkspaceInheritedLists();
|
||||||
scheduleSidebarErrors();
|
scheduleSidebarErrors();
|
||||||
updateToc(collectWorkspaces(), collectSites());
|
updateToc(collectWorkspaces(), collectSites());
|
||||||
@@ -3768,6 +3862,7 @@ addWorkspaceBtn.addEventListener("click", () => {
|
|||||||
addSiteBtn.addEventListener("click", () => {
|
addSiteBtn.addEventListener("click", () => {
|
||||||
const newCard = buildSiteCard({
|
const newCard = buildSiteCard({
|
||||||
id: newSiteId(),
|
id: newSiteId(),
|
||||||
|
name: "",
|
||||||
urlPattern: "",
|
urlPattern: "",
|
||||||
workspaceId: "global",
|
workspaceId: "global",
|
||||||
theme: "inherit",
|
theme: "inherit",
|
||||||
@@ -3778,7 +3873,12 @@ addSiteBtn.addEventListener("click", () => {
|
|||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
disabledInherited: normalizeDisabledInherited()
|
disabledInherited: normalizeDisabledInherited()
|
||||||
}, collectWorkspaces());
|
}, collectWorkspaces());
|
||||||
sitesContainer.appendChild(newCard);
|
const first = sitesContainer.firstElementChild;
|
||||||
|
if (first) {
|
||||||
|
sitesContainer.insertBefore(newCard, first);
|
||||||
|
} else {
|
||||||
|
sitesContainer.appendChild(newCard);
|
||||||
|
}
|
||||||
refreshSiteInheritedLists();
|
refreshSiteInheritedLists();
|
||||||
scheduleSidebarErrors();
|
scheduleSidebarErrors();
|
||||||
updateToc(collectWorkspaces(), collectSites());
|
updateToc(collectWorkspaces(), collectSites());
|
||||||
@@ -3792,11 +3892,17 @@ addShortcutBtn.addEventListener("click", () => {
|
|||||||
profileId: "",
|
profileId: "",
|
||||||
taskId: ""
|
taskId: ""
|
||||||
});
|
});
|
||||||
shortcutsContainer.appendChild(newCard);
|
const first = shortcutsContainer.firstElementChild;
|
||||||
|
if (first) {
|
||||||
|
shortcutsContainer.insertBefore(newCard, first);
|
||||||
|
} else {
|
||||||
|
shortcutsContainer.appendChild(newCard);
|
||||||
|
}
|
||||||
scheduleSidebarErrors();
|
scheduleSidebarErrors();
|
||||||
});
|
});
|
||||||
|
|
||||||
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
|
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
|
||||||
|
initSidebarResize();
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
@@ -3908,7 +4014,7 @@ function updateToc(workspaces, sites) {
|
|||||||
for (const site of sites) {
|
for (const site of sites) {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.textContent = site.urlPattern || "Untitled Site";
|
a.textContent = site.name || site.urlPattern || "Untitled Site";
|
||||||
a.href = "#";
|
a.href = "#";
|
||||||
a.addEventListener("click", (e) => {
|
a.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
Reference in New Issue
Block a user