Added multi-api support and advanced mode
This commit is contained in:
@@ -1,24 +1,22 @@
|
||||
const apiBaseUrlInput = document.getElementById("apiBaseUrl");
|
||||
const apiKeyHeaderInput = document.getElementById("apiKeyHeader");
|
||||
const apiKeyPrefixInput = document.getElementById("apiKeyPrefix");
|
||||
const modelInput = document.getElementById("model");
|
||||
const systemPromptInput = document.getElementById("systemPrompt");
|
||||
const resumeInput = document.getElementById("resume");
|
||||
const saveBtn = document.getElementById("saveBtn");
|
||||
const addApiConfigBtn = document.getElementById("addApiConfigBtn");
|
||||
const apiConfigsContainer = document.getElementById("apiConfigs");
|
||||
const activeApiConfigSelect = document.getElementById("activeApiConfigSelect");
|
||||
const addApiKeyBtn = document.getElementById("addApiKeyBtn");
|
||||
const apiKeysContainer = document.getElementById("apiKeys");
|
||||
const activeApiKeySelect = document.getElementById("activeApiKeySelect");
|
||||
const addTaskBtn = document.getElementById("addTaskBtn");
|
||||
const tasksContainer = document.getElementById("tasks");
|
||||
const statusEl = document.getElementById("status");
|
||||
const themeSelect = document.getElementById("themeSelect");
|
||||
const resetApiBtn = document.getElementById("resetApiBtn");
|
||||
|
||||
const OPENAI_DEFAULTS = {
|
||||
apiBaseUrl: "https://api.openai.com/v1",
|
||||
apiKeyHeader: "Authorization",
|
||||
apiKeyPrefix: "Bearer "
|
||||
};
|
||||
const DEFAULT_MODEL = "gpt-4o-mini";
|
||||
|
||||
function getStorage(keys) {
|
||||
return new Promise((resolve) => chrome.storage.local.get(keys, resolve));
|
||||
@@ -47,6 +45,308 @@ function newApiKeyId() {
|
||||
return `key-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function newApiConfigId() {
|
||||
if (crypto?.randomUUID) return crypto.randomUUID();
|
||||
return `config-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function collectNames(container, selector) {
|
||||
if (!container) return [];
|
||||
return [...container.querySelectorAll(selector)]
|
||||
.map((input) => (input.value || "").trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function buildUniqueDefaultName(names) {
|
||||
const lower = new Set(names.map((name) => name.toLowerCase()));
|
||||
if (!lower.has("default")) return "Default";
|
||||
let index = 2;
|
||||
while (lower.has(`default-${index}`)) {
|
||||
index += 1;
|
||||
}
|
||||
return `Default-${index}`;
|
||||
}
|
||||
|
||||
function ensureUniqueName(desired, existingNames) {
|
||||
const trimmed = (desired || "").trim();
|
||||
const lowerNames = existingNames.map((name) => name.toLowerCase());
|
||||
if (trimmed && !lowerNames.includes(trimmed.toLowerCase())) {
|
||||
return trimmed;
|
||||
}
|
||||
return buildUniqueDefaultName(existingNames);
|
||||
}
|
||||
|
||||
function setApiConfigAdvanced(card, isAdvanced) {
|
||||
card.classList.toggle("is-advanced", isAdvanced);
|
||||
card.dataset.mode = isAdvanced ? "advanced" : "basic";
|
||||
|
||||
const basicFields = card.querySelectorAll(
|
||||
".basic-only input, .basic-only textarea"
|
||||
);
|
||||
const advancedFields = card.querySelectorAll(
|
||||
".advanced-only input, .advanced-only textarea"
|
||||
);
|
||||
basicFields.forEach((field) => {
|
||||
field.disabled = isAdvanced;
|
||||
});
|
||||
advancedFields.forEach((field) => {
|
||||
field.disabled = !isAdvanced;
|
||||
});
|
||||
|
||||
const resetBtn = card.querySelector(".reset-openai");
|
||||
if (resetBtn) resetBtn.disabled = isAdvanced;
|
||||
|
||||
const advancedBtn = card.querySelector(".advanced-toggle");
|
||||
if (advancedBtn && isAdvanced) advancedBtn.remove();
|
||||
}
|
||||
|
||||
function readApiConfigFromCard(card) {
|
||||
const nameInput = card.querySelector(".api-config-name");
|
||||
const keySelect = card.querySelector(".api-config-key-select");
|
||||
const baseInput = card.querySelector(".api-config-base");
|
||||
const headerInput = card.querySelector(".api-config-header");
|
||||
const prefixInput = card.querySelector(".api-config-prefix");
|
||||
const modelInput = card.querySelector(".api-config-model");
|
||||
const urlInput = card.querySelector(".api-config-url");
|
||||
const templateInput = card.querySelector(".api-config-template");
|
||||
const isAdvanced = card.classList.contains("is-advanced");
|
||||
|
||||
return {
|
||||
id: card.dataset.id || newApiConfigId(),
|
||||
name: (nameInput?.value || "Default").trim(),
|
||||
apiKeyId: keySelect?.value || "",
|
||||
apiBaseUrl: (baseInput?.value || "").trim(),
|
||||
apiKeyHeader: (headerInput?.value || "").trim(),
|
||||
apiKeyPrefix: prefixInput?.value || "",
|
||||
model: (modelInput?.value || "").trim(),
|
||||
apiUrl: (urlInput?.value || "").trim(),
|
||||
requestTemplate: (templateInput?.value || "").trim(),
|
||||
advanced: isAdvanced
|
||||
};
|
||||
}
|
||||
|
||||
function buildApiConfigCard(config) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "api-config-card";
|
||||
card.dataset.id = config.id || newApiConfigId();
|
||||
const isAdvanced = Boolean(config.advanced);
|
||||
|
||||
const nameField = document.createElement("div");
|
||||
nameField.className = "field";
|
||||
const nameLabel = document.createElement("label");
|
||||
nameLabel.textContent = "Name";
|
||||
const nameInput = document.createElement("input");
|
||||
nameInput.type = "text";
|
||||
nameInput.value = config.name || "";
|
||||
nameInput.className = "api-config-name";
|
||||
nameField.appendChild(nameLabel);
|
||||
nameField.appendChild(nameInput);
|
||||
|
||||
const keyField = document.createElement("div");
|
||||
keyField.className = "field";
|
||||
const keyLabel = document.createElement("label");
|
||||
keyLabel.textContent = "API Key";
|
||||
const keySelect = document.createElement("select");
|
||||
keySelect.className = "api-config-key-select";
|
||||
keySelect.dataset.preferred = config.apiKeyId || "";
|
||||
keyField.appendChild(keyLabel);
|
||||
keyField.appendChild(keySelect);
|
||||
|
||||
const baseField = document.createElement("div");
|
||||
baseField.className = "field basic-only";
|
||||
const baseLabel = document.createElement("label");
|
||||
baseLabel.textContent = "API Base URL";
|
||||
const baseInput = document.createElement("input");
|
||||
baseInput.type = "text";
|
||||
baseInput.placeholder = OPENAI_DEFAULTS.apiBaseUrl;
|
||||
baseInput.value = config.apiBaseUrl || "";
|
||||
baseInput.className = "api-config-base";
|
||||
baseField.appendChild(baseLabel);
|
||||
baseField.appendChild(baseInput);
|
||||
|
||||
const headerField = document.createElement("div");
|
||||
headerField.className = "field basic-only";
|
||||
const headerLabel = document.createElement("label");
|
||||
headerLabel.textContent = "API Key Header";
|
||||
const headerInput = document.createElement("input");
|
||||
headerInput.type = "text";
|
||||
headerInput.placeholder = OPENAI_DEFAULTS.apiKeyHeader;
|
||||
headerInput.value = config.apiKeyHeader || "";
|
||||
headerInput.className = "api-config-header";
|
||||
headerField.appendChild(headerLabel);
|
||||
headerField.appendChild(headerInput);
|
||||
|
||||
const prefixField = document.createElement("div");
|
||||
prefixField.className = "field basic-only";
|
||||
const prefixLabel = document.createElement("label");
|
||||
prefixLabel.textContent = "API Key Prefix";
|
||||
const prefixInput = document.createElement("input");
|
||||
prefixInput.type = "text";
|
||||
prefixInput.placeholder = OPENAI_DEFAULTS.apiKeyPrefix;
|
||||
prefixInput.value = config.apiKeyPrefix || "";
|
||||
prefixInput.className = "api-config-prefix";
|
||||
prefixField.appendChild(prefixLabel);
|
||||
prefixField.appendChild(prefixInput);
|
||||
|
||||
const modelField = document.createElement("div");
|
||||
modelField.className = "field basic-only";
|
||||
const modelLabel = document.createElement("label");
|
||||
modelLabel.textContent = "Model name";
|
||||
const modelInput = document.createElement("input");
|
||||
modelInput.type = "text";
|
||||
modelInput.placeholder = DEFAULT_MODEL;
|
||||
modelInput.value = config.model || "";
|
||||
modelInput.className = "api-config-model";
|
||||
modelField.appendChild(modelLabel);
|
||||
modelField.appendChild(modelInput);
|
||||
|
||||
const urlField = document.createElement("div");
|
||||
urlField.className = "field advanced-only";
|
||||
const urlLabel = document.createElement("label");
|
||||
urlLabel.textContent = "API URL";
|
||||
const urlInput = document.createElement("input");
|
||||
urlInput.type = "text";
|
||||
urlInput.placeholder = "https://api.example.com/v1/chat/completions";
|
||||
urlInput.value = config.apiUrl || "";
|
||||
urlInput.className = "api-config-url";
|
||||
urlField.appendChild(urlLabel);
|
||||
urlField.appendChild(urlInput);
|
||||
|
||||
const templateField = document.createElement("div");
|
||||
templateField.className = "field advanced-only";
|
||||
const templateLabel = document.createElement("label");
|
||||
templateLabel.textContent = "Request JSON template";
|
||||
const templateInput = document.createElement("textarea");
|
||||
templateInput.rows = 8;
|
||||
templateInput.placeholder = [
|
||||
"{",
|
||||
" \"stream\": true,",
|
||||
" \"messages\": [",
|
||||
" { \"role\": \"system\", \"content\": \"SYSTEM_PROMPT_GOES_HERE\" },",
|
||||
" { \"role\": \"user\", \"content\": \"PROMPT_GOES_HERE\" }",
|
||||
" ],",
|
||||
" \"api_key\": \"API_KEY_GOES_HERE\"",
|
||||
"}"
|
||||
].join("\n");
|
||||
templateInput.value = config.requestTemplate || "";
|
||||
templateInput.className = "api-config-template";
|
||||
templateField.appendChild(templateLabel);
|
||||
templateField.appendChild(templateInput);
|
||||
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "api-config-actions";
|
||||
if (!isAdvanced) {
|
||||
const advancedBtn = document.createElement("button");
|
||||
advancedBtn.type = "button";
|
||||
advancedBtn.className = "ghost advanced-toggle";
|
||||
advancedBtn.textContent = "Advanced Mode";
|
||||
advancedBtn.addEventListener("click", () => {
|
||||
setApiConfigAdvanced(card, true);
|
||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
||||
});
|
||||
actions.appendChild(advancedBtn);
|
||||
}
|
||||
|
||||
const duplicateBtn = document.createElement("button");
|
||||
duplicateBtn.type = "button";
|
||||
duplicateBtn.className = "ghost duplicate";
|
||||
duplicateBtn.textContent = "Duplicate";
|
||||
duplicateBtn.addEventListener("click", () => {
|
||||
const names = collectNames(apiConfigsContainer, ".api-config-name");
|
||||
const copy = readApiConfigFromCard(card);
|
||||
copy.id = newApiConfigId();
|
||||
copy.name = ensureUniqueName(`${copy.name || "Default"} Copy`, names);
|
||||
const newCard = buildApiConfigCard(copy);
|
||||
card.insertAdjacentElement("afterend", newCard);
|
||||
updateApiConfigKeyOptions();
|
||||
updateApiConfigSelect(newCard.dataset.id);
|
||||
});
|
||||
actions.appendChild(duplicateBtn);
|
||||
|
||||
const resetBtn = document.createElement("button");
|
||||
resetBtn.type = "button";
|
||||
resetBtn.className = "ghost reset-openai";
|
||||
resetBtn.textContent = "Reset to OpenAI";
|
||||
resetBtn.addEventListener("click", () => {
|
||||
if (card.classList.contains("is-advanced")) {
|
||||
setStatus("Advanced mode cannot be reset to OpenAI.");
|
||||
return;
|
||||
}
|
||||
baseInput.value = OPENAI_DEFAULTS.apiBaseUrl;
|
||||
headerInput.value = OPENAI_DEFAULTS.apiKeyHeader;
|
||||
prefixInput.value = OPENAI_DEFAULTS.apiKeyPrefix;
|
||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
||||
});
|
||||
actions.appendChild(resetBtn);
|
||||
|
||||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.type = "button";
|
||||
deleteBtn.className = "ghost delete";
|
||||
deleteBtn.textContent = "Delete";
|
||||
deleteBtn.addEventListener("click", () => {
|
||||
card.remove();
|
||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
||||
});
|
||||
actions.appendChild(deleteBtn);
|
||||
|
||||
const updateSelect = () => updateApiConfigSelect(activeApiConfigSelect.value);
|
||||
nameInput.addEventListener("input", updateSelect);
|
||||
baseInput.addEventListener("input", updateSelect);
|
||||
headerInput.addEventListener("input", updateSelect);
|
||||
prefixInput.addEventListener("input", updateSelect);
|
||||
modelInput.addEventListener("input", updateSelect);
|
||||
urlInput.addEventListener("input", updateSelect);
|
||||
templateInput.addEventListener("input", updateSelect);
|
||||
|
||||
card.appendChild(nameField);
|
||||
card.appendChild(keyField);
|
||||
card.appendChild(baseField);
|
||||
card.appendChild(headerField);
|
||||
card.appendChild(prefixField);
|
||||
card.appendChild(modelField);
|
||||
card.appendChild(urlField);
|
||||
card.appendChild(templateField);
|
||||
card.appendChild(actions);
|
||||
|
||||
setApiConfigAdvanced(card, isAdvanced);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function collectApiConfigs() {
|
||||
const cards = [...apiConfigsContainer.querySelectorAll(".api-config-card")];
|
||||
return cards.map((card) => readApiConfigFromCard(card));
|
||||
}
|
||||
|
||||
function updateApiConfigSelect(preferredId) {
|
||||
const configs = collectApiConfigs();
|
||||
activeApiConfigSelect.innerHTML = "";
|
||||
|
||||
if (!configs.length) {
|
||||
const option = document.createElement("option");
|
||||
option.value = "";
|
||||
option.textContent = "No configs configured";
|
||||
activeApiConfigSelect.appendChild(option);
|
||||
activeApiConfigSelect.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
activeApiConfigSelect.disabled = false;
|
||||
const selectedId =
|
||||
preferredId && configs.some((config) => config.id === preferredId)
|
||||
? preferredId
|
||||
: configs[0].id;
|
||||
|
||||
for (const config of configs) {
|
||||
const option = document.createElement("option");
|
||||
option.value = config.id;
|
||||
option.textContent = config.name || "Default";
|
||||
activeApiConfigSelect.appendChild(option);
|
||||
}
|
||||
|
||||
activeApiConfigSelect.value = selectedId;
|
||||
}
|
||||
|
||||
function buildApiKeyCard(entry) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "api-key-card";
|
||||
@@ -97,11 +397,11 @@ function buildApiKeyCard(entry) {
|
||||
deleteBtn.textContent = "Delete";
|
||||
deleteBtn.addEventListener("click", () => {
|
||||
card.remove();
|
||||
updateApiKeySelect();
|
||||
updateApiConfigKeyOptions();
|
||||
});
|
||||
actions.appendChild(deleteBtn);
|
||||
|
||||
const updateSelect = () => updateApiKeySelect(activeApiKeySelect.value);
|
||||
const updateSelect = () => updateApiConfigKeyOptions();
|
||||
nameInput.addEventListener("input", updateSelect);
|
||||
keyInput.addEventListener("input", updateSelect);
|
||||
|
||||
@@ -125,33 +425,37 @@ function collectApiKeys() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateApiKeySelect(preferredId) {
|
||||
function updateApiConfigKeyOptions() {
|
||||
const keys = collectApiKeys();
|
||||
activeApiKeySelect.innerHTML = "";
|
||||
const selects = apiConfigsContainer.querySelectorAll(".api-config-key-select");
|
||||
selects.forEach((select) => {
|
||||
const preferred = select.dataset.preferred || select.value;
|
||||
select.innerHTML = "";
|
||||
if (!keys.length) {
|
||||
const option = document.createElement("option");
|
||||
option.value = "";
|
||||
option.textContent = "No keys configured";
|
||||
select.appendChild(option);
|
||||
select.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keys.length) {
|
||||
const option = document.createElement("option");
|
||||
option.value = "";
|
||||
option.textContent = "No keys configured";
|
||||
activeApiKeySelect.appendChild(option);
|
||||
activeApiKeySelect.disabled = true;
|
||||
return;
|
||||
}
|
||||
select.disabled = false;
|
||||
for (const key of keys) {
|
||||
const option = document.createElement("option");
|
||||
option.value = key.id;
|
||||
option.textContent = key.name || "Default";
|
||||
select.appendChild(option);
|
||||
}
|
||||
|
||||
activeApiKeySelect.disabled = false;
|
||||
const selectedId =
|
||||
preferredId && keys.some((key) => key.id === preferredId)
|
||||
? preferredId
|
||||
: keys[0].id;
|
||||
if (preferred && keys.some((key) => key.id === preferred)) {
|
||||
select.value = preferred;
|
||||
} else {
|
||||
select.value = keys[0].id;
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
const option = document.createElement("option");
|
||||
option.value = key.id;
|
||||
option.textContent = key.name || "Default";
|
||||
activeApiKeySelect.appendChild(option);
|
||||
}
|
||||
|
||||
activeApiKeySelect.value = selectedId;
|
||||
select.dataset.preferred = select.value;
|
||||
});
|
||||
}
|
||||
|
||||
function buildTaskCard(task) {
|
||||
@@ -242,7 +546,10 @@ function buildTaskCard(task) {
|
||||
});
|
||||
|
||||
addBelowBtn.addEventListener("click", () => {
|
||||
const newCard = buildTaskCard({ id: newTaskId(), name: "", text: "" });
|
||||
const name = buildUniqueDefaultName(
|
||||
collectNames(tasksContainer, ".task-name")
|
||||
);
|
||||
const newCard = buildTaskCard({ id: newTaskId(), name, text: "" });
|
||||
card.insertAdjacentElement("afterend", newCard);
|
||||
updateTaskControls();
|
||||
});
|
||||
@@ -250,7 +557,10 @@ function buildTaskCard(task) {
|
||||
duplicateBtn.addEventListener("click", () => {
|
||||
const copy = {
|
||||
id: newTaskId(),
|
||||
name: `${nameInput.value || "Untitled"} Copy`,
|
||||
name: ensureUniqueName(
|
||||
`${nameInput.value || "Untitled"} Copy`,
|
||||
collectNames(tasksContainer, ".task-name")
|
||||
),
|
||||
text: textArea.value
|
||||
};
|
||||
const newCard = buildTaskCard(copy);
|
||||
@@ -307,6 +617,8 @@ async function loadSettings() {
|
||||
apiKey = "",
|
||||
apiKeys = [],
|
||||
activeApiKeyId = "",
|
||||
apiConfigs = [],
|
||||
activeApiConfigId = "",
|
||||
apiBaseUrl = "",
|
||||
apiKeyHeader = "",
|
||||
apiKeyPrefix = "",
|
||||
@@ -319,6 +631,8 @@ async function loadSettings() {
|
||||
"apiKey",
|
||||
"apiKeys",
|
||||
"activeApiKeyId",
|
||||
"apiConfigs",
|
||||
"activeApiConfigId",
|
||||
"apiBaseUrl",
|
||||
"apiKeyHeader",
|
||||
"apiKeyPrefix",
|
||||
@@ -329,10 +643,6 @@ async function loadSettings() {
|
||||
"theme"
|
||||
]);
|
||||
|
||||
apiBaseUrlInput.value = apiBaseUrl;
|
||||
apiKeyHeaderInput.value = apiKeyHeader;
|
||||
apiKeyPrefixInput.value = apiKeyPrefix;
|
||||
modelInput.value = model;
|
||||
systemPromptInput.value = systemPrompt;
|
||||
resumeInput.value = resume;
|
||||
themeSelect.value = theme;
|
||||
@@ -349,17 +659,75 @@ async function loadSettings() {
|
||||
apiKeys: resolvedKeys,
|
||||
activeApiKeyId: resolvedActiveId
|
||||
});
|
||||
} else if (resolvedKeys.length) {
|
||||
const hasActive = resolvedKeys.some((entry) => entry.id === resolvedActiveId);
|
||||
if (!hasActive) {
|
||||
resolvedActiveId = resolvedKeys[0].id;
|
||||
await chrome.storage.local.set({ activeApiKeyId: resolvedActiveId });
|
||||
}
|
||||
}
|
||||
|
||||
apiKeysContainer.innerHTML = "";
|
||||
if (!resolvedKeys.length) {
|
||||
apiKeysContainer.appendChild(buildApiKeyCard({ id: newApiKeyId(), name: "", key: "" }));
|
||||
apiKeysContainer.appendChild(
|
||||
buildApiKeyCard({ id: newApiKeyId(), name: "", key: "" })
|
||||
);
|
||||
} else {
|
||||
for (const entry of resolvedKeys) {
|
||||
apiKeysContainer.appendChild(buildApiKeyCard(entry));
|
||||
}
|
||||
}
|
||||
updateApiKeySelect(resolvedActiveId);
|
||||
|
||||
let resolvedConfigs = Array.isArray(apiConfigs) ? apiConfigs : [];
|
||||
let resolvedActiveConfigId = activeApiConfigId;
|
||||
|
||||
if (!resolvedConfigs.length) {
|
||||
const migrated = {
|
||||
id: newApiConfigId(),
|
||||
name: "Default",
|
||||
apiBaseUrl: apiBaseUrl || OPENAI_DEFAULTS.apiBaseUrl,
|
||||
apiKeyHeader: apiKeyHeader || OPENAI_DEFAULTS.apiKeyHeader,
|
||||
apiKeyPrefix: apiKeyPrefix || OPENAI_DEFAULTS.apiKeyPrefix,
|
||||
model: model || DEFAULT_MODEL,
|
||||
apiKeyId: resolvedActiveId || resolvedKeys[0]?.id || "",
|
||||
apiUrl: "",
|
||||
requestTemplate: "",
|
||||
advanced: false
|
||||
};
|
||||
resolvedConfigs = [migrated];
|
||||
resolvedActiveConfigId = migrated.id;
|
||||
await chrome.storage.local.set({
|
||||
apiConfigs: resolvedConfigs,
|
||||
activeApiConfigId: resolvedActiveConfigId
|
||||
});
|
||||
} else {
|
||||
const fallbackKeyId = resolvedActiveId || resolvedKeys[0]?.id || "";
|
||||
const withKeys = resolvedConfigs.map((config) => ({
|
||||
...config,
|
||||
apiKeyId: config.apiKeyId || fallbackKeyId,
|
||||
apiUrl: config.apiUrl || "",
|
||||
requestTemplate: config.requestTemplate || "",
|
||||
advanced: Boolean(config.advanced)
|
||||
}));
|
||||
if (withKeys.some((config, index) => config.apiKeyId !== resolvedConfigs[index].apiKeyId)) {
|
||||
resolvedConfigs = withKeys;
|
||||
await chrome.storage.local.set({ apiConfigs: resolvedConfigs });
|
||||
}
|
||||
const hasActive = resolvedConfigs.some(
|
||||
(config) => config.id === resolvedActiveConfigId
|
||||
);
|
||||
if (!hasActive) {
|
||||
resolvedActiveConfigId = resolvedConfigs[0].id;
|
||||
await chrome.storage.local.set({ activeApiConfigId: resolvedActiveConfigId });
|
||||
}
|
||||
}
|
||||
|
||||
apiConfigsContainer.innerHTML = "";
|
||||
for (const config of resolvedConfigs) {
|
||||
apiConfigsContainer.appendChild(buildApiConfigCard(config));
|
||||
}
|
||||
updateApiConfigKeyOptions();
|
||||
updateApiConfigSelect(resolvedActiveConfigId);
|
||||
|
||||
tasksContainer.innerHTML = "";
|
||||
if (!tasks.length) {
|
||||
@@ -379,17 +747,21 @@ async function loadSettings() {
|
||||
async function saveSettings() {
|
||||
const tasks = collectTasks();
|
||||
const apiKeys = collectApiKeys();
|
||||
const apiConfigs = collectApiConfigs();
|
||||
const activeApiConfigId =
|
||||
apiConfigs.find((entry) => entry.id === activeApiConfigSelect.value)?.id ||
|
||||
apiConfigs[0]?.id ||
|
||||
"";
|
||||
const activeConfig = apiConfigs.find((entry) => entry.id === activeApiConfigId);
|
||||
const activeApiKeyId =
|
||||
apiKeys.find((entry) => entry.id === activeApiKeySelect.value)?.id ||
|
||||
activeConfig?.apiKeyId ||
|
||||
apiKeys[0]?.id ||
|
||||
"";
|
||||
await chrome.storage.local.set({
|
||||
apiBaseUrl: apiBaseUrlInput.value.trim(),
|
||||
apiKeyHeader: apiKeyHeaderInput.value.trim(),
|
||||
apiKeyPrefix: apiKeyPrefixInput.value,
|
||||
apiKeys,
|
||||
activeApiKeyId,
|
||||
model: modelInput.value.trim(),
|
||||
apiConfigs,
|
||||
activeApiConfigId,
|
||||
systemPrompt: systemPromptInput.value,
|
||||
resume: resumeInput.value,
|
||||
tasks,
|
||||
@@ -400,7 +772,10 @@ async function saveSettings() {
|
||||
|
||||
saveBtn.addEventListener("click", () => void saveSettings());
|
||||
addTaskBtn.addEventListener("click", () => {
|
||||
const newCard = buildTaskCard({ id: newTaskId(), name: "", text: "" });
|
||||
const name = buildUniqueDefaultName(
|
||||
collectNames(tasksContainer, ".task-name")
|
||||
);
|
||||
const newCard = buildTaskCard({ id: newTaskId(), name, text: "" });
|
||||
const first = tasksContainer.firstElementChild;
|
||||
if (first) {
|
||||
tasksContainer.insertBefore(newCard, first);
|
||||
@@ -411,31 +786,49 @@ addTaskBtn.addEventListener("click", () => {
|
||||
});
|
||||
|
||||
addApiKeyBtn.addEventListener("click", () => {
|
||||
const newCard = buildApiKeyCard({ id: newApiKeyId(), name: "", key: "" });
|
||||
const name = buildUniqueDefaultName(
|
||||
collectNames(apiKeysContainer, ".api-key-name")
|
||||
);
|
||||
const newCard = buildApiKeyCard({ id: newApiKeyId(), name, key: "" });
|
||||
const first = apiKeysContainer.firstElementChild;
|
||||
if (first) {
|
||||
apiKeysContainer.insertBefore(newCard, first);
|
||||
} else {
|
||||
apiKeysContainer.appendChild(newCard);
|
||||
}
|
||||
updateApiKeySelect(activeApiKeySelect.value);
|
||||
updateApiConfigKeyOptions();
|
||||
});
|
||||
|
||||
activeApiKeySelect.addEventListener("change", () => {
|
||||
updateApiKeySelect(activeApiKeySelect.value);
|
||||
addApiConfigBtn.addEventListener("click", () => {
|
||||
const name = buildUniqueDefaultName(
|
||||
collectNames(apiConfigsContainer, ".api-config-name")
|
||||
);
|
||||
const newCard = buildApiConfigCard({
|
||||
id: newApiConfigId(),
|
||||
name,
|
||||
apiBaseUrl: OPENAI_DEFAULTS.apiBaseUrl,
|
||||
apiKeyHeader: OPENAI_DEFAULTS.apiKeyHeader,
|
||||
apiKeyPrefix: OPENAI_DEFAULTS.apiKeyPrefix,
|
||||
model: DEFAULT_MODEL,
|
||||
apiUrl: "",
|
||||
requestTemplate: "",
|
||||
advanced: false
|
||||
});
|
||||
const first = apiConfigsContainer.firstElementChild;
|
||||
if (first) {
|
||||
apiConfigsContainer.insertBefore(newCard, first);
|
||||
} else {
|
||||
apiConfigsContainer.appendChild(newCard);
|
||||
}
|
||||
updateApiConfigKeyOptions();
|
||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
||||
});
|
||||
|
||||
activeApiConfigSelect.addEventListener("change", () => {
|
||||
updateApiConfigSelect(activeApiConfigSelect.value);
|
||||
});
|
||||
|
||||
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
|
||||
resetApiBtn.addEventListener("click", async () => {
|
||||
apiBaseUrlInput.value = OPENAI_DEFAULTS.apiBaseUrl;
|
||||
apiKeyHeaderInput.value = OPENAI_DEFAULTS.apiKeyHeader;
|
||||
apiKeyPrefixInput.value = OPENAI_DEFAULTS.apiKeyPrefix;
|
||||
await chrome.storage.local.set({
|
||||
apiBaseUrl: OPENAI_DEFAULTS.apiBaseUrl,
|
||||
apiKeyHeader: OPENAI_DEFAULTS.apiKeyHeader,
|
||||
apiKeyPrefix: OPENAI_DEFAULTS.apiKeyPrefix
|
||||
});
|
||||
setStatus("OpenAI defaults restored.");
|
||||
});
|
||||
themeSelect.addEventListener("change", () => applyTheme(themeSelect.value));
|
||||
|
||||
loadSettings();
|
||||
|
||||
Reference in New Issue
Block a user